Spring注解开发+源码分析

文章概要

  • 起因和概述
  • 常用注解解析
    • 控制反转IOC,容器的作用
    • 依赖注入DI,获取对象的作用
    • 面象切面AOP
    • 声明式事务Tx
    • 扩展原理
  • 源码分析,分析为什么有这些注解
  • web开发 servlet3.0和SpringMvc注解

起因

从来没学过spring,也没看过源码,但是并不耽误我开发,别问我为什么 :) 我都是面向Google编程,手动狗头,so 今天来补一下sb(SpringBoot)基础知识

附带视频讲解百度云地址:

链接:https://pan.baidu.com/s/1YOPRjBl_25VnuWLnV3lrQA
提取码:ybx4
QQ群:317896269 点击链接加入群聊【数据爬取技术群】:https://jq.qq.com/?_wv=1027&k=5H2Y1Tg
群文件有很多分享的数据,需要的可以去下载,另外入群费2元,老有人来发广告,没办法了,收费。

特殊说明:虽然我不懂spring的源码和原理,但是不影响我开发,这可能就是spring做的伟大的地方。但是,我看过spring原理后开发会更高效、优质。
为什么说上边两句话,因为有这个前提,所以我学习spring原理的途径是找一个好的视频,简明扼要,快速精炼的了解一遍,然后在实践。
在看了官方文档之后,我选择了找个视频,文档说的太全了,简直浪费生命。
所以我google了这段视频,并简单整理后发到了百度云,视频来源是某培训机构的,网上随便搜的,使用开发工具eclipse,比较老旧,但是不影响观看思想,总的来说讲的很好,讲师水平很高,建议1.5倍速度播放视频观看,我用了1天半,总计大概8个小时的时间观看视频、做笔记、coding部分代码加这篇文章整理。受益良多。
加这段废话的原因是:告诉你怎么快速掌握这段知识,and,少走弯路。

感谢原视频作者,感谢spring框架,感谢google

起头

SpringBoot起源于Spring,意在简化Spring的配置和使用,Spring源于EJB,其源远流长,更古不变的两点核心,DI和IOC,即依赖注入和控制反转

先看容器Spring首先是容器。

  • 海纳百川,有容奶大,对,奶大!

容器的配置一开始是使用xml配置文件的,现在用注解更方便了。故对照着来看,如下:

配置容器 @Configuration

标记一个类为配置类,这个类等同于配置文件beans.xml

注入组件 @Bean

标记配置类中的某个方法为注入的bean对象,被标记的方法等同于beans.xml 文件中的 <bean>标签

Bean的生命周期

指定初始化和销毁方法

1. @Bean(initMethod="初始化方法名",destroyMethod="销毁方法名")
2. 让Bean实现InitializingBean接口(定义初始化逻辑),DisposableBean(定义销毁逻辑)
3. 可以使用JSR250;
 *      @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
 *      @PreDestroy:在容器销毁bean之前通知我们进行清理工作
-- 备注:JCP(Java Community Process)成立于1998年,是使有兴趣的各方参与定义Java的特征和未来版本的正式过程。
4. BeanPostProcessor【interface】:bean的后置处理器;在bean初始化前后进行一些处理工作;
 *      postProcessBeforeInitialization:在初始化之前工作
 *      postProcessAfterInitialization:在初始化之后工作

包扫描 @ComponentScans

标记在配置类上,指明要扫描的包。等同于xml配置文件中的 <context:component-scan>标签

包扫描时的过滤规则 @Filter

包扫描时@ComponentScans需要一些过滤规则,由注解@Filter标注。

组件的作用域 @Scope

用在@Bean注解的方法上,标注bean的作用域,Srping的bean默认是单例的,如果更改作用域使用这个注解

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

组件懒加载 @Lazy

用在@Bean标注的方法上,指定bean懒加载,或者说延时加载,启动的时候不加载,bean被第一次调用的时候加载。

组件按条件注入 @Conditional

注入bean时,指定一些条件@Conditional,标记在配置类或bean方法上

给容器快速导入一个组件 @Import

给容器导入组件的方式:

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

属性注入 @Value

给属性赋值,等同于xml中的 <bean>的 <property name="age" value="${}"></property>

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

加载配置文件@PropertySource

使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值@PropertySource(value={"classpath:/person.properties"})

  • 多个配置文件可以适用多个@PropertySource或者适用@PropertySources

再看自动注入

