Spring源码学习(二)—spring概览

1.spring特性

    一提到spring,想到最多的就是IOC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程),这两个特性不仅让spring框架风靡全球,更是改变了整个行业,所有java开发工程师甚至其他工程师,都开始直接或间接地学习并使用spring的这两大特性,高内聚和低耦合,一切资源变得合理、简单,这不仅是spring框架所追求的的目标,更是面向对象编程甚至整个行业所追求的至高目标。

1).控制反转

a).概念

    控制反转,这不是一个技术,而是一种编程思想。究其字面意思,将控制进行了反转,那么我们就有几个问题了:一,控制的啥;二,要是反转的话,之前是谁控制的;三,反转给了谁;四,到底带来了什么变化。

    对于第一个问题,控制的是外部资源的获取,包括对象的创建、文件的读取等等。

    对于第二个问题,与反转相对的正转到底是咋回事儿,也就是反转之前是谁控制的,我们先举个例子。

public Class A{

	 public void a(){
	    System.out.println("output letter a!");
	  }
	}
public Class B{
	   A aTest= new A();
	     public void b(){
	       System.out.println("gonna use function a()!");   
	       aTest.a();
	  }
	}

       上面的代码中有两个类,A和B,类A中有个方法a,用来输出一句话,类B中有个方法b,用来调用方法a。如果没有IOC的思想,那么实现起来就像上面的代码一样,我们需要在类B中首先实例化一个A,就是使用new创建一个类A的对象aTest,然后再在方法b中调用aTest.a()来实现对方法a的使用。可以看出,在这个例子中,对象aTest的创建是由类B控制的,在创建了对象的同时,类B就持有了类A的引用,也就是类A的对象成为了类B的成员变量,二者就产生了关联关系,而这种关联关系使得两个类之间的耦合度大大增加,并且类A的实例和类B的生命周期是相同的,当类B被销毁后,类A的实例也就不复存在了。如果我们想再使用方法a,就必须再重新实例化一个A的对象进行调用。这也就是所谓的正转(相对于IOC中的反转来说),这也同时回答了第二个问题,之前是由应用程序自己控制对象的创建的。

    对于第三个问题,spring框架实现了一个IOC容器,通过该容器实现了控制反转,也就是说,将对象创建的控制权反转给了IOC容器,应用程序不再控制对象的创建,而是只关心对象的使用。还是上面的例子,为了将控制权交给IOC容器,我们需要进行三个步骤的操作:1.首先,建立类A,其实就是一个bean;2.其次,将类A注册到配置文件中,告诉IOC容器我把类A交给你控制了;最后,建立类B,然后通过IOC容器调用类A的a方法。代码如下:

/**
 * 首先,建立类A作为IOC容器中的一个bean
 */
public class A {
	public void a() {
		System.out.println("output letter a!");
	}
}
<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 其次,将类A注册到配置文件中,告诉IOC容器我把类A交给你控制了 -->
    <bean id="A" class="springDemo.bean.A">
    </bean>
</beans>
/**
 * 最后,建立类B,然后通过IOC容器调用类A的a方法
 *
 */
public class B{
	public static void b() {
		BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-Config.xml"));//告诉IOC容器配置文件是哪个
		A aTest= (A) bf.getBean("A");//IOC容器根据配置文件选择类A并为其创建实例,然后让b方法调用a方法
		System.out.println("gonna use function a()!");   
		aTest.a();
	}

    对于第四个问题,到底变了什么。我们从代码里可以看到,类B在调用方法a的时候,再也不用对类A进行实例化了,只需要让IOC容器去给它所需要的bean就可以了,而那些对象创建等繁杂的工作都交给了IOC容器去执行;同时,类A的对象的生命周期也是由IOC容器进行管理,与类B已经不相干了。也就是说,当类B销毁之后,类A不一定销毁,如果又出现了一个类C也需要调用a方法,只需要再告诉IOC容器去给它提供就可以了。我们可以得出,IOC容器实现了解耦,将所有的对象进行统一管理,降低了我们编码的复杂性,同时也提高了代码的鲁棒性、复用性和可扩展性。

b).依赖注入和依赖查找

    为了实现控制反转,我们可以选择两种方式,一种是依赖查找(Dependency Lookup,DL),一种是依赖注入(Dependency Injection,DI)。二者的根本区别在于对于依赖,对象是主动寻找还是被动接受。依赖查找是对象自己去寻找自己所需要的依赖,例如上文中我们举的类A和类B的例子,类B的方法b需要自己告诉IOC容器它需要哪个bean,让容器给它。而对于依赖注入,则是当这个对象生成的时候,IOC容器将依赖作为属性自动绑定在bean中。spring实现依赖注入主要有三种方式,set方法注入、构造器注入和注解注入。还是上文的例子,这回加一个工具类C,类C依赖于类A,类C有一个方法c,类A的方法a调用方法c。为了实现这个目的,我们需要让类A依赖于类C。

