1.BeanFactory与ApplicationContext区别
先看下接口继承/实现关系
这里可以清楚看到ApplicationContext是BeanFactory接口的一个子接口,当然ApplicationContext下也有很多子接口与实现类,这里我列举了一个配置的子接口(ConfigurableApplicationContext)。
在spring中BeanFactory是IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,⽐BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等。
2.BeanFactory与FactoryBean的区别
在上面问题简述了BeanFactory,我们这里主要看下FactoryBean。
Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。
Bean创建的三种⽅式中的静态⽅法和实例化⽅法和FactoryBean作⽤类似,FactoryBean使⽤较多,尤其在Spring框架⼀些组件中会使⽤,还有其他框架和Spring框架整合时使⽤。
FactoryBean
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器的单例对象缓存池中Map
@Nullable
T getObject() throws Exception;
// 返回FactoryBean创建的Bean类型
@Nullable
Class<?> getObjectType();
// 返回作⽤域是否单例
default boolean isSingleton() {
return true;
}
}
FactoryBeanTest (用于生产自定义Bean)
@Component
public class FactoryBeanTest implements FactoryBean<BeanInfo> {
@Override
public Class<?> getObjectType() {
return BeanInfo.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public BeanInfo getObject() throws Exception {
return new BeanInfo();
}
}
测试
@Test
public void test() {
FactoryBeanTest bean1 = applicationContext.getBean(FactoryBeanTest.class);
try {
// 这里产生的就是BeanInfo对象(自定义bean)
BeanInfo object = bean1.getObject();
} catch (Exception e) {
e.printStackTrace();
}
}
BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext
FactoryBean是个bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由spring管理后,生产的对象是由getObject()方法决定的(返回自定义bean)。
3.后置处理器
Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和
BeanFactoryPostProcessor
BeanPostProcessor
BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean
接口主要有以下两个方法:
/**
* Bean初始化前执行
* @param bean 第一个参数是每个bean的实例
* @param beanName 第二个参数是每个bean的name或者id属性的值
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}
/**
* Bean初始化后执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return null;
}
该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏
初始化定义:
- bean实现了InitializingBean接口,对应的方法为afterPropertiesSet
- 在bean定义的时候,通过init-method设置的方法
定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String。
这里需要注意:BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后。
BeanFactoryPostProcessor
接口只有一个方法:
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置’order’属性来控制各个BeanFactoryPostProcessor的执行次序。
注意:
BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。
4.Bean初始化流程
1)根据配置情况调⽤ Bean 构造⽅法或⼯⼚⽅法实例化 Bean。
2)利⽤依赖注⼊完成 Bean 中所有属性值的配置注⼊。
3)如果 Bean 实现了 BeanNameAware 接⼝,则 Spring 调⽤ Bean 的 setBeanName() ⽅法传⼊当前 Bean 的 id 值。
4)如果 Bean 实现了 BeanFactoryAware 接⼝,则 Spring 调⽤ setBeanFactory() ⽅法传⼊当前⼯⼚实例的引⽤。
5)如果 Bean 实现了 ApplicationContextAware 接⼝,则 Spring 调⽤ setApplicationContext() ⽅法传⼊
当前 ApplicationContext 实例的引⽤。
6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调⽤该接⼝的预初始化⽅法
postProcessBeforeInitialzation() 对 Bean 进⾏加⼯操作,此处⾮常重要,Spring 的 AOP 就是利⽤它实现的。
7)如果 Bean 实现了 InitializingBean 接⼝,则 Spring 将调⽤ afterPropertiesSet() ⽅法。
8)如果在配置⽂件中通过 init-method 属性指定了初始化⽅法,则调⽤该初始化⽅法。
9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调⽤该接⼝的初始化⽅法 postProcessAfterInitialization()。此时,Bean 已经可以被应⽤系统使⽤了。
10)如果在 中指定了该 Bean 的作⽤范围为 scope=“singleton”,则将该 Bean 放⼊ Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的⽣命周期管理;如果在 中指定了该 Bean 的作⽤范围为 scope=“prototype”,则将该 Bean 交给调⽤者,调⽤者管理该 Bean 的⽣命周期,Spring 不再管理该 Bean。
11)如果 Bean 实现了 DisposableBean 接⼝,则 Spring 会调⽤ destory() ⽅法将 Spring 中的 Bean 销毁;如果在配置⽂件中通过 destory-method 属性指定了 Bean 的销毁⽅法,则 Spring 将调⽤该⽅法对 Bean 进⾏销毁。
注意:Spring 为 Bean 提供了细致全⾯的⽣命周期过程,通过实现特定的接⼝或 的属性设置,都可以对 Bean 的⽣命周期过程产⽣影响。
虽然可以随意配置 的属性,但是建议不要过多地使⽤ Bean 实现接⼝,因为这样会导致代码和 Spring 的聚合过于紧密
5.AOP相关
- Spring 实现AOP思想使⽤的是动态代理技术
- 默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术。
- 大致流程
- @EnableAspectJAutoProxy 开启AOP功能
- @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
- 容器的创建流程:
4.1. registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
4.2 finishBeanFactoryInitialization()初始化剩下的单实例bean
4.2.1 创建业务逻辑组件和切面组件
4.2.2 AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
4.2.3 组件创建完之后,判断组件是否需要增强是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib); - 执行目标方法:
5.1 代理对象执行目标方法
5.2 CglibAopProxy.intercept();
5.2.1 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
5.2.2 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
5.2.3 效果:
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知
6.@AutoWired注解自动装配的过程是怎样的?
使用@ Autowired注解来自动装配指定的bean。在使用@ Autowired注解之前需要在 Spring配置文件进行配置,< context:annotation-config/>在启动 spring IOC时,容器自动装载了一个 AutowiredAnnotation Bean PostProcessor后置处理器,当容器扫描到
@ Autowied、@ Resource或@Inject时,就会在loC容器自动查找需要的bean,并装配给该对象的属性。在使用@ Autowired时,首先在容器中查询对应类型的bean:
- 如果查询结果刚好为一个,就将该bean装配给@Autowired指定数据
- 如果查询结果不止一个,那么@Autowired会根据名称查找
- 如果上述查找结果为空,那么就会抛出异常
7. Spring 是如何解决循环依赖的?
Spring 循环依赖的场景有:
- 构造器循环依赖
- Field属性循环依赖
其中构造器循环依赖问题比较难解决,会抛出BeanCurrentlyInCreationException,在解决属性循环依赖时,spring采用的是提前暴露对象的方法(使用三级缓存)
假设BeanA 与 BeanB 互相依赖
- singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;
- earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;
- singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。
8. @Bean 和 @Component的区别
- @Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
- @Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。
两者对比:
相同点:
两者的结果都是为spring容器注册Bean.
不同点:
@Component 通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
@Bean 注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
@Bean 需要在配置类中使用,即类上需要加上@Configuration注解
@Configuration
public class WebSocketConfig {
@Bean
public Student student(){
return new Student();
}
}
那为什么有了@Compent,还需要@Bean呢?
如果你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component注解的,因此就不能使用自动化装配的方案了,但是我们可以使用@Bean,当然也可以使用XML配置。