@Autowired注入

  • 默认优先按照类型去容器中找bean,applicationContext.getBean(BookDao.class),找到就赋值,找不到时再按id去容器中找applicationContext.getBean("bookDao"),还找不到就报错了。自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false)标注为找不到时注入null并不报错;

  • 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的名字

Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

  • @Autowired:Spring定义的; @Resource、@Inject都是java规范
  • @Resource:可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;没有能支持@Primary功能;没有支持@Autowired(reqiured=false);
  • @Inject:需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
  • 源码使用后置处理器实现自动注入的,工作原理看下边源码分析:AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;

自动注入注意事项

  • @Autowired:可以标注在 造器,参数,方法,属性;都是从容器中获取参数组件的值
 *      1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
 *      2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
 *      3)、放在参数位置:
  • 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
 *      自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
 *      把Spring底层一些组件注入到自定义的Bean中;
 *      xxxAware:功能使用xxxProcessor;
 *          ApplicationContextAware==》ApplicationContextAwareProcessor;

不同环境注入不同组件@Profile

指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

  • 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
  • 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  • 3)、没有标注环境标识的bean在,任何环境下都是加载的;
    切换环境的方式
  • //1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
  • //2、代码的方式激活某种环境;

AOP面象切面编程

面象切面编程:指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;底层使用动态代理实现。

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

1、导入aop模块;Spring AOP:(spring-aspects)
2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
        通知方法:
            前置通知(@Before):logStart:在目标方法(div)运行之前运行
            后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
            返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
            异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
            环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
4、给切面类的目标方法标注何时何地运行(通知注解);
5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
[7]、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
        在Spring中很多的 @EnableXXX;

AOP总结 三步:

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

AOP原理

看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?

AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
        @EnableAspectJAutoProxy;
1、@EnableAspectJAutoProxy是什么?
        @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
            利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion
            internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

        给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;

2、 AnnotationAwareAspectJAutoProxyCreator:
        AnnotationAwareAspectJAutoProxyCreator
            ->AspectJAwareAdvisorAutoProxyCreator
                ->AbstractAdvisorAutoProxyCreator
                    ->AbstractAutoProxyCreator
                            implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
                        关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;

AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()

AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
 *
 *
流程:
        1)、传入配置类,创建ioc容器
        2)、注册配置类,调用refresh()刷新容器;
        3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
            1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
            2)、给容器中加别的BeanPostProcessor
            3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
            4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
            5)、注册没实现优先级接口的BeanPostProcessor;
            6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
                创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
                1)、创建Bean的实例
                2)、populateBean;给bean的各种属性赋值
                3)、initializeBean:初始化bean;
                        1)、invokeAwareMethods():处理Aware接口的方法回调
                        2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
                        3)、invokeInitMethods();执行自定义的初始化方法
                        4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
                4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
            7)、把BeanPostProcessor注册到BeanFactory中;
                beanFactory.addBeanPostProcessor(postProcessor);
=======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程========

            AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
        4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
            1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
                getBean->doGetBean()->getSingleton()->
            2)、创建bean
                【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
                1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
                    只要创建好的Bean都会被缓存起来
                2)、createBean();创建bean;
                    AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
                    【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
                    【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
                    1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
                        希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
                        1)、后置处理器先尝试返回对象;
                            bean = applyBeanPostProcessorsBeforeInstantiation():
                                拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
                                就执行postProcessBeforeInstantiation
                            if (bean != null) {
                                bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                            }

                    2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
                    3)、
            
        
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
        关心MathCalculator和LogAspect的创建
        1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
        2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
            或者是否是切面(@Aspect)
        3)、是否需要跳过
            1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
                每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
                判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
            2)、永远返回false

2)、创建对象
postProcessAfterInitialization;
        return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
        1)、获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
            1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
            2、获取到能在bean使用的增强器。
            3、给增强器排序
        2)、保存当前bean在advisedBeans中;
        3)、如果当前bean需要增强,创建当前bean的代理对象;
            1)、获取所有增强器(通知方法)
            2)、保存到proxyFactory
            3)、创建代理对象:Spring自动决定
                JdkDynamicAopProxy(config);jdk动态代理;
                ObjenesisCglibAopProxy(config);cglib的动态代理;
        4)、给容器中返回当前组件使用cglib增强了的代理对象;
        5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
        
    
    3)、目标方法执行   ;
        容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
        1)、CglibAopProxy.intercept();拦截目标方法的执行
        2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            1)、List<Object> interceptorList保存所有拦截器 5
                一个默认的ExposeInvocationInterceptor 和 4个增强器;
            2)、遍历所有的增强器,将其转为Interceptor;
                registry.getInterceptors(advisor);
            3)、将增强器转为List<MethodInterceptor>;
                如果是MethodInterceptor,直接加入到集合中
                如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
                转换完成返回MethodInterceptor数组;

        3)、如果没有拦截器链,直接执行目标方法;
            拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
        4)、如果有拦截器链,把需要执行的目标对象,目标方法,
            拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
            并调用 Object retVal =  mi.proceed();
        5)、拦截器链的触发过程;
            1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
            2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
                拦截器链的机制,保证通知方法与目标方法的执行顺序;
        
    总结:
        1)、  @EnableAspectJAutoProxy 开启AOP功能
        2)、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
        3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
        4)、容器的创建流程:
            1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
            2)、finishBeanFactoryInitialization()初始化剩下的单实例bean
                1)、创建业务逻辑组件和切面组件
                2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
                3)、组件创建完之后,判断组件是否需要增强
                    是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
        5)、执行目标方法:
            1)、代理对象执行目标方法
            2)、CglibAopProxy.intercept();
                1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
                2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
                3)、效果:
                    正常执行:前置通知-》目标方法-》后置通知-》返回通知
                    出现异常:前置通知-》目标方法-》后置通知-》异常通知

