2020-09-24

Spring源码(基于注解)

基于注解的Spring源码分析

//包含了配置类和包扫描
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); 

一、 组件添加

1、@ComponentScan

指定要扫描的包

2、@Bean

给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id

bean的生命周期(bean创建---初始化----销毁的过程)
1)、指定初始化和销毁方法;通过@Bean指定init-method和destroy-method;
2)、通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑);
3)、可以使用JSR250;
		@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
		@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4)、BeanPostProcessorinterface】:bean的后置处理器;在bean初始化前后进行一些处理工作;
		postProcessBeforeInitialization:在初始化之前工作
		postProcessAfterInitialization:在初始化之后工作
		
    BeanPostProcessor原理
    populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
    initializeBean 实例化bean
    {
        applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); //遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
        invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
        applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    Spring底层对 BeanPostProcessor 的使用;
		bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async等功能都是有xxx BeanPostProcessor实现的;

3、@Scope

调整作用域,一般有四种属性
prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中;每次获取的时候才会调用方法创建对象;
singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中;以后每次获取就是直接从容器(map.get())中拿,
request:同一次请求创建一个实例
session:同一个session创建一个实例

4、@Lazy

开启懒加载懒加载
单实例bean:默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;

5、@Primary

让Spring进行自动装配的时候,默认使用首选的bean;

6、@Configuration

配置类==配置文件,告诉Spring这是一个配置类

7. @Component代表

@Component,@Service,@Controller,@Repository:组件扫描中useDefaultFilters = true默认开启的,会扫描自己定义的类

8、@Conditional

类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;

@Conditional({WindowsCondition.class}) //widow系统@Configuration中的bean才生效

9、@Import

导入组件,id默认是组件的全类名,或实现ImportSelector或ImportBeanDefinitionRegistrar的全类名

@Import({Color.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})

工厂模式:实现FactoryBean 例如,FactoryBean

// 工厂Bean获取的是调用getObject创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
//要获取工厂Bean本身,我们需要给id前面加一个&:&colorFactoryBean
Object bean4 = applicationContext.getBean("&colorFactoryBean");

10、总结

给容器中注册组件;
1、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2、@Bean[导入的第三方包里面的组件]
3、@Import[快速给容器中导入一个组件]
3.1、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
3.2、ImportSelector:返回需要导入的组件的全类名数组;
3.3、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4、使用Spring提供的 FactoryBean(工厂Bean);
4.1、默认获取到的是工厂bean调用getObject创建的对象
4.2、要获取工厂Bean本身,我们需要给id前面加一个&用&colorFactoryBean

二、 组件赋值

1、@PropertySource

使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值

2、@PropertySources

PropertySource的重复使用模板

3、@Value

对Spring属性进行赋值

  • 基本数值
  • 可以写SpEL; #{}
  • 可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)

4、@Autowired

required默认为true,是给成员变量赋好值的(参考组件注入)。

5、@Profile

Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能,指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

  • 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
  • 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  • 没有标注环境标识的bean在,任何环境下都是加载的

三、 组件注入

自动装配,Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;

1、@Autowired:自动注入

​ 1、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
​ 2、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean(“bookDao”)
​ 3、@Qualifier(“bookDao”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名(强制使用)
​ 4、自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);
​ 5、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字(强制使用)
​ BookService{
​ @Autowired
​ BookDao bookDao;
​ }

2、@Resource(JSR250)和@Inject(JSR330)

spring也支持@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

@Resource:
可以和@Autowired一样实现自动装配功能;默认是按照属性名称进行装配的;
没有能支持@Primary功能没有支持@Autowired(reqiured=false);
@Inject: 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
@Autowired:Spring定义的; @Resource、@Inject都是java规范
AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;

3、 @Autowired使用位置

构造器,参数,方法,属性;都是从容器中获取参数组件的值
1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
3)、放在参数位置:

4、自定义组件

​ 自定义主键想要【使用】Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);

​ 自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
​ 把Spring底层一些组件注入到自定义的Bean中;
​ xxxAware的功能是使用xxxProcessor实现的/处理的
​ ApplicationContextAware==》ApplicationContextAwareProcessor;

四、 AOP动态代理

指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;

