美文网首页
Spring Bean原理学习

Spring Bean原理学习

作者: 蓝调_4f2b | 来源:发表于2023-07-02 00:35 被阅读0次

1. Bean的生命周期

(1)生成BeanDefinition
(2)合并BeanDefinition
(3)加载类
(4)实例化前
(5)实例化
(6)BeanDefinition的后置处理
(7)实例化后
(8)自动注入
(9)处理属性
(10)执行Aware
(11)初始化前
(12)初始化
(13)初始化后

1.1 生成BeanDefinition

Spring启动的时候会进行扫描,会先调用
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandid
ateComponents(String basePackage) 扫描某个包路径,并得到BeanDefinition的Set集合。

1.2 合并BeanDefinition

在父子BeanDefinition的情况下:

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。

1.3 加载类

合并BeanDefinition后,尝试创建Bean对象,创建的第一步为对象的实例化,所以首先要进行对象所属类的加载resolveBeanClass(mbd, beanName):若果beanClass属性的类型是Class,那么就直接返回,如果不是,则会根据类名进行加载

1.4 实例化前

在Spring中,实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作(BeanPostProcessor)

1.5 实例化

根据BeanDefinition去创建一个对象.
(1)Supplier创建对象
首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象。
(2)工厂方法创建对象
检查BeanDefinition中是否设置了factoryMethod,有两种方式可以设置factoryMethod
方式一:

<bean id="userService" class="com.zhouyu.service.UserService" factory‐
method="createUserService" />
// bean类型定义
public class UserService {
public static UserService createUserService() {
System.out.println("执行createUserService()");
UserService userService = new UserService();
return userService;
}
}

方式二:对于抽象类型的bean的创建

<bean id="commonService" class="com.zhouyu.service.CommonService"/>
<bean id="userService1" factory‐bean="commonService" factory‐method="createUserService"
/>
// bean的定义
public class CommonService {
public UserService createUserService() {
return new UserService();
}
}

(3)bean对象构造方法推断
判断使用哪种构造方法进行对象的构建
特殊的@lookup注解

@Component
public class UserService {
private OrderService orderService;
public void test() {
OrderService orderService = createOrderService();
System.out.println(orderService);
}
@Lookup("orderService")
public OrderService createOrderService() {
return null;
}
}

1.6 BeanDefinition的后置处理

Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点
MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()

1.7 实例化后

在处理完BeanDefinition后,Spring又设计了一个扩展点InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),该扩展点在源码中使用次数极低

1.8 自动注入

自动注入对象依赖的属性

1.9 处理属性

这个步骤中,会处理@Autowired、@Resource、@Value等注解,也是通过postProcessProperties()扩展点来实现的,处理方式使用依赖注入

1.10 执行Aware

完成了属性赋值之后,Spring会执行一些回调

1.11 初始化前

初始化前,也是Spring提供的一个扩展点:
BeanPostProcessor.postProcessBeforeInitialization(),在该扩展点可以实现对完成依赖注入的Bean的处理

1.12 初始化

(1)查看当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()
方法
(2)执行BeanDefinition中指定的初始化方法

1.13 初始化后

这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization()

1.14 Bean的销毁

Bean销毁是发送在Spring容器关闭过程中的。
销毁过程中涉及到一种设计模式:适配器模式(adapter同时实现目标接口与传入接口,将传入接口的数据转换为目标接口可接受零星)
在销毁时,Spring会找出实现了DisposableBean接口的Bean。
但是我们在定义一个Bean时,如果这个Bean实现了DisposableBean接口,或者实现了
AutoCloseable接口,或者在BeanDefinition中指定了destroyMethodName,那么这个Bean都属
于“DisposableBean”(目标接口),这些Bean在容器关闭时都要调用相应的销毁方法。
所以,这里就需要进行适配,将实现了DisposableBean接口、或者AutoCloseable接口等适配成实现了DisposableBean接口,所以就用到了DisposableBeanAdapter。
会把实现了AutoCloseable接口的类封装成DisposableBeanAdapter,而DisposableBeanAdapter
实现了DisposableBean接口