声明式事务

@EnableTransactionManagement 启用事务

@Transactional 声明式事务

Spring源码分析

启动流程分析

  • 创建BeanFactory的预处理工作
  • 创建BeanFactory
  • 执行BeanFactoryPostProcessor后续处理工作
  • 注册Bean
  • 执行BeanPostProcessor后续处理工作
  • 初始化MessageSource消息绑定
  • 初始化initApplicationEventMulticaster事件派发器
  • 留给子容器自定义处理onRefresh
  • 注册监听器registerListeners
  • 初始化剩下的单例组件 finishBeanFactoryInitialization
  • ioc容器完成finishRefresh
    源码大体如下:
// Prepare this context for refreshing.
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();               

详细代码debug记录如下:

Spring容器的refresh()【创建刷新】;
1、prepareRefresh()刷新前的预处理;
    1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;
    2)、getEnvironment().validateRequiredProperties();检验属性的合法等
    3)、earlyApplicationEvents= new LinkedHashSet<ApplicationEvent>();保存容器中的一些早期的事件;
2、obtainFreshBeanFactory();获取BeanFactory;
    1)、refreshBeanFactory();刷新【创建】BeanFactory;
            创建了一个this.beanFactory = new DefaultListableBeanFactory();
            设置id;
    2)、getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;
    3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;
3、prepareBeanFactory(beanFactory);BeanFactory的预准备工作(BeanFactory进行一些设置);
    1)、设置BeanFactory的类加载器、支持表达式解析器...
    2)、添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
    3)、设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;
    4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:
            BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
    5)、添加BeanPostProcessor【ApplicationListenerDetector】
    6)、添加编译时的AspectJ;
    7)、给BeanFactory中注册一些能用的组件;
        environment【ConfigurableEnvironment】、
        systemProperties【Map<String, Object>】、
        systemEnvironment【Map<String, Object>】
4、postProcessBeanFactory(beanFactory);BeanFactory准备工作完成后进行的后置处理工作;
    1)、子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
======================以上是BeanFactory的创建及预准备工作==================================
5、invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor的方法;
    BeanFactoryPostProcessor:BeanFactory的后置处理器。在BeanFactory标准初始化之后执行的;
    两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
    1)、执行BeanFactoryPostProcessor的方法;
        先执行BeanDefinitionRegistryPostProcessor
        1)、获取所有的BeanDefinitionRegistryPostProcessor;
        2)、看先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor、
            postProcessor.postProcessBeanDefinitionRegistry(registry)
        3)、在执行实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor;
            postProcessor.postProcessBeanDefinitionRegistry(registry)
        4)、最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors;
            postProcessor.postProcessBeanDefinitionRegistry(registry)
            
        
        再执行BeanFactoryPostProcessor的方法
        1)、获取所有的BeanFactoryPostProcessor
        2)、看先执行实现了PriorityOrdered优先级接口的BeanFactoryPostProcessor、
            postProcessor.postProcessBeanFactory()
        3)、在执行实现了Ordered顺序接口的BeanFactoryPostProcessor;
            postProcessor.postProcessBeanFactory()
        4)、最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostProcessor;
            postProcessor.postProcessBeanFactory()
6、registerBeanPostProcessors(beanFactory);注册BeanPostProcessor(Bean的后置处理器)【 intercept bean creation】
        不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的
        BeanPostProcessor、
        DestructionAwareBeanPostProcessor、
        InstantiationAwareBeanPostProcessor、
        SmartInstantiationAwareBeanPostProcessor、
        MergedBeanDefinitionPostProcessor【internalPostProcessors】、
        
        1)、获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
        2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;
            把每一个BeanPostProcessor;添加到BeanFactory中
            beanFactory.addBeanPostProcessor(postProcessor);
        3)、再注册Ordered接口的
        4)、最后注册没有实现任何优先级接口的
        5)、最终注册MergedBeanDefinitionPostProcessor;
        6)、注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是
            applicationContext.addApplicationListener((ApplicationListener<?>) bean);
