zoukankan      html  css  js  c++  java
  • 【Spring源码这样读】-再次走近容器Spring IOC 一

    准备工作已经做完了,现在我们就可以开始正式进入阅读环节。本文如题,主要讲的就是我们Spring的核心部分容器。这个词相比都是不陌生的,很多人接触Spring的时候就会接触到Spring的两大核心功能IOC和AOP。这里我们换一种角度来读,希望能给大家带来一些新的认知。大佬请略过,有异议的地方还请各位大佬指出。

    什么是容器

    说到容器,估计很多人都会浮现两个概念:IOC、DI。

    • IOC:Inversion of Control,即控制反转。
      IOC并不是一种什么技术,而是一种编程思想。那什么是控制反转,为什么控制反转?
      先来看一个简单的示例:
    public class Test {
        public static void main(String[] args) {
            new Test1().testMethod();
        }
    }
    
    class Test1 {
        public void testMethod() {
            System.out.println("testMethod方法被调用");
        }
    }
    

    这是一段很简单的代码,在一个类里面调用了另外一个类的方法。调用另外一个类的方法时,我们直接创建该类的对象。在我们框架的使用当中我们是不需要去创建对象的,都是框架给我们在做这些事情。那这里我们就能明显的看到一个问题,对象在以不同的方式被使用。

    谁控制谁?在我们上面的例子当中,可以明显的看到,new对象是我们当前方法来控制的。但是容器呢?我们基本看不到new的操作,我们都是直接注入使用,所以可以直接推断出对象的new动作不是当前使用的程序,而是框架。具体点说就是框架在做这件事情。
    什么是正转、反转?所谓的正转,就是程序内部直接new对象,并赋值。反转就是容器new对象,主动给程序提供

    这种操作就解决了一个问题,解耦。试着想一下,如果我们的代码都通过写new来使用对象,对象的管理工作会很繁杂。

    • DI—Dependency Injection
      即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。上面我们提到的框架再给我们new对象,然后给我们提供可以用的对象,其实这里就包含了对象的注入,如果不注入,框架创建的对象怎么被我们写的代码引用呢?

    Spring IOC容器的应用场景

    在 Spring 中,Spring IoC 提供了一个基本 JavaBean 容器,通过 IoC 模式管理依赖关系,并通过依赖注入和 AOP 切面增强了为 JavaBean 这样的 POJO 对象提供了事务管理、声明周期管理等功能。其实如果我们把IOC看做一个水桶,那么项目里面那些Bean就都是水。在没有框架的情况下,水是很不好收集保留的,但是当我们有了这个容器之后,我们发现,他给我们带来了很大的便利,不仅不需要来回的去new,也不需要去关心,他有多少了。

    Spring IOC容器的实现

    目前Spring IOC的主流实现就两种方式,BeanFactory和ApplicationContext,我们不妨来体验一下两种实现方式。

    • BeanFactory
      这里需要创建一个简单的Spring项目,引入Spring的基本依赖即可,然后我们来尝试使用一下容器
      先来创建一个简单的bean
    public class TestBean {
    
        private String name = "this is bean";
    
        public String getName() {
            return this.name;
        }
    
    }
    

    这个bean并没有什么特别的地方,很普通。然后我们再来创建一个配置文件testBeanFactory.xml,将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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="testBean" class="com.echo.demo.TestBean"/>
    
    </beans>
    

    在我们的普通程序中,我们要使用bean就需要去new,但是这里,我们不需要了。可以直接通过容器来获取bean

    获取被容器管理的bean

    package com.echo.demo;
    
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class Test {
    
        public static void main(String[] args) {
    
            XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("testBeanFactory.xml"));
            TestBean testBean = (TestBean)xmlBeanFactory.getBean("testBean");
            System.out.println(testBean.getName());
    
        }
    
    }
    

    到这里为止,我们已经体验了BeanFactory的基本用法。很简单,我们仅仅只是建了一个bean,并且声明了一下,然后就可以拿到这个bean的实例了。

    • ApplicationContext
      我们不妨用ApplicatonContext也来做个实例,比对一下。和上面一样,来个一样的bean
    public class TestBean {
    
        private String name = "this is bean";
    
        public String getName() {
            return this.name;
        }
    
    }
    

    注意ApplicatonContext的实现也需要xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="testBean" class="com.echo.demo.TestBean" />
    
    </beans>
    

    到目前为止,其实我们看到的两种方式也没啥区别,但是我们看看获取bean的代码就知道了,使用上也就是获取bean的这两行代码不一样

    package com.echo.demo;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
            TestBean testBean = (TestBean) applicationContext.getBean("testBean");
            System.out.println(testBean.getName());
        }
    
    }
    

    这两段简易的代码,其实从我们一个使用的角度来讲,并没有很实质性的区别,无非就是用了另外一个类来调用获取bean的方法。这两段代码,不管是从表面上看还是从他的实现来看,都逃不过这么几部:

    • 读取配置文件
    • 根据配置文件找到对应的类并通过反射来实例化
    • 然后存入容器,被调用的时候取出

    那真的是这样的子的吗?我们深入实现来复核一下我们的想法

    这里我们已经有了猜想,同时代码实例也已经有了,我们不妨来通过时序图深入了解一下。这个时序图是从我们代码的Test类开始的,这里我们画了一个XmlBeanFactory初始化的时序图来看我们的代码逻辑到底做了件什么事情。
    在这里插入图片描述

    当我们看到这个时序图的时候,其实应该是能比较明了的知道前面我们的代码,到底做了些什么事情。代码从ClassPathResource获取文件开始,最终拿到对象。如果光从时序图上来看的话,跟我们之前的推测没啥很大的出入,为了进一步的来了解我们的推测,我们用源码来应正一下。

    • 代码中ClassPathResource到底做了件什么事情?先看这里
    /**
     * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
     * A leading slash will be removed, as the ClassLoader resource access
     * methods will not accept it.
     * <p>The thread context class loader will be used for
     * loading the resource.
     * @param path the absolute path within the class path
     * @see java.lang.ClassLoader#getResourceAsStream(String)
     * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
     */
    public ClassPathResource(String path) {
    	this(path, (ClassLoader) null);
    }
    
    /**
     * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
     * A leading slash will be removed, as the ClassLoader resource access
     * methods will not accept it.
     * @param path the absolute path within the classpath
     * @param classLoader the class loader to load the resource with,
     * or {@code null} for the thread context class loader
     * @see ClassLoader#getResourceAsStream(String)
     */
    public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
    	Assert.notNull(path, "Path must not be null");
    	String pathToUse = StringUtils.cleanPath(path);
    	if (pathToUse.startsWith("/")) {
    		pathToUse = pathToUse.substring(1);
    	}
    	this.path = pathToUse;
    	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }
    
    
    /**
     * Return the default ClassLoader to use: typically the thread context
     * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
     * class will be used as fallback.
     * <p>Call this method if you intend to use the thread context ClassLoader
     * in a scenario where you clearly prefer a non-null ClassLoader reference:
     * for example, for class path resource loading (but not necessarily for
     * {@code Class.forName}, which accepts a {@code null} ClassLoader
     * reference as well).
     * @return the default ClassLoader (only {@code null} if even the system
     * ClassLoader isn't accessible)
     * @see Thread#getContextClassLoader()
     * @see ClassLoader#getSystemClassLoader()
     */
    @Nullable
    public static ClassLoader getDefaultClassLoader() {
    	ClassLoader cl = null;
    	try {
    		cl = Thread.currentThread().getContextClassLoader();
    	}
    	catch (Throwable ex) {
    		// Cannot access thread context ClassLoader - falling back...
    	}
    	if (cl == null) {
    		// No thread context class loader -> use class loader of this class.
    		cl = ClassUtils.class.getClassLoader();
    		if (cl == null) {
    			// getClassLoader() returning null indicates the bootstrap ClassLoader
    			try {
    				cl = ClassLoader.getSystemClassLoader();
    			}
    			catch (Throwable ex) {
    				// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
    			}
    		}
    	}
    	return cl;
    }
    
    
    /**
     * Returns the system class loader for delegation.  This is the default
     * delegation parent for new <tt>ClassLoader</tt> instances, and is
     * typically the class loader used to start the application.
     *
     * <p> This method is first invoked early in the runtime's startup
     * sequence, at which point it creates the system class loader and sets it
     * as the context class loader of the invoking <tt>Thread</tt>.
     *
     * <p> The default system class loader is an implementation-dependent
     * instance of this class.
     *
     * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
     * when this method is first invoked then the value of that property is
     * taken to be the name of a class that will be returned as the system
     * class loader.  The class is loaded using the default system class loader
     * and must define a public constructor that takes a single parameter of
     * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
     * instance is then created using this constructor with the default system
     * class loader as the parameter.  The resulting class loader is defined
     * to be the system class loader.
     *
     * <p> If a security manager is present, and the invoker's class loader is
     * not <tt>null</tt> and the invoker's class loader is not the same as or
     * an ancestor of the system class loader, then this method invokes the
     * security manager's {@link
     * SecurityManager#checkPermission(java.security.Permission)
     * <tt>checkPermission</tt>} method with a {@link
     * RuntimePermission#RuntimePermission(String)
     * <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
     * access to the system class loader.  If not, a
     * <tt>SecurityException</tt> will be thrown.  </p>
     *
     * @return  The system <tt>ClassLoader</tt> for delegation, or
     *          <tt>null</tt> if none
     *
     * @throws  SecurityException
     *          If a security manager exists and its <tt>checkPermission</tt>
     *          method doesn't allow access to the system class loader.
     *
     * @throws  IllegalStateException
     *          If invoked recursively during the construction of the class
     *          loader specified by the "<tt>java.system.class.loader</tt>"
     *          property.
     *
     * @throws  Error
     *          If the system property "<tt>java.system.class.loader</tt>"
     *          is defined but the named class could not be loaded, the
     *          provider class does not define the required constructor, or an
     *          exception is thrown by that constructor when it is invoked. The
     *          underlying cause of the error can be retrieved via the
     *          {@link Throwable#getCause()} method.
     *
     * @revised  1.4
     */
    @CallerSensitive
    public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
    

    基本上核心代码都已经拿出来了,不难发现,其实就是做了一些初始化,还做了一个权限的判断。但是这一步代码好像并没有去获取到文件?没有获取到文件的真正内容?这一个问题由于本文过长,我们在下一篇文章再分析

  • 相关阅读:
    HTML5之Canvas影片广场
    iOS英语—》中国本土化,如调用专辑,摄像头的变化“cancel”,“photos”至“撤消”,“摄像头”
    约瑟夫问题解决
    2014在百度之星资格赛的第二个问题Disk Schedule
    2015第30周二
    2015第30周一
    2015第29周日
    2015第29周六Spring
    2015第29周五AOP
    2015第29周四
  • 原文地址:https://www.cnblogs.com/xlecho/p/14663593.html
Copyright © 2011-2022 走看看