C.class

public class C {
	public void c() {
		System.out.println("output letter C!");
	}
}

set方法注入:

A.class

public class A {
	private C cTest;//被注入的对象C
	//对象C的set方法
	public void setC(C c) {
		this.cTest=c;
	}
	public void a() {
		System.out.println("output letter a!");
	    cTest.c();
	}
}

spring-config.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" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <bean id="A" class="springDemo.bean.A">
    <!-- 将被注入的依赖C作为A的属性写入配置文件中 -->
        <property name="C" ref="C"></property>
    </bean>
    <bean id="C" class="springDemo.bean.C"></bean>    
</beans>

构造器注入:

A.class

public class A {
        private C cTest;
	public A(C c){
        this.cTest=c;
}
	public void a() {
		System.out.println("output letter a!");
	    cTest.c();
	}
}

spring-config.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" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <bean id="A" class="springDemo.bean.A">
       <constructor-arg ref="C"></constructor-arg>
    </bean>
    <bean id="C" class="springDemo.bean.C"></bean>    
</beans>

注解注入:

spring-config.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" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <context:annotation-config/>
    <bean id="A" class="springDemo.bean.A"/>
    <bean id="C" class="springDemo.bean.C"/>
</beans>

A.class

@Component
public class A {
    @Autowired
    C cTest;
	public void a() {
		System.out.println("output letter a!");
	    cTest.c();
	}
}

      从上述代码中,我们可以体会到三种注入方式的区别和联系,通过配置文件告诉IOC容器我们需要注入的依赖是什么,然后再需要注入的地方写好对应的成员变量等,为要注入的依赖提供一个注入的切点。我们也可以看到依赖注入和依赖查找的显著区别,依赖查找是主动向IOC容器进行查找所需要的依赖,而依赖注入则是随着对象的创建,动态初始化依赖并注入到相应对象中,这是一个被动的过程。


2).面向切面编程

     与IOC相应的spring的第二大特性AOP,即面向切面编程,它是面向对象编程的一个补充。由于接触的较少,目前为止我只能从理论的角度进行说明,随着接下来的学习,将对AOP的实现机制进行更为深入的理解。面向对象编程实现了业务模块的分离,是对业务横向的解剖,但是如果当我们的几个模块都需要同一个模块工具的时候,我们需要将每一个模块进行重复性的修改,这无疑为开发带来了不便。面向切面编程应运而生。AOP能够让我们在需要的时候,同时为多个模块增加相同的功能,就像对一整个切面进行了修改。笼统的说,AOP能够在不同的时间点,在不同的位置,插入指定方法。目前对于AOP的理解太浅显,接下来还需要在学习工作中重点关注和总结。

2.spring整体架构

   

   书中介绍,spring框架可以分为5个模块,分别是

  1. Data Access/Intergration
  2. web
  3. CoreContainer
  4. Test
  5. AOP

1).Data Access/Intergration(数据交互模块)

  • JDBC模块提供了一个JDBC抽象层
  • ORM模块提供了对象-关系映射API
  • OXM模块提供了对象-XML映射的抽象层
  • JMS模块包含制造和消费消息的实现
  • Transaction则提供了事务管理1).Data Access/Intergration(数据交互模块)

2).Web(Web上下文模块)

  • web模块提供了面向web的集成特性
  • servlet模块包含spring mvc的实现
  • portlet模块提供了用于portlet环境和web-servlet的mvc实现

3).CoreContainer(核心容器模块)

  • Beans模块包含spring框架的IOC实现、对bean的创建管理等所有的类
  • Core模块包含了spring框架的通用工具类
  • Context模块包含了spring的上下文环境
  • Expression Language模块提供了spring的表达式语言处理

4).Test(测试模块)

    提供了对spring组件进行测试的相关工具

5).AOP(面向切面编程模块)

  • AOP模块提供了spring对面向切面编程的实现
  • Aspects模块提供了对AspectJ的支持
  • Instrumentation模块提供了一些类级的支持,包括类加载器等,该模块主要用于服务器
    以上只是对spring框架的各个模块进行一个大致的介绍,在接下来的学习中,会对每一个模块进行深入的学习和理解。

猜你喜欢

转载自blog.csdn.net/qq_21399231/article/details/80648868