7、initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
        1)、获取BeanFactory
        2)、看容器中是否有id为messageSource的,类型是MessageSource的组件
            如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
                MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
        3)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;
            beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);    
            MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);
8、initApplicationEventMulticaster();初始化事件派发器;
        1)、获取BeanFactory
        2)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
        3)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
        4)、将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
9、onRefresh();留给子容器(子类)
        1、子类重写这个方法,在容器刷新的时候可以自定义逻辑;
10、registerListeners();给容器中将所有项目里面的ApplicationListener注册进来;
        1、从容器中拿到所有的ApplicationListener
        2、将每个监听器添加到事件派发器中;
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        3、派发之前步骤产生的事件;
11、finishBeanFactoryInitialization(beanFactory);初始化所有剩下的单实例bean;
    1、beanFactory.preInstantiateSingletons();初始化后剩下的单实例bean
        1)、获取容器中的所有Bean,依次进行初始化和创建对象
        2)、获取Bean的定义信息;RootBeanDefinition
        3)、Bean不是抽象的,是单实例的,是懒加载;
            1)、判断是否是FactoryBean;是否是实现FactoryBean接口的Bean;
            2)、不是工厂Bean。利用getBean(beanName);创建对象
                0、getBean(beanName); ioc.getBean();
                1、doGetBean(name, null, null, false);
                2、先获取缓存中保存的单实例Bean。如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来)
                    从private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);获取的
                3、缓存中获取不到,开始Bean的创建对象流程;
                4、标记当前bean已经被创建
                5、获取Bean的定义信息;
                6、【获取当前Bean依赖的其他Bean;如果有按照getBean()把依赖的Bean先创建出来;】
                7、启动单实例Bean的创建流程;
                    1)、createBean(beanName, mbd, args);
                    2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);让BeanPostProcessor先拦截返回代理对象;
                        【InstantiationAwareBeanPostProcessor】:提前执行;
                        先触发:postProcessBeforeInstantiation();
                        如果有返回值:触发postProcessAfterInitialization();
                    3)、如果前面的InstantiationAwareBeanPostProcessor没有返回代理对象;调用4)
                    4)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);创建Bean
                         1)、【创建Bean实例】;createBeanInstance(beanName, mbd, args);
                            利用工厂方法或者对象的构造器创建出Bean实例;
                         2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                            调用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName);
                         3)、【Bean属性赋值】populateBean(beanName, mbd, instanceWrapper);
                            赋值之前:
                            1)、拿到InstantiationAwareBeanPostProcessor后置处理器;
                                postProcessAfterInstantiation();
                            2)、拿到InstantiationAwareBeanPostProcessor后置处理器;
                                postProcessPropertyValues();
                            =====赋值之前:===
                            3)、应用Bean属性的值;为属性利用setter方法等进行赋值;
                                applyPropertyValues(beanName, mbd, bw, pvs);
                         4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd);
                            1)、【执行Aware接口方法】invokeAwareMethods(beanName, bean);执行xxxAware接口的方法
                                BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
                            2)、【执行后置处理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
                                BeanPostProcessor.postProcessBeforeInitialization();
                            3)、【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
                                1)、是否是InitializingBean接口的实现;执行接口规定的初始化;
                                2)、是否自定义初始化方法;
                            4)、【执行后置处理器初始化之后】applyBeanPostProcessorsAfterInitialization
                                BeanPostProcessor.postProcessAfterInitialization();
                         5)、注册Bean的销毁方法;
                    5)、将创建的Bean添加到缓存中singletonObjects;
                ioc容器就是这些Map;很多的Map里面保存了单实例Bean,环境信息。。。。;
        所有Bean都利用getBean创建完成以后;
            检查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就执行afterSingletonsInstantiated();
12、finishRefresh();完成BeanFactory的初始化创建工作;IOC容器就创建完成;
        1)、initLifecycleProcessor();初始化和生命周期有关的后置处理器;LifecycleProcessor
            默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();
            加入到容器;
            
            写一个LifecycleProcessor的实现类,可以在BeanFactory
                void onRefresh();
                void onClose(); 
        2)、 getLifecycleProcessor().onRefresh();
            拿到前面定义的生命周期处理器(BeanFactory);回调onRefresh();
        3)、publishEvent(new ContextRefreshedEvent(this));发布容器刷新完成事件;
        4)、liveBeansView.registerApplicationContext(this);
    

