1、bean的生命周期总结;
①:实例化会根据构造方法来实例化对象。如果对象中有多个构造方法,默认会使用无参构造方法,可以使用@Autowired指定构造方法。如果删掉无参构造方法,而又不指定构造方法,保留两个及以上有参构造方法,那么将报错。
②属性填充
@Autowired
先根据类型去spring中寻找,如果找到多个。
(1)是否配置autowireCandidate(不参与自动注入)为false,默认为true,当为false时,不参与自动注入。
(2)是否在注入与引用时采用Qualifier注解映射,在注入和引入采用同一个Qualifier的值,那么引用时使用名字Qualifier的值一样的。
(3)查看是否有类配置了@Primary,主bean,定义,如果有,那么优先级最高,优先使用。
(4)都没有的时候那么将根据name是否一致来判断。
如果以上条件都不满足将报错。
@Resource
按照名字查找,查找不到时根据类型查找,根据类型找不到或者找到多个都会报错。
③:初始化
在属性填充之后,可以通过继承专有接口的专用方法实现初始化,程序员自由的操作,比如从mysql中查找出数据给某个属性赋值。
④AOP
判断该类是否需要执行aop,如果有那么将生成代理类,这个代理类中,有一个重要的属性target属性,该属性为原来的对象,并且重写需要代理的方法,在重写的方法中使用target调用原有方法。原有spring帮我们自动注入的属性都会为null,spring不会再次做依赖注入这个操作。注入的属性都在target中。
2、为什么spring在java中那么重要;
在java中一切都是对象,java工程中有很多很多的对象。而spring就是用来管理这些对象的创建和管理对象之间的依赖关系;所以spring在java中非常的重要。
3、谈谈IOC
控制反转:把底层类作为参数传入高层类,而不是先构建底层类;
依赖注入:程序运行时依靠ioc动态的注入对象需要的外部依赖。
控制反转好处:在构建上层类时,不再先构建底层,而是将底层类作为参数传入。例如我们需要构建A类需要传入1个参数和传入B类,构建B类需要传入两个参数。正常逻辑我们构建完成B类,再构建A类,相当于构建A类时我们传入三个参数去构建。如果此时我们修改B类需要的参数为三个,那么就要修改构建A类的方法。传入四个参数去构建。在一个项目中依赖方式错综复杂,依赖链也可能长达10来个。如果我们改一个底层类的构造方法需要多加一个参数进去。所有的类都要被修改,这是非常庞大的修改,而spring的控制反转思想非常好的解决了这个问题。
4、谈谈AOP
实现方式:
JDK动态代理:
当目标类有继承接口时,spring将采用jdk动态代理来实现aop,构建代理对象;因为jdk的动态代理是根据JDK包中的Prox类动态创建代理对象,而该类构建代理对象要求需要该类有有接口,其方法需要传入类加载器,目标类的接口,还有实现了InvocationHandler接口的代理类。在代理类中执行invoke方法就是执行我们被代理类中的需要被代理的方法,在其前后就可以写上增强方法。
CGLIB动态代理:
如果被代理类中没有接口,那么spring将采用CGLIB来实现代理类。cglib是通过继承的方式动态的在运行期间生成代理类。底层采用字节码生成框架来实现,如果该类被final标记将无法使用cglib动态代理,方法采用final也不能代理。
有哪几种增强:
前置增强:在方法执行前;
后置增强:在方法执行后,无论是否有抛异常;
抛异常增强:当抛出异常后执行;
return后增强:如果没有抛出异常,正常返回,执行该增强;
环绕增强:包围方法的通知,可以在方法调用前后调用。功能强大,可以选择是否继续执行该方法,也可以自行抛出异常,或自定义返回值。
5、Bean和对象的区别
所有的bean都是对象,spring将由@Autowired修饰的属性注入到bean中,Spring帮我们完成自动注入,填充属性,而对象中这个属性的值为null;
4、获取Bean的name
实现BeanNameAware调用接口中的setBeanName就可以获取当前bean的name;
6、什么是BeanDefinition
在BeanDefinition就是bean的定义,比如单例,原型,懒加载,注入模型等等都是bean定义。在srping生命周期中我们实例化一个对象并不是直接根据class文件来实例化的,首先要根据class生成bean定义,再根据bean定义实例化对象。并且在BeanDefinition生成后,在实例化之前,还是可以通过BeanFactoryPostProcessor来修改;
7、Bean的后置处理器和bean工厂的后置处理器
BeanFactoryPostProcessor:
通过继承该类可以获取BeanDefinition,并且可以修改BeanDefinition的属性;
执行时机:
当spring扫描到类中的注解,根据这些注解生成BeanDefinition对象,把生成的BeanDefinition对象放到bean工厂的BeanDefinitionMap属性中。key就是我们的beanname他的value就是我们的BeanDefinition对象。当把所有需要扫描的对象都扫描完毕,并且生成BeanDefinition放到BeanDefinitionMap中后。bean工厂就算是组建完毕了。
bean工厂组件完毕就开始执行bean工厂的后置处理器
单例池也是属于bean工厂的。
BeanPostProcessor也是属于bean工厂的。
单bean实例化完毕后,就可以去执行bean的后置处理器了,像初始,aop都有其bean的后置处理器去实现初始化和aop的功能。
在bean工厂后置处理器中甚至可以创建一个新的bean到加入到spring容器当中。
beanFactory.registerSingleton(“xxx”,User.class);
8、beanFactory和FactoryBean的区别
beanFactory时springIOC最核心的类,里面有大量核心组件,比如bean定义容器,单例池都在beanFactory定义,beanFactory就是定义,创建,存储bean的核心接口。
FactoryBean和beanFactory其实差别很大,仅仅是名字有点类似而已。FactoryBean用来给程序员定义复杂的bean的对象,然后手动加入spring容器时使用的接口;
手动把对象放到spring容器中。
1、beanFactory.registerSingleton(“xxx”,User.class); 通过Bean工厂的后置处理器,在其中执行这个方法可以把bean注入spring容器中。
2、实现FactoryBean接口重写方法getObject和getObjectType,可以手动的对象放到spring容器中。
9、循环依赖
A类的构建需要B类,B类的构建需要A类,就变成了循环依赖,如果不做特殊处理将会变成死循环。
①使用二级缓存解决循环依赖
(1)在构建A类时,需要先new A();再填充属性,我们new完后直接把未填充属性的A类放到二级缓存中;
(2)填充A类属性需要B类,开始构建B类–>此时A类在二级缓存中有一个未填充属性的A类直接拿来构建B类
(3)构建完B类,重新继续构建A类,A类B类都构建完成
为什么二级缓存不能够完成aop情况下的循环依赖?
将未填充属性的A类放到二级缓存中,而构建B类的时候也是把未填充属性的A类拿来构建B类。但是我们如果A类需要使用AOP操作,那么将来放到spring容器中的A类将会是一个代理对象。那么这个放到spring容器中的A类和构建B类时使用的A类不是同一个对象。在单例池中这明显是不被允许的,所以二级缓存没办法完成AOP情况下的循环依赖。
②使用三级缓存解决循环依赖(需要完成AOP操作)
(1)new A();把未填充属性的A类放到二级缓存中,保存lambda表达式到三级缓存中(lambda(ObjectFactory)表达式是bean的半成品,记录原始对象的引用,是生成bean对象的工厂方法),如果没有用到循环依赖,这一步将没有作用,但是必须要走这一步为了防止有循环依赖产生,我们没有对应的方法来生成代理对象。
(2)、填充属性 -->如果被AOP代理了,那么取三级缓存中找lambda表达式,执行其工厂方法,生成aop代理类,保存到二级缓存中,并且删除三级缓存中的bean的半成品
从二级缓存中拿A类到注入B中,B创建完成,可以进行下一步
AOP正常情况下是在这边执行的,在循环依赖的情况下将不在这边执行,而是判断二级缓存中是否已经存有对象了,直接从二级缓存中
拿到对象即可,如果不是AOP的话,那么将是第二步执行的new也就是初始对象;
如果没有三级缓存,那么每次去二级缓存中拿对象执行singleFactory.getObject(),都会产生一个新的代理对象,这明显不合理,在spring中所有的对象都是单例对象,不然一个name对应两个不同的对象,这是明显不行的;所以如果只有二级缓存将不能解决这个问题;
(3)、初始化
(4)如果上面做了AOP这边是不会做aop的,有一个专门的类负责判断,与创建AOP,是由那个专门管理AOP的类来管理的和其他的无关;
(5)、加入单例池(一级缓存)