2. 依赖注入

2.1 手动注入:包括xml注入及构造方法注入

(1)xml注入:底层通过set方法完成注入

<bean name="userService" class="com.luban.service.UserService">
<property name="orderService" ref="orderService"/>
</bean>

(2)构造方法注入:

<bean name="userService" class="com.luban.service.UserService">
<constructor‐arg index="0" ref="orderService"/>
</bean>

2.2 Autowired自动注入

(1) XML的autowire自动注入

<bean id="userService" class="com.luban.service.UserService" autowire="byType"/>
//表示Spring会自动的给userService中所有的属性自动赋值(不需要这个属性上有@Autowired注解,但需要这个属性有对应的set方法)

支持进行自动注入的方式有:
(寻找set方法进行注入)byType,byName,
(寻找构造方法进行注入)constructor,default,no;
在创建Bean的过程中,在填充属性时,Spring会去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象;
注1:byName解析:
这个name并不是方法的名字,而是拿方法名字进过处理后的名字
i. 如果方法名字以“get”开头,比如“getXXX”,那么name=XXX
ii. 如果方法名字以“is”开头,比如“isXXX”,那么name=XXX
iii. 如果方法名字以“set”开头,比如“setXXX”,那么name=XXX
注2:通过构造方法的属性注入
如果是constructor,那么就可以不写set方法了,当某个bean是通过构造方法来注入时,spring利用构造方法的参数信息从Spring容器中去找bean,找到bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值
(2)通过@Autowired注解方式注入
优点:比xml配置文件提供更细粒度的自动注入方式(XML中的autowire控制的是整个bean的所有属性,而@Autowired注解是直接写在某个属性、某个set方法、某个构造方法上的)

2.3 基于@AutoWired注解底层注入原理

@Autowired注入举例:

// 普通注入点
@Autowired
private OrderService orderService;
// set方法注入
@Autowired
public void setOrderService(OrderService orderService);

2.3.1 各位置@Autowired寻找Bean:

(1)属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
(2)构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
(3)set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

2.3.2 寻找注入点

AutowiredAnnotationBeanPostProcessor:
在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的
postProcessMergedBeanDefinition()找出注入点并缓存

1. 遍历当前类的所有的属性字段Field
2. 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段
是一个注入点
3. 如果字段是static的,则不进行注入
4. 获取@Autowired中的required属性的值
5. 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到
currElements集合中。
6. 遍历当前类的所有方法Method
7. 判断当前Method是否是桥接方法,如果是找到原方法
8. 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法
是一个注入点
9. 如果方法是static的,则不进行注入
10. 获取@Autowired中的required属性的值
11. 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到
currElements集合中。
12. 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。
13. 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合
对象,并缓存。

2.3.3 对注入点进行依赖注入

Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历所找到的注入点依次进行注入。
(1)字段注入

1. 遍历所有的AutowiredFieldElement对象。
2. 将对应的字段封装为DependencyDescriptor对象。
3. 调用BeanFactory的resolveDependency()方法,进行依赖查找,找到当前字段所匹配的Bean对象。
4. 将匹配结果存入缓存(比如当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了)
5. 利用反射将结果对象赋值给字段。

(2)set方法注入

1. 遍历所有的AutowiredMethodElement对象
2. 遍历将对应的方法的参数,将每个参数封装MethodParameter对象
3. 将MethodParameter对象封装为DependencyDescriptor对象
4. 调用BeanFactory的resolveDependency()方法,进行依赖查找,找到当前字段所匹配的Bean对象。
5. 将匹配结果存入缓存(比如当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了)
6. 利用反射将结果对象赋值给字段。

2.4 重点方法详解

2.4.1 findAutowireCandidates

寻找autowired候选:

1. 找出BeanFactory中类型为type的所有的Bean的名字
2. 把resolvableDependencies中key为type的对象找出来并添加到result中
3. 对找到的beanName进行判断,是否可进行自动注入(检查beanDefinition中的autowireCandidate属性是否为true)
4.  判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配
5. 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
6. 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中

