spring源码

spring源码

一、IOC模块:组件注册

1.原始方式,是通过配置xml配置文件,写bean类。使用如下ClassPathXmlApplicationContext获取类。

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");  
RegisterDAO registerDAO = (RegisterDAO)ac.getBean("RegisterDAO");  

2.注解方式,通过@Configuration+@Bean的方式。其中@Scope可以配置单例(只需一份实例)还是多例(适用于并发环境,但其实我们还是更倾向于使用代码控制并发),request还是session。区别:单例在spring启动时,不管调不调用直接进入spring容器(因为是单实例嘛,所以干脆直接加入了)。而多例只在使用时才加入,且每次使用都会初始化一个新的类。

由此才有了@Lazy去懒加载,保证默认的单例不会加入容器,只有使用时才会。

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        //ctx.register(AppConfig.class);相当于读哪份配置文件
        ctx.refresh();
        Entitlement ent = (Entitlement)ctx.getBean("entitlement");

重点:@Component主要注入自己写的类交给spring管理,@Bean注入第三方包交给spring管理。

但是实际上对于无参的第三方包可以直接通过@import(类.class)导入,方便多了

3.包扫描作用:让@Component,@Controller这些注解自动注入spring管理,并且自动装配。否则得用application对象完成这些操作。

同样的,xml需要有一句话配置,注解也需要有个注解。@ComponentScan

@ComponentScan(里面的确还有很多配置,比如扫描的包,去除和只包含哪些)其实注解里面方法就是这样配置 方法名=””

@ComponentScan(value = "com.nrsc.springstudy.c1_componentscan1.study2.config2_test",
        includeFilters = {
                @Filter(type = FilterType.ANNOTATION, classes = {Component.class})},
        useDefaultFilters = false)

总结:

1.配置的所有@Component等注解,spring不能主动进行管理。所以得要我们告诉他哪些需要管理,所以才有了@ComponentScan。

二、生命周期

可自定义bean的初始化和销毁。@Bean(initMethod= ,destoryMethod=)

1.通过@Bean注解中配置初始化和销毁的方法。(单例是执行初始化和销毁的方法。但是多例是不会调用销毁方法,spring要求自己销毁)
2.通过bean实现接口initializingBean,afterPropertieset()方法定义初始化,通过实现DisposableBean接口,destory()定义销毁方法。

(Timing:对象创建并赋值之后)

3.@PostConstruct配置初始化,@PreDestory配置销毁

(Timing:对象创建并赋值之后)

4.bean的后置处理器beanPostProcessor接口。postProcessBeforeInitialization,postProcessAfterInitialization

(Timing:对象创建并赋值之后,在其他任何初始化调用之前,在初始化调用之后。初始化前后。作用:在bean初始化前后完成操作)

注:1.spring底层用beanPostProcessor也用的非常多。2.必须在对象装配完属性之后

大概过程:

  1. bean实例化

    finishBeanFactoryInitialization
    getBean(就是doGetBean,只是一层包装)
    doGetBean 
    getSingleton
    createBean
    doCreateBean(包含了populateBean,initializeBean的全过程,即使通过这个方法来完成后面的方法)
    以下是2,3,4是docreateBean中的执行方法。
    
  2. createBeanInstance 通过bean的name得到一个BeanWrapper,通过这个调用getWrappedInstance就可以得到我们要的bean。

  3. 属性赋值 Populate populateBean() -> 属性赋值 (AutowiredAnnotationBeanPostProcessor,一般是这个进行属性装配)

  4. 初始化 Initialization initializeBean() -> 初始化 (上面的4种方法都是围绕这个)

    initializeBean的过程如下:

    applyBeanPostProcessorsBeforeInitialization 遍历取出所有的beanPostProcessor,进行执行相应的前置处理
    invokeInitMethods 初始化方法(就是我们定义的前几种初始化的方法,执行init-method、postConstract注解的方法、afterPropertiesSet)
    applyBeanPostProcessorsAfterInitialization  遍历取出所有的beanPostProcessor,进行执行相应的后置处理
    
  5. 销毁 Destruction

bean的赋值(@value),注入其他组件(xxxAware所有接口),@Autowired(AutowiredAnnotationBeanPostProcessor,其实查看源码可以发现,他还支持@value,@autowired,@inject),生命周期注解功能(@PostConstruct属于InitDestroyAnnotationBeanPostProcessor)…..等等功能,都是beanPostProcessor的实现类实现的。

三、快速获取application对象

1.实现Spring的ApplicationContextAware接口,重写setApplicationContext方法,将得到的ApplicationContext对象保存到一个静态变量中,有了这个上下文对象,就可以在项目的任意地方用它来得到任意Bean;

2.直接注入对象

 @Autowired
 private ApplicationContext applicationContext;

四、属性赋值(AutowiredAnnotationBeanPostProcessor)

1.@Vlaue(${}):取出配置文件中的值(可以指定读取配置)。@Vlaue(#{}):使用Spel表达式取值

2.@Autoired:默认是按类型去容器中找组件。如果找到多个,默认按属性名作为id。加上@Qualifer可以直接指定名字。默认必须装配成功,装配失败则报错。当然里面也有属性可以配置。@Autowired(required =false)

3.@Resource java规范注解,仅提供按名称注入。

4.@Inject java规范注解额,需要额外导包,和@Autoired效果一样。但是不支持required。

重点介绍:@Autoired

作用域:@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

即可以加到构造器,方法,属性等上。

@Autoired//加在方法上,传入的参数从容器中拿。加在参数上也是一样。用构造器也是这样,但是对于只有一个有参构造器,可以省略注解
public void setCar(Car car){
		this.car=car;
}

5.获取spring底层组件,实现XXXAware接口。(Aware类型的接口可以得到很多spring的组件)

如:ApplicationContextAware,BeanFactoryAware等。也不难,在实现这些接口,都会要求重写set方法,底层无非就是通过判断当前类是否实现了XXXAware,如果是,则调用刚才实现的set方法进行注入。

底层依赖ApplicationContextAwareProcessor,是BeanPostProcessor的实现类。所以对于获得spring底层组件的类基本都是利用了BeanPostProcessor,且多以XXXAware结尾。并在postProcessBeforeInitialization中进行判断是否是ApplicationContextAware类(里面还判断是否其他类,如上下文环境等),如果是,则赋值applicationContext。

对于这类方法必定会有一个setxxxxx()方法。用于注入对象。

五、AOP

JoinPoint这个参数必须写参数表的第一位,写在后面无法生效。

1.写切面记得用@Aspect。

2.在切面的通知方法中必须标注注解,并写切入点表达式。

3.开启aop注解模式@EnableAspectJAutoProxy,不然上面注解都不能生效。

源码:@EnableAspectJAutoProxy做了哪些事?

通过导入@Import(AspectJAutoProxyRegistrar.class),这个类创建一个自动代理创建器AnnotationAwareAspectJAutoProxyCreator。

可以发现最后还是实现了BeanPostProcessor接口和Aware接口。

diagram

Tags: