更改allowcircularReference为false可以关闭循环依赖,方式有三种。
1、更改spring源码
2、不要使用带参构造函数
改为
3、拓展spring(具体不知道怎么做,应该是通过BeanPostProcessor接口)
1、重要的区别,别搞混了
1、在填充bean属性时,会调用一系列的BeanPostProcessor,其中CommonAnnotationBeanPostProcessor处理@Resource注解,AutowiredAnnotationBeanPostProcessor处理@Autowired注解,他们是解决循环依赖的关键。
2、代理是在初始化后调用postprocessor完成的。解决依赖过程中,从二级缓存工厂获取对象的时候,获取到的是原对象的引用,再完成属性注入,再初始化。
3、初始化过程是先处理注解再处理类方法最后再处理注解。所以如果bean同时使用三种初始化方法,@PostConstruct注解的初始化方法先执行(先执行BeanPostProcessor接口,相关BeanPostProcessor接口实现类会处理注解),实现了InitializingBean的afterPropertiesSet()再执行,最后是xml配置的init方法后执行。
4、BeanPostProcessor接口(有方法体的,java8以后就可以了)只有两个方法,
Object postProcessBeforeInitialization(Object bean, String beanName)
postProcessAfterInitialization(Object bean, String beanName)
从名字可以看出,实现该类只能插手初始化的过程,而对实例化过程不能产生影响。
对bean实例化过程产生影响的有BeanPostProcessor接口的子类,比如CommonAnnotationBeanPostProcessor等等
5、若是想把类(不是对象)交给spring管理,可以采用以下方法
查看Spring原文档
比如有循环依赖如下,类一定要是无参构造器
public class TestInstanceBean1 {
@Autowired
private TestInstanceBean2 testInstanceBean2;
@Value("this is TestInstanceBean")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printlnSomething() {
System.out.println("在TestInstanceBean1中获取testInstanceBean2的名字:"+testInstanceBean2.getName());
System.out.println("在TestInstanceBean1中获取自己的名字:"+name);
}
}
public class TestInstanceBean2 {
@Autowired
private TestInstanceBean1 testInstanceBean1;
@Value("this is TestInstanceBean2")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printlnSomething() {
System.out.println("在TestInstanceBean2中获取testInstanceBean1的名字:"+testInstanceBean1.getName());
System.out.println("在TestInstanceBean2中获取自己的名字:"+name);
}
}
对于第一种
//加载TestMain到上下文当中,并且完成spring容器的初始化工作
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ac.getBeanFactory();
BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.genericBeanDefinition(TestInstanceBean1.class);
BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.genericBeanDefinition(TestInstanceBean2.class);
beanFactory.registerBeanDefinition("testInstanceBean1",builder1.getBeanDefinition());
beanFactory.registerBeanDefinition("testInstanceBean2",builder2.getBeanDefinition());
ac.register(TestMain.class);
//完成容器初始化
ac.refresh();
TestInstanceBean1 testInstanceBean1= (TestInstanceBean1) ac.getBean("testInstanceBean1");
testInstanceBean1.printlnSomething();
TestInstanceBean2 testInstanceBean2= (TestInstanceBean2) ac.getBean("testInstanceBean2");
testInstanceBean2.printlnSomething();
结果会打印出
在TestInstanceBean1中获取testInstanceBean2的名字:this is TestInstanceBean2
在TestInstanceBean1中获取自己的名字:this is TestInstanceBean
在TestInstanceBean2中获取testInstanceBean1的名字:this is TestInstanceBean
在TestInstanceBean2中获取自己的名字:this is TestInstanceBean2
对于第二种
public class TestBeanFactoryPostProcesser implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.genericBeanDefinition(TestInstanceBean1.class);
BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.genericBeanDefinition(TestInstanceBean2.class);
registry.registerBeanDefinition("testInstanceBean1",builder1.getBeanDefinition());
registry.registerBeanDefinition("testInstanceBean2",builder2.getBeanDefinition());
}
}
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestMain.class);
TestInstanceBean1 testInstanceBean1= (TestInstanceBean1) ac.getBean("testInstanceBean1");
testInstanceBean1.printlnSomething();
TestInstanceBean2 testInstanceBean2= (TestInstanceBean2) ac.getBean("testInstanceBean2");
testInstanceBean2.printlnSomething();
结果和之前一样,打印出
在TestInstanceBean1中获取testInstanceBean2的名字:this is TestInstanceBean2
在TestInstanceBean1中获取自己的名字:this is TestInstanceBean
在TestInstanceBean2中获取testInstanceBean1的名字:this is TestInstanceBean
在TestInstanceBean2中获取自己的名字:this is TestInstanceBean2
可以看到类里面的注解也会被解析,配置被注入,这是因为BeanDefinition有相应的BeanPostProcessor接口处理了这些注解。这样的手动注入,spring也能够解决循环依赖。
6、若是想把对象(不是类)交给spring管理,可以采用以下三种方法
registerSingleton(String beanName, Object singletonObject)
BeanFactory:是访问Spring bean容器的根接口,是浏览spring bean容器情况的底层客户端。(The root interface for accessing a Spring bean container.This is the basic client view of a bean container;)
FactoryBean:BeanFactory生产bean的时候不是直接返回bean的实例,而是调用该bean自身的工厂类生产。BeanFactory存储的是bean自身的工厂,而不是bean实例。
这种模式就是抽象工厂模式,外层工厂放置着内层工厂对象。
循环依赖只存在与单例模式当中,虽然有三级缓存,但对于一个对象来说,它始终只有一个实例。而循环依赖的关键在于对原对象的引用。首先要明白下面的代码。
public class TesCIrcularDependencies {
private String name;
public TesCIrcularDependencies(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void print() {
System.out.println(name);
}
}
TesCIrcularDependencies a0 = new TesCIrcularDependencies("原名字");
TesCIrcularDependencies a1=a0;
TesCIrcularDependencies a2=a0;
a1.setName("a1对象更改后的名字");
a0.print();
a1.print();
a2.print();
打印结果是
a1对象更改后的名字
a1对象更改后的名字
a1对象更改后的名字
比如有A依赖于B,B依赖于A, 解决循环依赖的大致流程为:
singletonObjects:一级缓存,单例池,放置着spring bean的实例(注入完成的bean)(最快);
singletonFactories:二级缓存,根据beanName能获取到原对象的引用的工厂。
(还不是spring bean, 已经构造完成,但还没有注入属性或者初始化)
解决循环依赖时,当单例池没有beanName对应的spring bean的时候去生产;
earlySingletonObjects:三级缓存,因为每次都从singletonFactories生产会比较慢,
所以将二级缓存的生产结果缓存起来。
1、Cglib和jdk动态代理的区别
2、为什么jdk动态代理必须基于接口 ?