2.4.2 @Qualifier的使用

(1)解决@Autowired注解注入时多个bean不知加载哪个的问题,通过@Qualifier指定要加载的bean

@component("test")
public class TestImpl impements Test {}

@component("test2")
public class TestImpl2 impements Test {}

@component
public class Service {
  @Autowired
  @Qualifier("test")
  private Test testMatters;
}

(2)与@Primary区别
@Primary注解标注在默认情况下优先注入哪个bean

@component
@Primary
public class TestImpl impements Test {}

@component
public class TestImpl2 impements Test {}

@component
public class Service {
  @Autowired
  private Test testMatters;
}

2.5 依赖注入总体流程

(1)先找到所有注入点
(2)遍历每个注入点
(3)根据注入点类型找到Bean
(4)对找到的多个Bean:
单例Bean:直接注入该Bean
多例Bean:向下判断
(5)先判断isAutoWiredCandidate(是否为autowired候选)
(6)判断是否符合@Qualifier
(7)若还有多个Bean
(8)取@Primary标注的Bean
(9)取优先级最高的Bean
(10)进行名称筛选:
属性注入:则为属性名
set方法注入:为参数名称
(11)结束

2.6 @Resource注解

(1)同样标注注入点,通过调用inject()方法完成注入BeanPostProcess中;
(2)与@Autowired区别
@Resource不依赖Spring框架,底层不同
性能无区别

3. 循环依赖问题

3.1 循环依赖问题

// A依赖了B
class A{
public B b;
}
// B依赖了A
class B{
public A a;
}

3.2 解决思路

通过存入缓存的方式进行解决
(1)实例化一个service -> 得到一个对象,放入临时Map中<name, aBean>
(2)实例化BService过程中,发现无单例,首先尝试从Map中获取到bBean

3.3 解决方案 -- 三级缓存

(1)singletonObjects单例池
(2)earlySingletonObjects存储代理对象/早期未完成初始化对象
解决创建过程中原始对象无处存放的问题;
(3)SingtonFactories存放Aservice创建时原始对象,在Bservice依赖时获取
打破循环依赖的关键,未初始化的属性预先放入缓存中(第一次遇到即放入);

3.4 总体流程

(1)填充Aservice属性
(2)去单例池中找
(3)CreateSet
(4)出现循环依赖
(5)尝试从earlySingletonObjects中寻找
(6)找不到,从最底层SingletonFactories中查找,肯定可以找到
(7)执行lambda
(8)将lambda生成的Bean存入earlySinletonObjects中,并返回

3.5 补充

(1)原型Bean循环问题可以解决吗?
不可解决,若两个Bean均为原型,则要求每次创建一个新实例,走不出循环;
(2)自身循环注入问题
同样使用三级缓存解决;

4. Spring寻找Bean构造器的集中方式

(1)默认情况下,用无参构造方法/仅有的一个构造方法
(2)用户指定构造方法入参值:通过getBean()或BeanDefinition中getConstructorArgumentValues()指定指定,框架通过参数数量,类型等因素匹配构造方法
(3)由框架自动选择构造方法及入参,xml配置寻找方法

<bean id = "userService" class = "userService" autowired = "byType"/>  //  向下寻找set方法

(4)通过注解@Autowired注解指定某个构造方法,希望框架自动找入参值

<bean id = "userService" class = "userService" autowired = "byName"/>
public class userService() {}

4.1 补充

(1)@Bean
Spring框架将@Bean修饰的方法解析为BeanDefinition

public class Appconfig {
  @Bean
  public static UserService userService() {} // 生成一个BeanDefinition
  @Bean
  public static UserService userService(OrderService order) {}
  // 发现已有BeanDefinition,复用即可
}

(2)@lookUp的使用
@lookUp将按照用户指定方法生成代理对象