简单总结

    1)、Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;
        1)、xml注册bean;<bean>
        2)、注解注册Bean;@Service、@Component、@Bean、xxx
    2)、Spring容器会合适的时机创建这些Bean
        1)、用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
        2)、统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();
    3)、后置处理器;BeanPostProcessor
        1)、每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
            AutowiredAnnotationBeanPostProcessor:处理自动注入
            AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
            xxx....
            增强的功能注解:
            AsyncAnnotationBeanPostProcessor
            ....
    4)、事件驱动模型;
        ApplicationListener;事件监听;
        ApplicationEventMulticaster;事件派发:
            

Web开发

servlet3.0注解和插件

注解

  • 注册servlet @WebServlet("/路由")
  • 注册filter @WebFilter

Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

1、Servlet容器启动会扫描,当前应用里面每一个jar包的
ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;
必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的内容就是ServletContainerInitializer实现类的全类名;

总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;

ServletContainerInitializer;
@HandlesTypes;

我没关注过servlet3标准,这地方看看就算了,以后用到在Google,做个记录,粘贴一个例子


//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * 应用启动的时候,会运行onStartup方法;
     * 
     * Set<Class<?>> arg0:感兴趣的类型的所有子类型;
     * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
     * 
     * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
     * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
     *      必须在项目启动的时候来添加;
     *      1)、ServletContainerInitializer得到的ServletContext;
     *      2)、ServletContextListener得到的ServletContext;
     */
    @Override
    public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
        // TODO Auto-generated method stub
        System.out.println("感兴趣的类型:");
        for (Class<?> claz : arg0) {
            System.out.println(claz);
        }
        
        //注册组件  ServletRegistration  
        ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
        //配置servlet的映射信息
        servlet.addMapping("/user");
        
        
        //注册Listener
        sc.addListener(UserListener.class);
        
        //注册Filter  FilterRegistration
        FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
        //配置Filter的映射信息
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        
    }

SpringMVC注解

  • 1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
  • 2、加载这个文件指定的类SpringServletContainerInitializer
  • 3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
  • 4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
    • 1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
    • 2)、AbstractDispatcherServletInitializer:
      创建一个web的ioc容器;createServletApplicationContext();
      创建了DispatcherServlet;createDispatcherServlet();
      将创建的DispatcherServlet添加到ServletContext中;
      getServletMappings();
    • 3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
      创建根容器:createRootApplicationContext()
      getRootConfigClasses();传入一个配置类
      创建web的ioc容器: createServletApplicationContext();
      获取配置类;getServletConfigClasses();

总结:

以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;

定制SpringMVC;

1)、@EnableWebMvc:开启SpringMVC定制配置功能;相当于原来的xml配置<mvc:annotation-driven/>;

2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
extends WebMvcConfigurerAdapter

异步支持

servlet3.0 异步请求支持

接收到请求后,启动新的线程处理业务,立即返回响应。
servlet 的doGet方法类似如下代码:

@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、支持异步处理asyncSupported=true
        //2、开启异步模式
        System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
        AsyncContext startAsync = req.startAsync();
        
        //3、业务逻辑进行异步处理;开始异步处理
        startAsync.start(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                    sayHello();
                    startAsync.complete();
                    //获取到异步上下文
                    AsyncContext asyncContext = req.getAsyncContext();
                    //4、获取响应
                    ServletResponse response = asyncContext.getResponse();
                    response.getWriter().write("hello async...");
                    System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                } catch (Exception e) {
                }
            }
        });     
        System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
    }

    public void sayHello() throws Exception{
        System.out.println(Thread.currentThread()+" processing...");
        Thread.sleep(3000);
    }
}

SpringMVC异步处理

/**
     * 1、控制器返回Callable
     * 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
     * 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
     * 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
     * 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
     * 
     * preHandle.../springmvc-annotation/async01
        主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
        主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
        =========DispatcherServlet及所有的Filter退出线程============================
        
        ================等待Callable执行==========
        副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
        副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
        ================Callable执行完成==========
        
        ================再次收到之前重发过来的请求========
        preHandle.../springmvc-annotation/async01
        postHandle...(Callable的之前的返回值就是目标方法的返回值)
        afterCompletion...
        
        异步的拦截器:
            1)、原生API的AsyncListener
            2)、SpringMVC:实现AsyncHandlerInterceptor;
     * @return
     */
    @ResponseBody
    @RequestMapping("/async01")
    public Callable<String> async01(){
        System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
        
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
                return "Callable<String> async01()";
            }
        };
        
        System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
        return callable;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容