spring 创建bean的过程
生成BeanDefinition -- > 合并BeanDefinition --> 创建单例Bean对象 (非懒加载) --> 依赖注入 (属性赋值) --> 初始化前 (@PostConstruct)-->初始化 (InitializingBean)-->初始化后 (AOP)-->bean
AbstractAutowireCapableBeanFactory.doCreateBean (创建bean) --> instanceWrapper.getWrappedInstance (实例化bean) --> populateBean (填充属性)-->initializeBean (初始化)-->applyBeanPostProcessorsAfterInitialization(AOP)
生成BeanDefinition
spring启动时会进行扫描,会先调用
org.Springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComonents(String basePackage) 扫描某个包路径,并得到BeanDefinition的Set集合.
- 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(封装成Resource对象)
- 遍历每个Resource对象
- 利用MetadataReaderFactory解析Resourece对象,得到MetadataReader
- 利用MetadataReader的具体实现类进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选
- 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
- 在基于metadataReader判断对应的是否是接口或者抽象类
- 如果筛选通过则表示扫描到一个bean,将ScannedGenericBeanDefinition加入结果集
- Definition对象的加载采用ASM技术,并没有加载到jvm
- MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,可以获取
类名
父类名
所以实现接口名
所有内部类名
判断是不是抽象类
判断是不是接口
判断是不是注解
获取拥有某个注解的方法集合
获取类上所有注解信息
获取类上添加的所有注解类型集合
在创建bean时,会创建BeanDefiniton对象,该对象会存储bean的一些注解信息,bean信息等。
模拟创建BeanDefinition:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
beanDefinition.setScope("prototype");
//注册bean
context.registerBeanDefinition("userService",beanDefinition);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);
reader.register(UserService.class);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.example");
合并BeanDefinition
spring支持创建父子bean,则子bean对应的BeanDefinition需要合并父Bean的BeanDefinition的信息。
创建单例Bean对象
-
这一步就会进行实例化bean,如果.class文件还没有加载,会在实例化前进行加载。
-
在类加载完后,可以实现InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),该方法会在加载完所有类,但是在bean实例化前被调用
-
postProcessBeforeInstantiation可以有返回值,如果返回某个对象,则spring不会对该对象进行实例化和依赖注入,会直接进入初始化后。
@Component
public class UserBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("实例化前");
return new UserService();
}
return null;
}
}
调用构造方法
推断构造方法:
- bean实例化默认调用无参构造方法
- bean实例化时如果有一个非无参构造方法则调用该方法
- bean实例化时如果有多个构造方法则调用无参方法
- bean实例化时如果有多个构造方法且没有无参方法,抛出异常
- bean实例化时有参构造方法的入参必须是对象,spring容器中如果没有对应bean,抛出异常。
- bean实例化时有参构造会,spring容器进行参数注入时会先根据类型查找过滤,然后再根据bean的名称过滤。 所以:
1.如果存在多个该类型的bean,一个同名的bean则注入该bean;
2.如果存在一个该类型的bean,bean名称和入参不同,注入该bean;
3.如果存在多个该类型的bean,bean名称和入参都不相同,抛出异常。
依赖注入(属性赋值)
例如@Autowired,初始化前会通过遍历object.class.getFields(),判断field.isAnnotationPresent(Autowired.class),拿到对应的field进行依赖注入。
初始化前
@PostConstruct:初始化前会通过遍历object.class.getMethods(),判断method.isAnnotationPresent(PostConstruct.class),然后method.invoke()
所以我们可以在bean中的方法上加上@PostConstruct方法,使该方法可以在bean初始化前被调用。
初始化
InitalizingBean:初始化时会判断bean instance of InitalizingBean(是否实现InitalizingBean接口),并且 调用其afterPropertiesSet()。
所以可以在Bean上实现InitializingBean接口,并实现afterPropertiesSet方法,使其在bean初始化时被调用。
BeanPostProcessor:BeanPostProcessor有初始化前和初始化后的方法,同样可以自己实现
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String value() default "";
}
@Component
public class UserService {
@TestAnnotation("test")
private String test;
}
@Component
public class TestBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof UserService){
for (Field field : bean.getClass().getFields()) {
if(field.isAnnotationPresent(TestAnnotation.class)){
try {
field.set(bean,field.getAnnotation(TestAnnotation.class));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
return bean;
}
}
初始化后
- 如果进行AOP会产生一个代理对象,代理对象会成为spring的AOP
- 该代理对象不会有依赖注入的bean
- 该代理对象会继承被代理的对象,并且代理对象会先调用AOP逻辑
- 该代理对象会放入被代理对象,在AOP逻辑完成后,使用被代理对象调用方法(被代理对象拥有依赖注入)。
- AOP中 joinPoint.getTarget()可以获取被代理对象
- 遍历所有的切面bean,遍历切面bean中方法,找到被代理对象的方法时则表示该被代理对象经行了AOP,则将其缓存到map,被代理对象触发AOP时使用
- spring事务也会生成代理对象:1.判断是否有注解@Transactional;2.创建一个数据库连接(事务管理器dataSource创建);3.conn.autocommit = false(修改属性,关闭自动提交事务);4.执行被代理对象方法;5.提交事务或者回滚。
- 当在上一点的第四点中,被代理对象中调用另一个带有@Trasactional的方法时会导致事务失效,因为当前对象不是代理对象。解决方法可在当前被代理对象中自己注入自己获得代理对象,用代理对象调用另一个事务方法。