@Component
public class UserService {
  @Autowired
   private OrderService orderService;
   public void test() {}
  @LookUp("OrderService")
   public OrderService func() {
    // 调用时,获得新的OrderService的Bean代理对象 
    return null;
  }
}
@Component
@Scope("prototype")
public class OredrService {}

5. Spring框架启动原理

5.1 流程

(1)构造一个BeanFactory对象
(2)解析配置类,得到BeanDefinition,并注册到BeanFactory中
(3)因为ApplicationContext还支持国际化,所以还需要初始化MessageSource对象
(4)因为ApplicationContext还支持事件机制,所以还需要初始化ApplicationEventMulticaster对象
(5)把用户定义的ApplicationListener对象添加到ApplicationContext中,等Spring启动完了就要发布事件了
(6)创建非懒加载的单例Bean对象,并存在BeanFactory的单例池中
(7)调用Lifecycle Bean的start()方法
(8)发布ContextRefreshedEvent事件

5.2 BeanFactoryPostProcessor

BeanPostProcessor表示Bean的后置处理器,是用来对Bean进行加工的,类似的,BeanFactoryPostProcessor理解为BeanFactory的后置处理器,用来用对BeanFactory进行加工的

@Component 
public class ZhouyuBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 
  @Override 
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)  {
 BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService"); beanDefinition.setAutowireCandidate(false); 
  }
}

5.3 如何理解refresh()

ConfigurableApplicationContext接口上refresh()方法的注释,意思是:加载或刷新持久化的配置,可能是XML文件、属性文件或关系数据库中存储的。

5.4 容器启动总结

(1)创建DefaultListableBeanFactory
(2)解析配置类
(3)扫描@Bean
(4)生成BeanDefinitionMap,BeanPostProcessor单例池

6. 配置类源码解析

6.1 不同解析配置类型

(1)Component 解析该配置类
(2)ComponentScan 扫描并注册BeanDefinition;有配置类则解析
(3)Import 调到ProcessImports()处理所导入的类
(4)ImportRource 将所导入的xml文件路径添加入当前配置类importedResource属性
(5)配置类中含有@Bean 将@Bean修饰的方法封装为BeanMethod对象,并添加入当前配置类beanMethods属性
最后:保存为BeanDefinition对象存入Factory中,获得配置类对应的Bean

6.2 补充@configuration注解

@configuration   // 生成AppConfig的代理对象
public class Appconfig {
  @Bean
  public UserService userService() {
    // 将会执行代理对象的构造方法
    return new UserService();  
  }
}

注:BeanMethodInterceptor拦截器返回Bean对象
(1)有@configuration,返回它的代理对象
(2)无,返回Bean对象

7. Spring Aop原理

7.1 动态代理

(1)应用场景:

@Component
public class UserService {
  public void test() {
    println();
  }
}
public class Test() {
  public static void main(String[] args) {
    UserService service = new UserService();
    service.test();   //  有什么方法可以在不修改Test()方法的前提下,为service.test()前增加额外功能
  }
}

(2)解决方案:动态代理设计模式

UserService target = new UserService();
Enhance en = new Enhance();    // cgLib增强器
en.setSuperClass(UserService.class);
en.setCallbacks(new Callback[] { new MethodInterceptor() {
    public Object intercept(Object o, Method m, ...) {
        //  切入逻辑
        Object res = m.invoke(target, o);   // java反射
        return res;
    }
  }
});
// 调用时
UserService userService = (UserService)en.create();
userService.test();   // 含新逻辑

(3)利用JDK动态代理来生成一个代理对象:

UserService target = new UserService();
// UserInterface接口的代理对象
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]
{UserInterface.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxy;
userService.test()

7.2 Spring框架对AOP的处理 -- ProxyFactory

在Spring中进行了封装,封装出来的类叫做ProxyFactory,
表示是创建代理对象的一个工厂

UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

通过ProxyFactory,我们可以不再关系到底是用cglib还是jdk动态代理了,ProxyFactory会帮我们去判断,如果UserService实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术,上面的代码,就是由于UserService实现了UserInterface接口,所以最后产生的代理对象是UserInterface类型。

7.3 其他重要特性

  1. Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
  2. Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  3. Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接
    点周围维护一个Interceptor链
  4. Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
  5. Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  6. Target object:目标对象,被代理对象
  7. AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
  8. Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP

7.4 创建代理对象的方式

我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象,而这些,Spring都是支持的。
(1)ProxyFactoryBean

@Bean
public ProxyFactoryBean userServiceProxy(){
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
proxyFactoryBean.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
    System.out.println("before...");
    Object result = invocation.proceed();
    System.out.println("after...");
    return result;
  }
});
return proxyFactoryBean;
}

通过这种方法来定义一个UserService的Bean,并且是经过了AOP的。但是这种方式只能针对某一个Bean。它是一个FactoryBean,所以利用的就是FactoryBean技术,间接的将UserService的代理对
象作为了Bean。
(2)通过注解定义PointCut以及Advice

@Aspect
@Component
public class ZhouyuAspect {
@Before("execution(public void com.zhouyu.service.UserService.test())")
public void zhouyuBefore(JoinPoint joinPoint) {
System.out.println("zhouyuBefore");
}
}

(3)Spring框架中整合ProxyFactory实践

@ComponentScan("com.test")
public class Appconfig {
  @Bean    // 将ProxyFactory功能注册为Bean;
          //  ProxyFactory实现了BeanPosytProcess接口,构造器将在其生命周期中执行
  public ProxyFactoryBean userSecvice() {
      ProxyFactoryBean proxy = new ProxyFactoryBean();
      proxy.addAdvice(new zhouyuBeforeAdvice());  // 实现接口MethodBeforeAdvice
      proxy.setTarget(userService);
      return proxy;
   }
}
// 使用时:
public static void main(String[] args) {
  ...
  UserService service = application.getBean("Appconfig.class");
  service.test();
  ...
}

7.5 AOP总结

  1. 总体由BeanPostProcessor实现
  2. 核心方法:
PostProcessAfterInitalization() {
  ...
  wrappIfNecessary(bean, beanName, cacheKey);
  ...
}
  1. 寻找当前Bean匹配的advice,可以找到则创建proxyFactory
  2. findCandidatesAdviors : 由BeanFactory找到所有切面Bean;有@aspect注解,则解析,返回整个列表List
  3. 对List执行业务筛选,依次调用matches()方法进行匹配
  4. 返回proxyFactory<List>
  5. 执行返回proxyFactoryList中advice的invoke逻辑及代理逻辑

8. Spring事务

8.1 Aop流程总结

(1)Advisor类包括

  • pointcut切点类型:
    ClassFilter 类型匹配
    MethodMatcher 方法匹配
  • Advice: 方法拦截器 + 实际执行的逻辑
    (2)流程总结:
    初始化UserService Bean
    找到所有匹配的Advisor
    利用ProxyFactory生成代理对象

8.2 事务原理

@Transactional
public void test() {
  jdbcTemplate.execute("insert into t1 value(1, 1, 1)");
}

(1)通过pointcut匹配生成的代理对象Advisor:
BeanFactoryTransactionAttributeSourceAdvisor

UserService userService = application.getBean("userService");
userService.test();

(2)流程(invoke):

  • Spring事务管理器,创建DB连接Conn
  • conn.autoCommit = false;放入ThreadLocal<Map>中;key(DataSource): Value(conn)
  • target.test() 执行sql语句
  • 提交,回滚
    (3)事务传播机制 @transactional(propagation = xxx)
  • Spring事务管理器
  • Conn放入ThreadLocal<Map>中
  • target.test()
  • a方法
  • Spring创建DB连接conn2
  • conn2放入ThreadLocal中
  • conn2提交
  • conn提交
    注:多层结构使用递归方式解决

相关文章

网友评论

      本文标题:Spring Bean原理学习

      本文链接:https://www.haomeiwen.com/subject/epuxudtx.html