精华内容
下载资源
问答
  • spring源码分析专题,源码分析视频,对spring的源码进行分析
  • Spring源码分析第二阶段,详细源码分析,有思路跟着主线走,妥妥的看懂源码
  • spring 源码分析

    2018-11-05 23:48:03
    spring 源码分析
  • NULL 博文链接:https://ylxy3058.iteye.com/blog/2224244
  • Spring源码分析.txt

    2019-07-25 17:32:11
    spring源码分析,百度云视频。链接如果失效了,请私聊我。
  • Spring源码分析

    2018-11-01 23:34:52
    Spring源码分析
  • spring源码分析

    千次阅读 多人点赞 2018-08-17 02:51:18
    1、spring ioc源码分析   Spring IOC中bean的创建是利用反射+xml解析实现的,因此这里也围绕这个来做分析。 ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("...

    1、spring ioc源码分析

     

    Spring IOC中bean的创建是利用反射+xml解析实现的,因此这里也围绕这个来做分析。

    ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

    Spring所有的工作由这一句代码开始,接下就去剖析这个类ClassPathXmlApplicationContext。

    1.1 BeanFactory

    由上图的继承关系可以看到,所有的类都包含这个接口(BeanFactory)。从下面的源码 可以看到BeanFactory定义了 IOC 容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是Spring IOC 所遵守的最底层和最基本的编程规范。在  Spring 代码中, BeanFactory 只是个接口,并不是 IOC容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory  XmlBeanFactory ApplicationContext 等,都是附加了某种功能的实现。

    public interface BeanFactory {
    	//这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,  
      //如果需要得到工厂本身,需要转义    
        //转义符“&”用来获取FactoryBean本身
    	String FACTORY_BEAN_PREFIX = "&";
    //根据bean的名字进行获取bean的实例,这是IOC最大的抽象方法
    	Object getBean(String name) throws BeansException;
    //根据bean的名字和Class类型进行获取Bean的实例,和上面方法不同的是,bean名字和Bean 的class类型不同时候会爆出异常
    	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
    	<T> T getBean(Class<T> requiredType) throws BeansException;
    	Object getBean(String name, Object... args) throws BeansException;
    //检测这个IOC容器中是否含有这个Bean
    boolean containsBean(String name);
    //判断这个Bean是不是单利
    
    	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    //判断这个Bean是不是原型
    	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    //查询指定的bean的名字和Class类型是不是指定的Class类型
    	boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
    //这里对得到bean实例的Class类型
    	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 
    	String[] getAliases(String name);
    }
    

    1.2 BeanDefinition

    从这个接口的函数名不难看出,这个接口是对应xml中bean的解析的。这里就不过多解释这些函数的意义了。

    有一个类BeanDefinitionHolder,根据名称或者别名持有beanDefinition,它承载了beanNameBeanDefinition的映射信息。

    BeanWrapper:提供对Javabean的分析和操作方法,单个或者批量获取和设置属性值,获取属性描述符,查询属性的可读性和可写性等。支持属性的嵌套设置,深度没有限制。

    1.3 源码分析IOC中的反射

    AbstractRefreshableApplicationContext:

    接下进入loadBeanDefinition的实现:

    进入loadBeanDefinitions(..)。

    继续进入reader.loadBeanDefinitions(..)的实现:

    继续loadDefinitions的实现直到到达下面这个实现:

    这里直接进入解析单个bean的实现

    都是加载bean的定义的,此时选择xml形式的实现,直接找到如下实现:

    进入真正加载的这个方法。

    进入bean的注册,

    继续点击有关register这个单词的,直到下面这个类,很明显这个类就是遍历这个xml节点

    点击默认的,进入判断解析类型。

    进入bean类型解析:

    接下来,我们直接看他怎么解析的

    看看怎么创建的?

    2 Spring获取bean对象

    进入一看这个类,原来就是包含一些删除,添加(注册)等等的操作的类嘛,说白了就是维护一个hashMap,然后查询获取bean咯。

    3  bean生命周期

    1、Spring对bean进行实例化(相当于new)

    2、Spring将值和bean的引用注入到Bean对应的属性中

    3、如果Bean实现了BeanNameAware接口,Spring将Bean的id传递给setBeanName()方法(实现BeanNameAware接口主要是为了通过Bean的引用来获得Bean的id,一般业务中是很少用到Bean的id的)

    4、如果实现了BeanFactoryAware接口,Spring将调用setBeanFactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware主要目的是为了获取Spring容器,如bean通过Spring容器发布事件等)

    5、如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext()方法,把Bean所在的应用上下文的引用传入。(作用和BeanFactory类似为获取Spring容器,不同的是Spring容器在调用setApplicationContext()时会把自己作为参数传入。而Spring容器在调用setBeanFactory前需要程序员自己指定(注入)BeanFactory里的参数)

    6、如果Bean实现了BeanPostProcessor接口,Spring将调用他们的postProcessBeforeInitialization(预初始化)方法。(作用是在Bean实例化创建成功后对其进行增强处理,比如对bean进行修改,增加功能等)

    7、如果bean实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet方法,作用与配置文件中对bean使用init-method声明初始化的作用一样,都是在bean的全部属性设置成功后执行的初始化方法。

    8、如果bean实现了BeanPostProcess接口,Spring将调用他们的postProcessAfterInitialization方法(后初始化),在bean初始化之后执行。

    9、此时bean应准备就绪,可以本程序使用,bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁。

    10、如果bean实现了DispostbleBean接口,Spring将调用他的destroy方法,作用与在配置文件中对bean调用destroy-method属性的作用一样,都是在bean实例化销毁之前执行。

    @Getter
    public class User implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,BeanPostProcessor,InitializingBean,DisposableBean {
        private String name;
        private Integer age;
    
        public void setName(String name) {
            System.out.println("2、填充属性");
            this.name = name;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public User() {
            System.out.println("1、实例化");
        }
    
    
        @Override
        public void setBeanName(String s) {
            System.out.println("3、BeanNameAware的setBeanName:name="+s);
        }
    
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.out.println("4、BeanFactoryAware的setBeanFactory。。");
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("5、ApplicationContextAware的setApplication...");
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
            System.out.println("6、bean初始化之前执行,beanname="+s);
            return o;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
            System.out.println("8、bean初始化之后执行,beanname="+s);
            return o;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("7、属性设置成功后执行的初始化方法");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("9、销毁bean方法");
        }
    }
        <bean id="user" class="com.spring4.model.User" scope="prototype">
            <property name="name" value="xiaoming"/>
        </bean>

    Scope为原型,在容器初始化定义的bean创建之前,容器会自己去查找所有的beanPostProcessor进行创建,自定义的类,由于是实现了BeanPostProcessor接口,因此这时候会进行BeanPostProcessor的创建和注册,源码中,在注册BeanPostProcessor会进行getBean操作,即创建自定义的bean。由于默认的是单例模式,因此后面再次进行获取就不会再次调用postProcessBeforeInitialization()和postProcessAfterInitialization()方法,因为已经放入了spring缓存,直接获取,不需要实例,因此没有调用。如果你真的想使用的时候调用postProcessBeforeInitialization()和postProcessAfterInitialization()方法,将你的bean设置为原型模式(prototype),这样每次调用都会创建,因此初始化容器之后每次都会调用的。

    4 AOP源码分析

    AOP的好处:1、降低模块之间的耦合度2、更好的代码复用3、更容易扩展

    流程说明

    1)AOP标签的定义解析是从NamespaceHandlerSupport的实现类开始解析的,这个实现类就是AopNamespaceHandler。至于为什么会是从NamespaceHandlerSupport的实现类开始解析的,这个的话我想读者可以去在回去看看Spring自定义标签的解析流程,里面说的比较详细。

    2)要启用AOP,我们一般会在Spring里面配置<aop:aspectj-autoproxy/>  ,所以在配置文件中在遇到aspectj-autoproxy标签的时候我们会采用AspectJAutoProxyBeanDefinitionParser解析器

    3)进入AspectJAutoProxyBeanDefinitionParser解析器后,调用AspectJAutoProxyBeanDefinitionParser已覆盖BeanDefinitionParser的parser方法,然后parser方法把请求转交给了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去处理

    4)进入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法后,先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,里面在转发调用给registerOrEscalateApcAsRequired,注册或者升级AnnotationAwareAspectJAutoProxyCreator类。对于AOP的实现,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根据@point注解定义的切点来代理相匹配的bean。

    5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法处理完成之后,接下来会调用useClassProxyingIfNecessary() 处理proxy-target-class以及expose-proxy属性。如果将proxy-target-class设置为true的话,那么会强制使用CGLIB代理,否则使用jdk动态代理,expose-proxy属性是为了解决有时候目标对象内部的自我调用无法实现切面增强。

    6)最后的调用registerComponentIfNecessary 方法,注册组建并且通知便于监听器做进一步处理。

    创建AOP代理流程

    1、Spring容器启动,每个bean的实例化之前都会先经过AbstractAutoProxyCreator类的postProcessAfterInitialization这个方法,然后接下来是wrapIfNecessary

    5 SpringMVC执行流程与源码分析

    SpringMVC其实底层也是封装HttpServlet实现的,我们知道HTTPServlet的流程是,每个请求到达,调用该Servlet的service方法。因此SpringMVC也是类似的呀,接下来走起。

    1、所有的MVC请求都交给DispatcherServlet这个类处理,然后调用doService方法,交给doDispatch方法

    最后通过视图解析器解析渲染后返回给客户端。

    描述:

    1、用户向服务器发送请求,请求被Spring前端控制Servlet DispatcherServlet捕获

    2、DispatcherServlet对请求URL进行解析,得到请求资源标识符,然后根据URI调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回。

    3、DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。

    4、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。

    在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

          HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

          数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

          数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

          数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

    5、Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象

    6、根据返回的ModelAndView选择一个合适的ViewResolver返回给DispatcherServlet

    7、ViewResolver结合Model和View,来渲染视图

    8、将渲染结果返回给客户端

     

      为什么Spring只使用一个Servlet(DispatcherServlet)来处理所有请求?

    用于集中统一化对外的请求接口,便于更好的封装内部逻辑。

    Spring为什么要结合使用HandlerMapping以及HandlerAdapter来处理Handler?

        符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。如HandlerAdapter可能会被用于处理多种Handler。

    展开全文
  • Spring 源码分析(Bean的初始化) 前言 本篇文章是个人第一次看spring源码并总结,同时也参考了下面这篇博客。基本也是按照他的思路来理解的。这也算是第一版个人简易理解。也算是窥见spring的冰山一角,之后也会...
  • spring源码分析(一)

    2017-08-28 18:29:35
    spring源码分析(一)
  • 这是一篇总结Spring5源码深度解析的文档,By 某Tom老师,很经典,讲解深入浅出!
  • Spring源码分析解读

    万次阅读 多人点赞 2018-11-14 07:12:19
    Spring源码下载https://github.com/spring-projects/spring-framework/tags?after=v3.1.0.RC1 源代码结构组织 Build-spring-framework是整个Spring源代码的构建目录,里面是项目的构建脚本,如果要自己动手构建...

     

    如何查看源码

    Spring源码下载https://github.com/spring-projects/spring-framework/tags?after=v3.1.0.RC1

    源代码结构组织

    Build-spring-framework是整个Spring源代码的构建目录,里面是项目的构建脚本,如果要自己动手构建Spring,可以进入这个目录使用ANT进行构建。

    l  org.springframework.context是IoC容器的源代码目录

    l  org.springframework.aop是AOP实现的源代码目录

    l  org.springframework.jdbc是JDBC的源代码部分

    l  org.springframework.orm是O/R Mapping对应的源代码实现部分

     

     

    SpringIOC源码分析

    IOC初始化

    1、 XmlBeanFactory(屌丝IOC)的整个流程

    2、 FileSystemXmlApplicationContext 的IOC容器流程

    1、高富帅IOC解剖

    2、 设置资源加载器和资源定位

    3、AbstractApplicationContext的refresh函数载入Bean定义过程:

    4、AbstractApplicationContext子类的refreshBeanFactory()方法:

    5、AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:

    6、AbstractBeanDefinitionReader读取Bean定义资源:

    7、资源加载器获取要读入的资源:

    8、XmlBeanDefinitionReader加载Bean定义资源:

    9、DocumentLoader将Bean定义资源转换为Document对象:

    10、XmlBeanDefinitionReader解析载入的Bean定义资源文件:

    11、DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析:

    12、BeanDefinitionParserDelegate解析Bean定义资源文件中的<Bean>元素:

    13、BeanDefinitionParserDelegate解析<property>元素:

    14、解析<property>元素的子元素:

    15、解析<list>子元素:

    16、解析过后的BeanDefinition在IoC容器中的注册:

    17、DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition:

    IOC体系

    BeanFactory

             Spring Bean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:

     

     

    BeanFactory 

     BeanFactory定义了 IOC 容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是Spring IOC 所遵守的最底层和最基本的编程规范。在  Spring 代码中, BeanFactory 只是个接口,并不是 IOC容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory  XmlBeanFactory ApplicationContext 等,都是附加了某种功能的实现。

    public interface BeanFactory {
    
    //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,  
    
      //如果需要得到工厂本身,需要转义    
    
        //转义符“&”用来获取FactoryBean本身
    
    String FACTORY_BEAN_PREFIX = "&";
    
    //根据bean的名字进行获取bean的实例,这是IOC最大的抽象方法
    
    Object getBean(String name) throws BeansException;
    
    //根据bean的名字和Class类型进行获取Bean的实例,和上面方法不同的是,bean名字和Bean 的class类型不同时候会爆出异常
    
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
    <T> T getBean(Class<T> requiredType) throws BeansException;
    
    Object getBean(String name, Object... args) throws BeansException;
    
    //检测这个IOC容器中是否含有这个Bean
    
    boolean containsBean(String name);
    
    //判断这个Bean是不是单利
    
    
    
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    //判断这个Bean是不是原型
    
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    //查询指定的bean的名字和Class类型是不是指定的Class类型
    
    boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
    
    //这里对得到bean实例的Class类型
    
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 
    
    String[] getAliases(String name);
    
    }

     

    BeanDefinition 

    这个接口,可以理解为xml bean元素的数据载体。通过对比xml bean标签的属性列表和BeanDefinition的属性列表一看便知。

    我的理解,是解析XML的过程,就是 xml <bean>元素内容 转换为BeanDefinition对象的过程。而且这个接口,支持层级,对应对象的继承。

    有一个类BeanDefinitionHolder,BeanDefinitionHolder,根据名称或者别名持有beanDefinition,它承载了name和BeanDefinition的映射信息。

    BeanWarpper:

    提供对标准javabean的分析和操作方法:单个或者批量获取和设置属性值,获取属性描述符,查询属性的可读性和可写性等。支持属性的嵌套设置,深度没有限制。

    AbstractRefreshableApplicationContext的refreshBeanFactory()这个方法

    protected final void refreshBeanFactory() throws BeansException {
    
    if (hasBeanFactory()) {
    
    destroyBeans();
    
    closeBeanFactory();
    
    }
    
    try {
    
    DefaultListableBeanFactory beanFactory = createBeanFactory();//创建IOC容器
    
    beanFactory.setSerializationId(getId());
    
    customizeBeanFactory(beanFactory);
    
    loadBeanDefinitions(beanFactory);//载入loadBeanDefinitions
    
    synchronized (this.beanFactoryMonitor) {
    
    this.beanFactory = beanFactory;
    
    }
    
    }
    
    catch (IOException ex) {
    
    throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    
    }
    
    }
    
    public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  实现
    
    /**
    
     * Loads the bean definitions via an XmlBeanDefinitionReader.
    
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
    
     * @see #initBeanDefinitionReader
    
     * @see #loadBeanDefinitions
    
     */
    
    @Override
    
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    
    
    // Configure the bean definition reader with this context's
    
    // resource loading environment.
    
    beanDefinitionReader.setResourceLoader(this);
    
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    
    
    // Allow a subclass to provide custom initialization of the reader,
    
    // then proceed with actually loading the bean definitions.
    
    initBeanDefinitionReader(beanDefinitionReader);
    
    loadBeanDefinitions(beanDefinitionReader);
    
    }
    
    先调用本类里面的loadBeanDefinitions
    
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    
    Resource[] configResources = getConfigResources();
    
    if (configResources != null) {
    
    reader.loadBeanDefinitions(configResources);
    
    }
    
    String[] configLocations = getConfigLocations();
    
    if (configLocations != null) {
    
    reader.loadBeanDefinitions(configLocations);
    
    }
    
    }
    
    
    
    委托给reader.loadBeanDefinitions(configLocation);    XmlBeanDefinitionReader
    通过XmlBeanDefinitionReader来读取。下面看一下XmlBeanDefinitionReader这个方法,但其实并不在这个类实现这个方法,而是在它的基类里面AbstractBeanDefinitionReader
    
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    
    Assert.notNull(locations, "Location array must not be null");
    
    int counter = 0;
    
    for (String location : locations) {
    
    counter += loadBeanDefinitions(location);
    
    }
    
    return counter;
    
    }
    
    进入到loadBeanDefinitions
    
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    
    if (logger.isInfoEnabled()) {
    
    logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    
    }
    
    
    
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    
    if (currentResources == null) {
    
    currentResources = new HashSet<EncodedResource>(4);
    
    this.resourcesCurrentlyBeingLoaded.set(currentResources);
    
    }
    
    if (!currentResources.add(encodedResource)) {
    
    throw new BeanDefinitionStoreException(
    
    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    
    }
    
    try {
    
    InputStream inputStream = encodedResource.getResource().getInputStream();//获取IO
    
    try {
    
    InputSource inputSource = new InputSource(inputStream);
    
    if (encodedResource.getEncoding() != null) {
    
    inputSource.setEncoding(encodedResource.getEncoding());
    
    }
    
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//这个方法从流中读取
    
    }
    
    finally {
    
    inputStream.close();
    
    }
    
    }
    
    catch (IOException ex) {
    
    throw new BeanDefinitionStoreException(
    
    "IOException parsing XML document from " + encodedResource.getResource(), ex);
    
    }
    
    finally {
    
    currentResources.remove(encodedResource);
    
    if (currentResources.isEmpty()) {
    
    this.resourcesCurrentlyBeingLoaded.remove();
    
    }
    
    }
    
    }
    
    进入到doLoadBeanDefinitions  Resource IO封装
    
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    
    throws BeanDefinitionStoreException {
    
    try {
    
    int validationMode = getValidationModeForResource(resource);
    
    Document doc = this.documentLoader.loadDocument(
    
    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
    
    return registerBeanDefinitions(doc, resource); //解析XML
    
    }
    
    catch (BeanDefinitionStoreException ex) {
    
    throw ex;
    
    }
    
    catch (SAXParseException ex) {
    
    throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    
    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    
    }
    
    catch (SAXException ex) {
    
    throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    
    "XML document from " + resource + " is invalid", ex);
    
    }
    
    catch (ParserConfigurationException ex) {
    
    throw new BeanDefinitionStoreException(resource.getDescription(),
    
    "Parser configuration exception parsing XML from " + resource, ex);
    
    }
    
    catch (IOException ex) {
    
    throw new BeanDefinitionStoreException(resource.getDescription(),
    
    "IOException parsing XML document from " + resource, ex);
    
    }
    
    catch (Throwable ex) {
    
    throw new BeanDefinitionStoreException(resource.getDescription(),
    
    "Unexpected exception parsing XML document from " + resource, ex);
    
    }
    
    }
    
    进入到registerBeanDefinitions
    
    /**
    
     * Register the bean definitions contained in the given DOM document.
    
     * Called by <code>loadBeanDefinitions</code>.
    
     * <p>Creates a new instance of the parser class and invokes
    
     * <code>registerBeanDefinitions</code> on it.
    
     * @param doc the DOM document
    
     * @param resource the resource descriptor (for context information)
    
     * @return the number of bean definitions found
    
     * @throws BeanDefinitionStoreException in case of parsing errors
    
     * @see #loadBeanDefinitions
    
     * @see #setDocumentReaderClass
    
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
    
     */
    
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    
    // Read document based on new BeanDefinitionDocumentReader SPI.
    
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    
    int countBefore = getRegistry().getBeanDefinitionCount();
    
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    
    return getRegistry().getBeanDefinitionCount() - countBefore;
    
    }
    
    documentReader.registerBeanDefinitionsXML解析
    
    /**
    
     * Parses bean definitions according to the "spring-beans" DTD.
    
     * <p>Opens a DOM Document; then initializes the default settings
    
     * specified at <code><beans></code> level; then parses
    
     * the contained bean definitions.
    
     */
    
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    
    this.readerContext = readerContext;
    
    
    
    logger.debug("Loading bean definitions");
    
    Element root = doc.getDocumentElement();
    
    
    
    BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
    
    
    
    preProcessXml(root);
    
    parseBeanDefinitions(root, delegate);
    
    postProcessXml(root);
    
    }
    
    -----遍历节点
    
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    
    if (delegate.isDefaultNamespace(root)) {
    
    NodeList nl = root.getChildNodes();
    
    for (int i = 0; i < nl.getLength(); i++) {
    
    Node node = nl.item(i);
    
    if (node instanceof Element) {
    
    Element ele = (Element) node;
    
    if (delegate.isDefaultNamespace(ele)) {
    
    parseDefaultElement(ele, delegate); //默认解析
    
    }
    
    else {
    
    delegate.parseCustomElement(ele);
    
    }
    
    }
    
    }
    
    }
    
    else {
    
    delegate.parseCustomElement(root);
    
    }
    
    }
    
    ---判断解析类
    
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    
    importBeanDefinitionResource(ele);//import类型
    
    }
    
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    
    processAliasRegistration(ele);//别名方式
    
    }
    
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    
    processBeanDefinition(ele, delegate);//bean解析方式
    
    }
    
    }

     

    Bean的解析方式

    进入到 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);   使用反射初始化类

    public AbstractBeanDefinition parseBeanDefinitionElement(
    
    Element ele, String beanName, BeanDefinition containingBean) {
    
    
    
    this.parseState.push(new BeanEntry(beanName));
    
    
    
    String className = null;
    
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    
    className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    
    }
    
    
    
    try {
    
    String parent = null;
    
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    
    parent = ele.getAttribute(PARENT_ATTRIBUTE);
    
    }
    
    AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
    
    
    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    
    bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
    
    
    parseMetaElements(ele, bd);
    
    parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    
    parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
    
    
    parseConstructorArgElements(ele, bd);
    
    parsePropertyElements(ele, bd);
    
    parseQualifierElements(ele, bd);
    
    
    
    bd.setResource(this.readerContext.getResource());
    
    bd.setSource(extractSource(ele));
    
    
    
    return bd;
    
    }
    
    catch (ClassNotFoundException ex) {
    
    error("Bean class [" + className + "] not found", ele, ex);
    
    }
    
    catch (NoClassDefFoundError err) {
    
    error("Class that bean class [" + className + "] depends on not found", ele, err);
    
    }
    
    catch (Throwable ex) {
    
    error("Unexpected failure during bean definition parsing", ele, ex);
    
    }
    
    finally {
    
    this.parseState.pop();
    
    }
    
    
    
    return null;
    
    }
    
    进入到AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
    protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
    
    throws ClassNotFoundException {
    
    
    
    return BeanDefinitionReaderUtils.createBeanDefinition(
    
    parentName, className, this.readerContext.getBeanClassLoader());
    
    }
    
    进入到BeanDefinitionReaderUtils.createBeanDefinition
    
    public static AbstractBeanDefinition createBeanDefinition(
    
    String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
    
    
    
    GenericBeanDefinition bd = new GenericBeanDefinition();
    
    bd.setParentName(parentName);
    
    if (className != null) {
    
    if (classLoader != null) {
    
    bd.setBeanClass(ClassUtils.forName(className, classLoader));//使用java反射机制初始化
    
    }
    
    else {
    
    bd.setBeanClassName(className);
    
    }
    
    }
    
    return bd;
    
    }
    
    

     

     

    Bean生命周期分析

    1.Spring对Bean进行实例化(相当于程序中的new Xx())

    2.Spring将值和Bean的引用注入进Bean对应的属性中

    3.如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法 
    (实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)

    4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。 
    (实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)

    5.如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext()方法,把bean所在的应用上下文的引用传入. 
    (作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )

    6.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法 
    (作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)

    7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。

    8.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )

    9.此时,bean已经准备就绪,可以被程序使用了,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

    10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

     

    @Component 
    
    public class UserEntity implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean,DisposableBean {
    
    
    
    private String userName;
    
    private Integer age = null;
    
    
    
    public UserEntity() {
    
    System.out.println("无惨构造函数.....");
    
    }
    
    
    
    public UserEntity(String userName, Integer age) {
    
    System.out.println("我是有参构造函数 userName:" + userName + ",age:" + age);
    
    this.userName = userName;
    
    this.age = age;
    
    }
    
    
    
    public String getUserName() {
    
    
    
    return userName;
    
    }
    
    
    
    public void setUserName(String userName) {
    
    
    
    this.userName = userName;
    
    }
    
    
    
    public Integer getAge() {
    
    
    
    return age;
    
    }
    
    
    
    public void setAge(Integer age) {
    
    
    
    this.age = age;
    
    }
    
    
    
    @Override
    
    public String toString() {
    
    return "UserEntity [userName=" + userName + ", age=" + age + "]";
    
    }
    
    
    
    public void setBeanName(String name) {
    
    System.out.println("BeanName:" + name);
    
    }
    
    
    
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    
    System.out.println("setBeanFactory");
    
    }
    
    
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    System.out.println("setApplicationContext");
    
    }
    
    
    
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    System.out.println("postProcessBeforeInitialization bean初始化之前" + beanName);
    
    return bean;
    
    
    
    }
    
    
    
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    System.out.println("postProcessAfterInitialization bean初始化之后" + beanName);
    
    return bean;
    
    
    
    }
    
        public void init(){
    
         System.out.println("init()");
    
        }
    
    public void afterPropertiesSet() throws Exception {
    
    System.out.println("afterPropertiesSet");
    
    }
    
    
    
    public void destroy() throws Exception {
    
     System.out.println("destroy 销毁bean");
    
        
    
    }
    
    
    
    }
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring003.xml");
    
    UserEntity user = (UserEntity) app.getBean("userEntity");
    
    app.destroy();

     

    SpringAop源码分析

    AOP简介

    概念

    切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
    连接点(Joinpoint) :程序执行过程中的某一行为。
    通知(Advice) :“切面”对于某个“连接点”所产生的动作。
    切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。
    目标对象(Target Object) :被一个或者多个切面所通知的对象。
    AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。

    通知(Advice)类型
    前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。
    后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。
    返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
    环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。
    抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。

    切入点表达式 :如execution(* com.spring.service.*.*(..))

    特点

    1、降低模块之间的耦合度

    2、使系统容易扩展

    3、更好的代码复用。

    流程说明

    1)AOP标签的定义解析刘彻骨肯定是从NamespaceHandlerSupport的实现类开始解析的,这个实现类就是AopNamespaceHandler。至于为什么会是从NamespaceHandlerSupport的实现类开始解析的,这个的话我想读者可以去在回去看看Spring自定义标签的解析流程,里面说的比较详细。

    2)要启用AOP,我们一般会在Spring里面配置<aop:aspectj-autoproxy/>  ,所以在配置文件中在遇到aspectj-autoproxy标签的时候我们会采用AspectJAutoProxyBeanDefinitionParser解析器

    3)进入AspectJAutoProxyBeanDefinitionParser解析器后,调用AspectJAutoProxyBeanDefinitionParser已覆盖BeanDefinitionParser的parser方法,然后parser方法把请求转交给了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去处理

    4)进入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法后,先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,里面在转发调用给registerOrEscalateApcAsRequired,注册或者升级AnnotationAwareAspectJAutoProxyCreator类。对于AOP的实现,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根据@point注解定义的切点来代理相匹配的bean。

    5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法处理完成之后,接下来会调用useClassProxyingIfNecessary() 处理proxy-target-class以及expose-proxy属性。如果将proxy-target-class设置为true的话,那么会强制使用CGLIB代理,否则使用jdk动态代理,expose-proxy属性是为了解决有时候目标对象内部的自我调用无法实现切面增强。

    6)最后的调用registerComponentIfNecessary 方法,注册组建并且通知便于监听器做进一步处理。

     

    创建AOP代理

    上面说到AOP的核心逻辑是在AnnotationAwareAspectJAutoProxyCreator类里面实现,那么我们先来看看这个类的层次关系

    流程说明

    1. spring 容器启动,每个bean的实例化之前都会先经过AbstractAutoProxyCreator类的postProcessAfterInitialization()这个方法,然后接下来是调用wrapIfNecessary方法。

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    if (bean != null) {
    
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    
    if (!this.earlyProxyReferences.contains(cacheKey)) {
    
    return wrapIfNecessary(bean, beanName, cacheKey);
    
    }
    
    }
    
    return bean;
    
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    
    if (this.targetSourcedBeans.contains(beanName)) {
    
    return bean;
    
    }
    
    if (this.nonAdvisedBeans.contains(cacheKey)) {
    
    return bean;
    
    }
    
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    
    this.nonAdvisedBeans.add(cacheKey);
    
    return bean;
    
    }
    
    // Create proxy if we have advice.
    
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    
    if (specificInterceptors != DO_NOT_PROXY) {
    
    this.advisedBeans.add(cacheKey);
    
    Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    
    this.proxyTypes.put(cacheKey, proxy.getClass());
    
    return proxy;
    
    }
    
    
    this.nonAdvisedBeans.add(cacheKey);
    
    return bean;
    
    }
    
    创建代理对象
    
    protected Object createProxy(
    
    Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    
    
    ProxyFactory proxyFactory = new ProxyFactory();
    
    // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
    
    proxyFactory.copyFrom(this);
    
    
    if (!shouldProxyTargetClass(beanClass, beanName)) {
    
    // Must allow for introductions; can't just set interfaces to
    
    // the target's interfaces only.
    
    Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
    
    for (Class<?> targetInterface : targetInterfaces) {
    
    proxyFactory.addInterface(targetInterface);
    
    }
    
    }
    
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    
    for (Advisor advisor : advisors) {
    
    proxyFactory.addAdvisor(advisor);
    
    }
    
    proxyFactory.setTargetSource(targetSource);
    
    customizeProxyFactory(proxyFactory);
    
    
    proxyFactory.setFrozen(this.freezeProxy);
    
    if (advisorsPreFiltered()) {
    
    proxyFactory.setPreFiltered(true);
    
    }
    
    
    return proxyFactory.getProxy(this.proxyClassLoader);
    
    }
    
    

    SpringMVC执行流程

     

    Spring MVC工作流程图

     

    图一

     

    图二 

     

    Spring工作流程描述

          1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

          2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

          3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法

           4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

          HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

          数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

          数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

          数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

          5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

          6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

          7. ViewResolver 结合Model和View,来渲染视图

          8. 将渲染结果返回给客户端。

     

    Spring工作流程描述

        为什么Spring只使用一个Servlet(DispatcherServlet)来处理所有请求?

         详细见J2EE设计模式-前端控制模式

        Spring为什么要结合使用HandlerMapping以及HandlerAdapter来处理Handler?

        符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。如HandlerAdapter可能会被用于处理多种Handler。

     

     

    展开全文
  • 我目前也在结合这两个方面看,也可以看下我的Tomcat源码分析系列。 二、Servlet规范 在servlet的规范当中,servlet容器或者叫web容器,如tomcat,中运行的每个应用都由一个ServletContext表示,在web容器中可以包含...

    一、概述

    • 对于大多数第一次看spring源码的人来说,都会感觉不知从哪开始看起,因为spring项目源码由多个子项目组成,如spring-beans,spring-context,spring-core,spring-aop,spring-web,spring-webmvc等,整个项目结构如图:
      在这里插入图片描述
    • 可能有人会觉得,既然spring是一个IOC容器或者说是一个bean的容器,那么应该从spring-beans看起,先了解spring是如何从xml文件配置获取需要创建的bean的信息,但是这里有个问题就是虽然知道怎么遍历初始化,但是不知道哪里用到或者说哪里让这些初始化开始,而且像BeanFactory,FactoryBean,Environment,PropertySource等接口还是比较抽象的,比较难看懂,所以很容易让人感觉枯燥,然后就放弃了。
    • 我们可以换个思路,从能接触到的角度开始,即我们通常会使用spring-mvc来进行web开发,如@Controller,@RequestMapping都是再熟悉不过的了。如果搭过spring-mvc项目都知道,通常需要在web.xml文件中,配置一个ContextLoaderListener,contextConfigLocation,DispatcherServlet,可能很多人都是从网上copy了一份配置过来或者知道contextConfigLocation是指定spring配置文件的位置,DispatcherServlet是接收所有请求的前端控制器,需要指定拦截路由:“/”,从而拦截所有URL中带“/”的请求,但是在spring源码中是怎么使用这些组件的呢?以及怎么配置了一个@Controller,@RequestMapping中指定了一个url,就可以访问了呢?还有就是通常我们的web项目都会部署在web容器,如tomcat当中,那么tomcat和spring有啥关系呢?所以我们可以带着这些问题去查看spring源码找到答案。
    • 所以我推荐是从spring-mvc开始看spring源码,因为这个是我们使用得比较多,比较容易理解的一个模块,然后一层一层往上剥,找到与spring-context,spring-beans,spring-aop等的关系。如果真的对JavaWeb开发,Java EE很感兴趣,或者更容易读懂spring的源码,可以先看servlet规范和Tomcat的设计与Tomcat的请求处理工作流。我目前也在结合这两个方面看,也可以看下我的Tomcat源码分析系列。

    二、Servlet规范

    • 在servlet的规范当中,servlet容器或者叫web容器,如tomcat,中运行的每个应用都由一个ServletContext表示,在web容器中可以包含多个ServletContext,即可以有多个web应用在web容器中运行。如在tomcat的webapp目录下,每个war包都对应一个web应用,tomcat启动时会解压war包,并启动相关的应用。
    • 在web容器启动的时候,会初始化web应用,即创建ServletContext对象,加载解析web.xml文件,获取该应用的Filters,Listener,Servlet等组件的配置并创建对象实例,作为ServletContext的属性,保存在ServletContext当中。之后web容器接收到客户端请求时,则会根据请求信息,匹配到处理这个请求的Servlet,同时在交给servlet处理之前,会先使用应用配置的Filters对这个请求先进行过滤,最后才交给servlet处理。
    • 了解web容器启动,之后接受客户端请求这些知识有啥用处呢?这里我们需要回过头来看我们的spring项目。我们在日常开发中,直接接触的是spring相关的组件,然后打成war包,放到web容器中,如拷贝到tomcat的webapp目录,并不会直接和web容器打交道。经过以上的分析,其实一个spring项目就是对应web容器里的一个ServletContext,所以在ServletContext对象的创建和初始化的时候,就需要一种机制来触发spring相关组件的创建和初始化,如包含@Controller和@RequestMapping注解的类和方法,这样才能处理请求。

    三、Listener监听器机制:ContextLoaderListener

    • servlet规范当中,使用了Listener监听器机制来进行web容器相关组件的生命周期管理以及Event事件监听器来实现组件之间的交互。
    • 其中一个重要的生命周期监听器是ServletContextListener。web容器在创建和初始化ServletContext的时候,会产生一个ServletContextEvent事件,其中ServletContextEvent包含该ServletContext的引用。然后交给在web.xml中配置的,注册到这个ServletContext的监听器ServletContextListener。ServletContextListener在其contextInitialized方法中定义处理逻辑,接口定义如下:
      /**
       * Implementations of this interface receive notifications about changes to the
       * servlet context of the web application they are part of. To receive
       * notification events, the implementation class must be configured in the
       * deployment descriptor for the web application.
       *
       * @see ServletContextEvent
       * @since v 2.3
       */
      public interface ServletContextListener extends EventListener {
      
          /**
           ** Notification that the web application initialization process is starting.
           * All ServletContextListeners are notified of context initialization before
           * any filter or servlet in the web application is initialized.
           * @param sce Information about the ServletContext that was initialized
           */
          public void contextInitialized(ServletContextEvent sce);
      
          /**
           ** Notification that the servlet context is about to be shut down. All
           * servlets and filters have been destroy()ed before any
           * ServletContextListeners are notified of context destruction.
           * @param sce Information about the ServletContext that was destroyed
           */
          public void contextDestroyed(ServletContextEvent sce);
      }
      
      从contextInitialized的注释可知:通知所有的ServletContextListeners,当前的web应用正在启动,而且这些ServletContextListeners是在Filters和Servlets创建之前接收到通知的。所以在这个时候,web应用还不能接收请求,故可以在这里完成底层处理请求的组件的加载,这样等之后接收请求的Filters和Servlets创建时,则可以使用这些创建好的组件了。spring相关的bean就是这里所说的底层处理请求的组件,如数据库连接池,数据库事务管理器等。
    • ContextLoaderListener:spring-web包的ContextLoaderListener就是一个ServletContextListener的实现类。ContextLoaderListener主要用来获取spring项目的整体配置信息,并创建对应的WebApplicationContext来保存bean的信息,以及创建这些bean的对象实例。默认去WEB-INF下加载applicationContext.xml配置,如果applicationContext.xml放在其他位置,或者使用其他不同的名称,或者使用多个xml文件,则与指定contextConfigLocation。具体spring源码的实现过程后续文章详细分析。
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <!-- 修改配置文件路径 -->
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/applicationContext.xml</param-value>
      </context-param>
      

    四、DispatcherServlet:前端控制器

    • 在web容器中,web.xml中的加载顺序:context-param -> listener -> filter -> servlet。其中ContextLoaderListener是属于listener阶段。我们通常需要在项目的web.xml中配置一个DispatcherServlet,并配置拦截包含“/”路径的请求,即拦截所有请求。这样在web容器启动应用时,在servlet阶段会创建这个servlet,由Servlet规范中servlet的生命周期方法可知:
      public interface Servlet {
          void init(ServletConfig var1) throws ServletException;
      
          ServletConfig getServletConfig();
      
          void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
      
          String getServletInfo();
      
          void destroy();
      }
      
      web容器在创建这个servlet的时候,会调用其init方法,故可以在DispatcherServlet的init方法中定义初始化逻辑,核心实现了创建DispatcherServlet自身的一个WebApplicationContext,注意在spring中每个servlet可以包含一个独立的WebApplicationContext来维护自身的组件,而上面通过ContextLoaderListener创建的WebApplicationContext为共有的,通常也是最顶层,即root WebApplicationContext,servlet的WebApplicationContext可以通过setParent方法设值到自身的一个属性。DispatcherServlet默认是加载WEB-INF下面的“servletName”-servlet.xml,来获取配置信息的,也可以与ContextLoaderListener一样通过contextLoaderConfig来指定位置。DispatcherServlet具体的源码设计在之后文章详细分析。

    五、总结

    • 从上面的分析,可知spring相关配置解析和组件创建其实是在web容器中,启动一个web应用的时候,即在其ServletContext组件创建的时候,首先解析web.xml获取该应用配置的listeners列表和servlet列表,然后保存在自身的一个属性中,然后通过分发生命周期事件ServletContextEvent给这些listeners,从而在listeners感知到应用在启动了,然后自定义自身的处理逻辑,如spring的ContextLoaderListener就是解析spring的配置文件并创建相关的bean,这样其实也是实现了一种代码的解耦;其次是创建配置的servlet列表,调用servlet的init方法,这样servlet可以自定义初始化逻辑,DispatcherServlet就是其中一个servlet。
    • 所以在ContextLoaderListener和DispatcherServlet的创建时,都会进行WebApplicationContext的创建,这里其实就是IOC容器的创建了,即会交给spring-context,spring-beans包相关的类进行处理了,故可以从这里作为一个入口,一层一层地剥spring的源码了。
    展开全文
  • Spring源码分析第三阶段,详细源码分析,有思路跟着主线走,妥妥的看懂源码
  • Spring源码分析扫描完整版(带完整目录),本书从源码层面分析Spring的整体架构及原理,读后会有很大收获
  • spring源码分析(1-10)

    2014-03-03 15:57:56
    Spring源代码分析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源代码解析(六):Spring声明式事务处理 Spring源代码解析(七):Spring AOP中对拦截器...
  • 第一步:获取spring-framework 源码 ,地址为:https://github.com/spring-projects/spring-framework/tree/5.0.x 第二步:导入源码  导入等就行了,一段时间后完成后构建。  坑1:plugin with id ‘java-test-...
  • 深入分析 Spring 源码(第一阶段),设计模式
  • Spring源码分析.rar

    2019-12-23 15:00:31
    Spring源码分析pdf
  • Tom_深入分析Spring源码docTom_深入分析Spring源码doc
  • Spring 源码分析文档----自用。
  • 本项目为Spring原始码分析项目,本仓库原始码来自v5.2.3版本,原始码注释仓库请查看 Netlify: ://huifer-spring-analysis.netlify.app 目录 Spring IoC讲义 独立类分析 支持 支付宝 微信公众号

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 137,013
精华内容 54,805
关键字:

spring源码分析

spring 订阅
友情链接: pdpcheck311.zip