zoukankan      html  css  js  c++  java
  • Spring源码阅读笔记

    前言

      作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌。

    每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精。就连我吃饭的家伙Java,现在想想,其实我根本就不了解。

      可是每当编写简历的时候,总想把工作经验、工作年限写的长一点,半年写成一年,一年写成两年。可是每当有人问我技术原理的时候,又会想,

    我的工作时间要是短一点的话,答不上来是不是就不会这么丢脸。

      还记得刚工作不久,就在项目中使用过Spring了,但是那个时候,只是照着别人写的代码,照葫芦画瓢似得写着自己的代码,画瓢花久了,写的熟练了,心中就开始窃喜,

    竟然也就敢厚着脸皮说,我也会Spring了,简历上就毫不犹豫的写上:了解Spring。(真是臭不要脸啊)

      Spring真的是很流行,做过了好多项目,基本上都或多或少的使用了Spring框架或者集成了Spring框架,于是乎,无论进入了哪个项目中,竟然也都得心应手。

    然后就有点飘飘然,领导还给安排带几个新人,开发时,还得意的给新人们讲解,这个注解这样使用,那个配置那样配置。但是,其实如何使用这个东西,只要是程序员,短短几天也都会了。

    几天后,新人就能与你干同样的活了,你这个老人与新人的区别在哪里呢?其实这时,我有点慌了。

      平时游荡于各个技术交流群,发现群里面其实有好多还是在校生,就开始钻研各种技术难题。回头再想想,我当时还在学校的时候在干嘛呢:打游戏、看小说、睡懒觉、泡校内网、约妹子、耗时间...

    人家还在学生时期,就开始阅读Spring源码了。而我呢,竟然都用了好几年了,都从来没有提起过阅读源码的念头。

      后来有一次面试,人家问:看你也有几年工作经验了,说一说Spring的原理吧、IOC、DI、AOP...再说一说,从一个请求开始,一直到得到响应,Spring都干了些什么?

    我的个亲娘啊,你到底在说什么,我怎么听不懂。我忽然想起了一首歌:


        我想了很久,我开始慌了,
    是不是我又做错了什么,你哭着对我说,童话里都是骗人的,你不可能是我要的程序猿

    岔题了...

    亡羊补牢,痛定思痛,总而言之,就读一下Spring源码(3.1版本)看看吧。

    读读读读读...

    对于没有任何源码阅读经验的人,而且大局观整体概念很差的人来说,源码真的是太难读了,可能还是人笨吧。所以我只能采取老办法,所谓书读百遍,其义自现,读源码应该也是同样的道理。

    到开始写本篇笔记开始,前前后后已经花了整整3周时间,期间各种debug,打了上百个断点,不厌其烦的一遍又一遍跟踪跟进,从服务器启动开始,

    从HttpServletBean的init()进入,再到initWebApplicationContext(),再到refresh(),再到refreshBeanFactory(),再到finishRefresh(),直到服务器启动成功。不知道读了多少遍,

    但是源码的东西实在的太多了,想要完全读懂,完全理清头绪,还差很远啊。所以我只重点关注了两块内容,就是bean的定位加载解析注册bean的实例化两大块内容,其他地方稍作了解,没有太过深入。

    整个容器的启动流程,都在AbstractApplicationContext的refresh()的模板方法中了。

     1 public void refresh() throws BeansException, IllegalStateException {
     2         synchronized (this.startupShutdownMonitor) {
     3             // Prepare this context for refreshing.
     4             prepareRefresh();
     5 
     6             // Tell the subclass to refresh the internal bean factory.
     7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
     8 
     9             // Prepare the bean factory for use in this context.
    10             prepareBeanFactory(beanFactory);
    11 
    12             try {
    13                 // Allows post-processing of the bean factory in context subclasses.
    14                 postProcessBeanFactory(beanFactory);
    15 
    16                 // Invoke factory processors registered as beans in the context.
    17                 invokeBeanFactoryPostProcessors(beanFactory);
    18 
    19                 // Register bean processors that intercept bean creation.
    20                 registerBeanPostProcessors(beanFactory);
    21 
    22                 // Initialize message source for this context.
    23                 initMessageSource();
    24 
    25                 // Initialize event multicaster for this context.
    26                 initApplicationEventMulticaster();
    27 
    28                 // Initialize other special beans in specific context subclasses.
    29                 onRefresh();
    30 
    31                 // Check for listener beans and register them.
    32                 registerListeners();
    33 
    34                 // Instantiate all remaining (non-lazy-init) singletons.
    35                 finishBeanFactoryInitialization(beanFactory);
    36 
    37                 // Last step: publish corresponding event.
    38                 finishRefresh();
    39             }
    40 
    41             catch (BeansException ex) {
    42                 // Destroy already created singletons to avoid dangling resources.
    43                 destroyBeans();
    44 
    45                 // Reset 'active' flag.
    46                 cancelRefresh(ex);
    47 
    48                 // Propagate exception to caller.
    49                 throw ex;
    50             }
    51         }
    52     }
    View Code

    其实,我并没有上来就看源码,而是先从看书开始,稍微了解,知道了一些关键点,关键流程,自己产生了一堆疑问,然后带着疑问去读源码,读着读着,发现有些疑问就这么解决了。

    看书:主要以《SPRING技术内幕:深入解析SPRING架构与设计原理》为主,仅仅才看了前两章节,关于Spring Framework的核心实现,AOP什么,事务控制什么的都还没看。

    看博客:看源码过程中,有些不理解的,就上网查博客,然后再回头读源码。觉得写得还可以的博客文章我记录了下来,供以后参考(希望管理员不要因为有外部链接,就把我踢掉了)。

    正如上文所说,因为水平有限,本篇随笔并非是解析源码的文章,因为源码很多看不懂的地方并不敢妄下断言, 网上各种解析源码,画UML图,时序图(我不会画图%>_<%)的文章也有很多,

    我是抱着解决疑问的态度来分析读源码的,另外在大神眼中,有些问题看起来可能问的很愚蠢,但是谁让我是菜鸟呢。

    以上是我的心路历程,不看也罢。


    那么,列出有疑问的地方(目前主要针对IOC相关部分、问题将不断补充)

    (补充:我用来阅读并且debug的demo是基于注解配置的,另外以下说明都纯属个人谬论,如果错漏,请予以批评指正。)

    问题1:我为什么要使用Spring?

    问题2:Spring怎么这么聪明,知道哪些bean需要被实例化?

    问题3:Spring中Bean是什么时候被实例化的?

    问题4:依赖的bean是何时被注入的?

    问题5:bean的单例模式?原型模式是什么东西?分别是何时被实例化的?

    问题6:采用注解注入时,为什么只声明接口就可以将其实现类注入?

    问题7:采用注解注入时,接口与实现类为何都能注入成功?区别?

    问题8:加上了autowired的属性是什么时候实例化的?

    问题9:网络上经常说的“第一次使用bean的时候实例化该bean”是什么意思?

    问题10:发送一个请求,是怎么定位到具体的Controller的某一个方法的?

    问题11:HandlerMapping、HandlerAdater与Controller到底是什么关系?

    问题1:我为什么要使用Spring?

      这个问题其实比较尴尬,因为其实我入行比较晚, 这个时候,已经有一大波比较成熟的框架体系了,早期的什么繁琐的臃肿的框架基本上没用过几个,再加上项目经验比较单一,用来用去就那么几个框架,用起来都大差不差,

    用的熟练了,感觉都差不多。所以说,完全没有能力来横向比较,谁谁谁架构合理,谁谁谁扩展性强,只能勉强说这个框架的那种写法挺方便,比起单纯的Servlet写法要方便之类的....其他的,我都呵呵就好了。

    如果你要强行问我为什么使用Spring,那我只能回答你:因为大家都在用啊,呵呵...

    问题2:Spring怎么这么聪明,知道哪些bean需要被实例化?

         什么控制反转、依赖注入,说到底就是程序在需要使用一个bean的时候,Spring框架确保该bean已经被实例化了,可以直接拿来使用。那么这时候,我想要知道,Spring是如何知道哪些bean需要实例化?

    其实程序总归是程序,程序是很笨的,它并不知道要实例化哪些东西,除非你告诉它。

    首先需要配置DispatcherServlet,这是web应用的总入口,也可以说是中央处理器,就是通过它,一步一步的启动Spring容器的。

    在web.xml中

        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:/spring/readspring-servlet.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>

    另外还需要配置另外一个文件:readspring-servlet.xml,这个东西叫做上下文,主要是告诉Spring,在启动的时候干些什么事情,启动哪些功能,实例化哪些Bean。

    上一个最简单的配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
                               http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context-3.1.xsd">
    
        <!-- 扫描指定目录 -->
        <context:component-scan base-package="com.readspring" />
    
        <!-- 不拦截静态资源 -->
        <mvc:default-servlet-handler />
    
        <!-- 启动注解支持 -->
        <mvc:annotation-driven />
    
        <!-- 配置视图解析器 -->
        <bean id="internalResourceViewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>

    我不逐一解读每句话的意思,还是针对上面的问题,Spring如何知道哪些bean要被实例化?

    或者说的具体一点,Spring是如何知道要实例化我们写的Controller、Service、Dao的。

    是这段配置告诉它的。

        <!-- 扫描指定目录 -->
        <context:component-scan base-package="com.readspring" />

    从字面意思可以看出,component-scan就是组件扫描的意思,然后有一个base-package,告诉它需要扫描的文件路径。  

     组件自动扫描是在Spring2.5加上的,在一个稍大的项目中通常会有上百个组件,如果都使用XML的bean定义来配置组件的话,显然会增加配置文件的体积,

     查找及维护也不方便,而Spring2.5就为我们引入了组件自动扫描机制,它可以在classpath下寻找标注了@Service、@Repository、@Controller、@Component注解的类 

     并把这些类纳入Spring容器中管理,它的作用和在XML中使用bean节点配置组件是一样的。

    另外补充说明,既然是component扫描,为什么标注了@Service等也会被扫描到,可以看一下源码。

    Service注解本身也被标注了@Component注解,所以说可以被扫描到,相当于继承自@Component。

    下面,我大概的描述一下,容器启动的过程中,如何根据配置,来扫描指定类的。

    容器启动过程中,首先调用DispatcherSerlvet的init方法,init方法内部根据web.xml的配置,读取配置的上下文readspring-servlet.xml,然后逐句解析该上下文,

    当它读取到context:component-scan标签时,就启动对应的解析器,也可以叫做扫描器,对应的Class为:ComponentScanBeanDefinitionParser

    有这么一个对应关系的Class,可以让Spring知道,为什么读取到这个标签,就实例化ComponentScanBeanDefinitionParser这个类。

    public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    
        public void init() {
            registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
            registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
            registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
            registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
            registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
            registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
            registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
        }
    
    }

    看一下这个类ComponentScanBeanDefinitionParser中调用了doScan方法,可以看到传了一个参数:basePackages,就是我们配置的路径。

        public BeanDefinition parse(Element element, ParserContext parserContext) {
            String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    
            // Actually scan for bean definitions and register them.
            ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
            Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
            registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
            return null;
        }

    具体的扫描逻辑,在这个类中ClassPathScanningCandidateComponentProvider.findCandidateComponents(...)

    这样,就读取到了需要被实例化的所有类,之后这些类的信息会被封装成一个一个的BeanDefinition,然后保存到DefaultListableBeanFactory的beanDefinitionMap中供后续使用。

        /** Map of bean definition objects, keyed by bean name */
        private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    这个问题算是解决了。 

    问题3:Spring中Bean是什么时候被实例化的?

               一般开发web应用时,有controller、service、dao等不同的bean,这些bean到底是怎么实例化的?什么时候被实例化的?有顺序吗?

    首先回答该问题:所有的这些Bean,在容器启动的时候,已经全部被实例化了。

    下面看代码一探究竟,还得看模板方法,AbstractApplicationContext.refresh(),其中有这么一个方法

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

    看注释知道:实例化剩下的所有单例。

    Spring中的Bean默认都是单例的,除非显示的声明为prototype。

    剩下的(remaining)的意思是有一些Spring自身的处理器、解析器等Bean不是在这里实例化的,我们自己编写的常规类Controller、Service等全部都是在此处实例化。

    下面我用伪代码的形式简要的说明一下实例化的过程

    以TestController为例子

    Class TestController {

     @Autowired

       private ITestService testService;

       ...

    }

    // 首先循环所有的BeanDefinition列表

    for(String beanName : 之前获取到的beanDefiniton的List){

      String  beanName = "testController"; // 以TestController为例子

      // 调用getbean方法

      getBean(beanName){

               // 检查是否已经被实例化

        Object  instance = getSingleton(beanName);

        if (instance  == null) {

                 // 如果没有,准备实例化

          bean = createBean(beanName){

                          // 执行实例化过程,正式实例化就是在这个方法中

             bean = createBeanInstance(){

              // 这里具体的实例化是利用cglib类库,通过Java反射原理构造函数实例化方式实例化

              ReflectionUtils.makeAccessible(ctor);
              Constructor.newInstance(args);

            }

             // 组装Bean,所谓的组装就是把该bean的属性给设置一下,如testService

                          populateBean(bean){

             // 以TestController为例子,这个方法里面,testService的实例的引用会被注入(Inject)到TestConroller的实例中的这个属性testService中

             // 如果testService的实例不存在,就会递归调用getBean()方法,直到一个没有任何依赖的Bean为止。

             Object serviceInstance =  getBean(testService);

             // 注入属性实例,相当于 testConroller.testService = serviceInstance;(依赖注入就是在这里实现的)

             inject(serviceInstance);

            }

            // 实例化成功后,实例会被保存到singletonObjects Map中

            addSingleton(beanName, serviceInstance);

          }

        }

      }

    }

    (此处省略了大量细节,只关注整体实现流程)

     所有的Bean被实例化后,会被存入这个Map中供后续使用。

    DefaultSingletonBeanRegistry.java

        /** Cache of singleton objects: bean name --> bean instance */
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();

    从上面的说明中,大体可以了解实例化Bean的流程,下面再来看一下源码实例化的一些具体细节实现。

    1.Bean的实例化

    首先看AbstractAutowireCapableBeanFactory.createBeanInstance(...)

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
            // Make sure bean class is actually resolved at this point.
            Class beanClass = resolveBeanClass(mbd, beanName);
    
            if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
            }
    
            if (mbd.getFactoryMethodName() != null)  {
                return instantiateUsingFactoryMethod(beanName, mbd, args);
            }
    
            // Shortcut when re-creating the same bean...
            boolean resolved = false;
            boolean autowireNecessary = false;
            if (args == null) {
                synchronized (mbd.constructorArgumentLock) {
                    if (mbd.resolvedConstructorOrFactoryMethod != null) {
                        resolved = true;
                        autowireNecessary = mbd.constructorArgumentsResolved;
                    }
                }
            }
            if (resolved) {
                if (autowireNecessary) {
                    return autowireConstructor(beanName, mbd, null, null);
                }
                else {
                    return instantiateBean(beanName, mbd);
                }
            }
    
            // Need to determine the constructor...
            Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
            if (ctors != null ||
                    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
                return autowireConstructor(beanName, mbd, ctors, args);
            }
    
            // No special handling: simply use no-arg constructor.
            return instantiateBean(beanName, mbd);
        } 

    经过一些列的必要性验证,最终调用了instantiateBean(...),有两个参数:一个beanName,一个是该bean的定义信息

        protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
            try {
                Object beanInstance;
                final BeanFactory parent = this;
                if (System.getSecurityManager() != null) {
                    beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                        }
                    }, getAccessControlContext());
                }
                else {
                    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
                }
                BeanWrapper bw = new BeanWrapperImpl(beanInstance);
                initBeanWrapper(bw);
                return bw;
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
            }
        }

    在这里可以看到获取了实例化策略:getInstantiationStrategy(),点进去可以看到,默认采取的是Cglib的实例化策略。

        /** Strategy for creating bean instances */
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    然后继续跟进instantiate方法,

    public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
            // Don't override the class with CGLIB if no overrides.
            if (beanDefinition.getMethodOverrides().isEmpty()) {
                Constructor<?> constructorToUse;
                synchronized (beanDefinition.constructorArgumentLock) {
                    constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
                    if (constructorToUse == null) {
                        final Class clazz = beanDefinition.getBeanClass();
                        if (clazz.isInterface()) {
                            throw new BeanInstantiationException(clazz, "Specified class is an interface");
                        }
                        try {
                            if (System.getSecurityManager() != null) {
                                constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
                                    public Constructor run() throws Exception {
                                        return clazz.getDeclaredConstructor((Class[]) null);
                                    }
                                });
                            }
                            else {
                                constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
                            }
                            beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
                        }
                        catch (Exception ex) {
                            throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                        }
                    }
                }
                return BeanUtils.instantiateClass(constructorToUse);
            }
            else {
                // Must generate CGLIB subclass.
                return instantiateWithMethodInjection(beanDefinition, beanName, owner);
            }
        }

    可以看到,首先获取Bean的Class对象,然后根据Class对象获取构造器,最终调用BeanUtils.instantiateClass方法。

    经过了一层又一层的嵌套调用,终于看到了真正的Bean实例化方式,通过Java的反射机制,根据Class的默认构造器实例化Bean对象。

        public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
            Assert.notNull(ctor, "Constructor must not be null");
            try {
                ReflectionUtils.makeAccessible(ctor);
                return ctor.newInstance(args);
            }
            catch (InstantiationException ex) {
                throw new BeanInstantiationException(ctor.getDeclaringClass(),
                        "Is it an abstract class?", ex);
            }
            catch (IllegalAccessException ex) {
                throw new BeanInstantiationException(ctor.getDeclaringClass(),
                        "Is the constructor accessible?", ex);
            }
            catch (IllegalArgumentException ex) {
                throw new BeanInstantiationException(ctor.getDeclaringClass(),
                        "Illegal arguments for constructor", ex);
            }
            catch (InvocationTargetException ex) {
                throw new BeanInstantiationException(ctor.getDeclaringClass(),
                        "Constructor threw exception", ex.getTargetException());
            }
        }

    2.依赖注入

    还是参考之前的例子

    Class TestController {

     @Autowired

       private ITestService testService;

       ...

    }

    TestController依赖于testService,如果想要正常使用TestController,首先TestController需要被实例化,然后其属性testService也需要被实例化。

    通过之前1.Bean的实例化,已经知道TestController是如何被实例化的,但是尽管TestController有了实例,但是如果你Debug会发现,其属性值testService

    此时仍然是testService == null,这个时候就需要把testService的实例(确切的说,应该是实例的引用),像“打针”一样,注入到TestController实例的testService属性中。

    这段逻辑发生在上面伪代码的populateBean(...)方法中,具体在AbstractAutowireCapableBeanFactory.populateBean(...)

    在这个方法中,有这么一段

            if (hasInstAwareBpps || needsDepCheck) {
                PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw);
                if (hasInstAwareBpps) {
                    for (BeanPostProcessor bp : getBeanPostProcessors()) {
                        if (bp instanceof InstantiationAwareBeanPostProcessor) {
                            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                            pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                            if (pvs == null) {
                                return;
                            }
                        }
                    }
                }
                if (needsDepCheck) {
                    checkDependencies(beanName, mbd, filteredPds, pvs);
                }
            }
            applyPropertyValues(beanName, mbd, bw, pvs);

    因为我是采用的@Autowired注解注入的方式,所以此次实际上调用的是AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(...)方法

        @Override
        public PropertyValues postProcessPropertyValues(
                PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
         // 根据bean的Class对象获取其对于的Field以及method
            InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
            try {
           // 调用注入方法 metadata.inject(bean, beanName, pvs); }
    catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }

    因为可能有多个需要注入的属性,所以需要循环注入

        public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
            if (!this.injectedElements.isEmpty()) {
                boolean debug = logger.isDebugEnabled();
                for (InjectedElement element : this.injectedElements) {
                    if (debug) {
                        logger.debug("Processing injected method of bean '" + beanName + "': " + element);
                    }
                    element.inject(target, beanName, pvs);
                }
            }
        }

    执行真正的注入(此处获取testService实例时,如果实例不存在,将会触发获取testService实例的递归调用)

            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
                Field field = (Field) this.member;
                try {
                    Object value;
             // 首先获取需要注入的属性的实例对象
    if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = descriptor; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); } } } } else { this.cachedFieldValue = null; } this.cached = true; } } }
             // 利用Java反射机制,将filed的对象注入bean的属性当中
             // 相当于执行了 testController.setTestService(testService)
            
    if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } catch (Throwable ex) { throw new BeanCreationException("Could not autowire field: " + field, ex); } } }

    这就注入好了。

    问题4:依赖的bean是何时被注入的?

    这个问题其实问题3的2.依赖注入中已经解答了,当其父Bean在实例化后,调用组装Bean的时候(populateBean方法),执行了依赖Bean的实例化以及注入操作。

    问题5:bean的单例?多例是什么东西?分别是何时被实例化的?

    首先来看看什么是Spring的scope

    目前,scope的取值有5种。

    在Spring 2.0之前,有singleton和prototype两种;

    在Spring 2.0之后,为支持web应用的ApplicationContext,推出另外三种:request、session和global session类型。

    1. singleton (单一实例)

    一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例。此外,singleton类型的bean定义,从容器启动,到他第一次被请求而实例化开始,只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。

    SpringMVC中,默认都是采用singleton模式。

    2. prototype(多例)

    spring容器在进行输出prototype的bean 对象时,会每次都重新生成一个新的对象给请求方,虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,

    并且对象实例返回给请求方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。

    3. request 、session和global session

    他们只适用于web程序,通常是和XmlWebApplicationContext共同使用。

    可以参考一下这篇博文:Spring对象生存周期(Scope)的分析

    下面看一下源码:AbstractBeanFactory.doGetBean(...),可以看到,在实例化Bean的时候,首先判断了该bean的scope范围,

    单例、多例、还是其他范围,然后最终都调用了createBean方法,所以说不管是什么作用范围,其实例化的过程都是一样的,可以参考上面的说明。

    另外可以看到,单例时,从getSingleton(...)中取值,这个方法中会缓存所有已经实例化的单例Bean。而多例时,可以看到每次都是直接调用的createBean方法,返回了新生成Bean。

    protected <T> T doGetBean(
                final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
                throws BeansException {
    
                ...// Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            public Object getObject() throws BeansException {
                                beforePrototypeCreation(beanName);
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                finally {
                                    afterPrototypeCreation(beanName);
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; " +
                                "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            
           ...return (T) bean;
        }

    问题6:采用注解注入时,为什么只声明接口就可以将其实现类注入?

    看如下例子:

    @Controller
    Class TestController {
    
      @Autowired
       private ITestService testService;
    
       ...
    
    }
    
    @Service
    public class TestServiceImpl implements ITestService {
    
      ...
    
    }

    一般情况下,采用非注解方式注入的时候,都需要显式的声明变量与实现类的对应关系,类似于

    <bean id="testService" class="com.readspring.service.impl.TestServiceImpl" />

    但是在基于注解的配置中,并不需要这段配置却仍然可以正常的实现注入。

    例如:在TestController中,其成员变量testService被声明为接口类型:ITestService,但是Spring在实例化TestController时,

    却可以将TestServiceImpl的实例成功注入到testService中。

    那么,使用注解方式配置时,Spring是如何根据接口找到其实现类的呢?

    :Spring主要利用了Class.isAssignableFrom()方法来实现接口与实现类的匹配。

      Class1.isAssignableFrom(Class2):如果Class1是Class2本身或者是其接口或者父类,则返回true,否则返回false;

    以testService的实例化为例,当Spring容器启动的时候,首先会读取配置文件,经过定位、加载、解析、注册,最终把标注了注解的类(例如:标注了@Service的TestServiceImpl)注册到BeanDefination的Map中并且缓存。

    然后,在实例化testService的时候,Spring此刻并不知道testService的实现类是哪个,而是循环遍历BeanDefinitionMap中的Bean,逐一与ITestService接口进行类型匹配,一旦匹配上就认定为其实现类,并对其进行实例化。

    用伪代码表示就是:

    for(BeanDefinition bean: BeanDefinitionList) {

     Class testServiceImplClass = Class.forName(bean.getBeanClassName());

     if (ITestService.class.isAssignableFrom(testServiceImplClass)) {

      // 成功定位到ITestService接口的实现类

      return testServiceImplClass;

     }

    }

    以上这段处理主要是发生在依赖注入阶段(请参考之前的2.依赖注入)。

    在之前依赖注入的描述中,侧重描述了属性注入其实就是通过Java反射来实现的,并没有讲解如何获得注入的实例,请看下面红色代码。

    @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
                Field field = (Field) this.member;
                try {
                    Object value;
                    if (this.cached) {
                        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
                    }
                    else {
                        DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
                        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
                        TypeConverter typeConverter = beanFactory.getTypeConverter();
    // 根据接口定位到实现类的过程发生此处 value
    = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = descriptor; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); } } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } catch (Throwable ex) { throw new BeanCreationException("Could not autowire field: " + field, ex); } }

    继续跟进,定位到DefaultListableBeanFactory.doResolveDependency(...),本方法主要判断属性类型,根据不同的类型做相应处理。

    protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
                Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException  {
    
            ...
            
            if (type.isArray()) {
                ...
            }
            else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
                ...
            }
            else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
                ...
            }
            else {
    // Find bean instances that match the required type.
    // 根据实际的类型找到对应的实例 Map
    <String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(type, "", descriptor); } return null; } if (matchingBeans.size() > 1) { String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + matchingBeans.size() + ": " + matchingBeans.keySet()); } if (autowiredBeanNames != null) { autowiredBeanNames.add(primaryBeanName); } return matchingBeans.get(primaryBeanName); } // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); if (autowiredBeanNames != null) { autowiredBeanNames.add(entry.getKey()); } return entry.getValue(); } }

    层层深入,最终定位到DefaultListableBeanFactory.getBeanNamesForType(...)

    根据方法名可以看出,本方法可以根据类型找到对应的类名,换句话说就是:根据我们例子中提供的接口ITestService可以找到其实现类TestServiceImpl。

    未完待续....

  • 相关阅读:
    文件拖拽上传
    30天自制操作系统笔记(第三天)
    PAT 1040到底有几个pat
    pat 1039 到底买不买
    pat 1038 统计同成绩学生
    pat 乙级1034
    pat 乙级1022
    pat 乙级1009
    pat 乙级1008
    pat 乙级1002
  • 原文地址:https://www.cnblogs.com/notDog/p/5420727.html
Copyright © 2011-2022 走看看