1. 创建aop的一般流程

  1. 导入aop模块;Spring AOP:(spring-aspects)
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aspects</artifactId>
   <version>4.3.12.RELEASE</version>
</dependency>
  1. 定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
public class MathCalculator {
   public int div(int i,int j){
      System.out.println("MathCalculator...div...");
      return i/j;
   }
}
  1. 定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;

通知方法:
前置通知(@Before):logStart:在目标方法(div)运行之前运行
后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())

  1. 给切面类的目标方法标注何时何地运行(通知注解–通知方法的使用);
  2. 将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
  3. 必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
 /**
  *切面类
  *@Aspect: 告诉Spring当前类是一个切面类
  */
  @Aspect
  public class LogAspects {

  //抽取公共的切入点表达式
  //1、本类引用
  //2、其他的切面引用
  @Pointcut("execution(public int com.aop.MathCalculator.*(..))")
  public void pointCut(){};

  //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
  @Before("pointCut()")
  public void logStart(JoinPoint joinPoint){
     Object[] args = joinPoint.getArgs();
     System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
  }

  @After("com.aop.LogAspects.pointCut()")
  public void logEnd(JoinPoint joinPoint){
     System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
  }

  //JoinPoint一定要出现在参数表的第一位
  @AfterReturning(value="pointCut()",returning="result")
  public void logReturn(JoinPoint joinPoint,Object result){
     System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
  }

  @AfterThrowing(value="pointCut()",throwing="exception")
  public void logException(JoinPoint joinPoint,Exception exception){
     System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
  }

}
  1. 给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
   //业务逻辑类加入容器中
   @Bean
   public MathCalculator calculator(){
      return new MathCalculator();
   }
   //切面类加入到容器中
   @Bean
   public LogAspects logAspects(){
      return new LogAspects();
   }
}
  1. 方法测试
public class IOCTest_AOP {
   @Test
   public void test01(){
      AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
      //1、不要自己创建对象
      // MathCalculator mathCalculator = new MathCalculator();
      // mathCalculator.div(1, 1);
      MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
      mathCalculator.div(1,0);
      applicationContext.close();
   }
}

2. aop一般流程总结

  1. 将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
  2. 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
  3. 开启基于注解的aop模式;@EnableAspectJAutoProxy

3. aop原理分析

在Spring中很多的 @EnableXXX和aop的原理相识
AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】

3.1 分析继承注解以及继承关系
  1. @EnableAspectJAutoProxy是什么?

    @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar利用 AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion

    internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

    给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;(代理模式-自动代理创建器)

  2. AnnotationAwareAspectJAutoProxyCreator: 继承关系

​ AnnotationAwareAspectJAutoProxyCreator
​ ->AspectJAwareAdvisorAutoProxyCreator
​ ->AbstractAdvisorAutoProxyCreator
​ ->AbstractAutoProxyCreator
​ implements SmartInstantiationAwareBeanPostProcessor(bean的后置处理器), BeanFactoryAware(自动装配)

​关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;
AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory() ----加断点
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()

3.2 源码流程分析

​ 1、传入配置类,创建ioc容器

​ 2、注册配置类,调用refresh()刷新容器;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXiUBpmA-1600904020802)(G:\学习\typora文件总结\spring源码\微信截图_20200923235601.png)]

​ 3、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;

​ 3.1 、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KDBTiEuv-1600904020818)(G:\学习\typora文件总结\spring源码\1600839708525.png)]

​ 3.2、给容器中加别的BeanPostProcessor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWwGxM19-1600904020825)(G:\学习\typora文件总结\spring源码\微信截图_20200923125425.png)]

​ 3.3、优先注册实现了PriorityOrdered接口的BeanPostProcessor;

​ 3.4、再给容器中注册实现了Ordered接口的BeanPostProcessor;

​ 3.5、注册没实现优先级接口的BeanPostProcessor;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efeQ1izh-1600904020833)(G:\学习\typora文件总结\spring源码\1600840062216.png)]

​ 3.6、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MnXSFH17-1600904020837)(G:\学习\typora文件总结\spring源码\微信截图_20200923135202.png)]

​ 创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
​ 3.6.1、创建Bean的实例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6bG9r4V-1600904020843)(G:\学习\typora文件总结\spring源码\微信截图_20200924002958.png)]

​ 3.6.2、populateBean;给bean的各种属性赋值

​ 3.6.3、initializeBean:初始化bean;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24UjRwwE-1600904020845)(G:\学习\typora文件总结\spring源码\微信截图_20200924003149.png)]

​ 3.6.3.1、invokeAwareMethods():处理Aware接口的方法回调

​ 3.6.3.2、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()

​ 3.6.3.3、invokeInitMethods();执行自定义的初始化方法

​ 3.6.3.4、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();

​ 3.6.4、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;–》aspectJAdvisorsBuilder

​ 3.7、把BeanPostProcessor注册到BeanFactory中;beanFactory.addBeanPostProcessor(postProcessor);

以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程======

​ AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor

​ 4、finishBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化工作;创建剩下的单实例bean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WJljBUdc-1600904020847)(G:\学习\typora文件总结\spring源码\微信截图_20200924004110.png)]

​ 4.1、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);

​ getBean->doGetBean()->getSingleton()->getObject->createBean

​ 4.2、创建bean

​ 【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】

​ 4.2.1、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来

​ 4.2.2、createBean();创建bean;

​ AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例

​ 【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】

​ 【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

​ 4.2.2.1、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation

​ 希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

​ 4.2.2.1.1、后置处理器先尝试返回对象;

​ bean = applyBeanPostProcessorsBeforeInstantiation(): 拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor; 就执行postProcessBeforeInstantiation

​ if (bean != null) {

​ bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

​ }
​ 4.2.2.1.2、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;

3.3 AnnotationAwareAspectJAutoProxyCreator说明

AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:

1、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator和LogAspect的创建
1.1、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
1.2、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
或者是否是切面(@Aspect)

  1. 3、是否需要跳过
    1.3.1、获取候选的增强器(切面里面的通知方法)【List candidateAdvisors】
    每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
    判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
    1.3.2、永远返回false
    2、创建对象
    postProcessAfterInitialization;
    return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
    2.1、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
    1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
    2、获取到能在bean使用的增强器。
    3、给增强器排序
    2.2、保存当前bean在advisedBeans中;
    2.3、如果当前bean需要增强,创建当前bean的代理对象;
    2.3.1、获取所有增强器(通知方法)
    2.3.2、保存到proxyFactory
    2.3.3、创建代理对象:Spring自动决定
    JdkDynamicAopProxy(config);jdk动态代理;
    ObjenesisCglibAopProxy(config);cglib的动态代理;
    2.4、给容器中返回当前组件使用cglib增强了的代理对象;
    2.5、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
    3、目标方法执行 ;
    容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
    3.1、CglibAopProxy.intercept();拦截目标方法的执行
    3.2、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    3.2.1、List interceptorList保存所有拦截器 5
    一个默认的ExposeInvocationInterceptor 和 4个增强器;
    3.2.2、遍历所有的增强器,将其转为Interceptor;
    registry.getInterceptors(advisor);
    3.2.3、将增强器转为List;
    如果是MethodInterceptor,直接加入到集合中
    如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
    转换完成返回MethodInterceptor数组;
    3.3、如果没有拦截器链,直接执行目标方法;
    拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
    3.4、如果有拦截器链,把需要执行的目标对象,目标方法,
    拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
    并调用 Object retVal = mi.proceed();
    3.5、拦截器链的触发过程;
    3.5.1、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
    3.5.2、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
    拦截器链的机制,保证通知方法与目标方法的执行顺序;
3.4总结:

​ 1、 @EnableAspectJAutoProxy 开启AOP功能
​ 2、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
​ 3、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
​ 4、容器的创建流程:
​ 4.1、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
​ 4.2、finishBeanFactoryInitialization()初始化剩下的单实例bean
​ 4.2.1、创建业务逻辑组件和切面组件
​ 4.2.2、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
​ 4.2.3、组件创建完之后,判断组件是否需要增强
​ 是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
​ 5、执行目标方法:
​ 5.1、代理对象执行目标方法
​ 5.2、CglibAopProxy.intercept();
​ 5.2.1、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
​ 5.2.2、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
​ 5.2.3、效果:
​ 正常执行:前置通知-》目标方法-》后置通知-》返回通知
​ 出现异常:前置通知-》目标方法-》后置通知-》异常通知
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值