Spring框架系列(一)-整体架构

来源:互联网 发布:qq windows phone版 编辑:程序博客网 时间:2024/05/21 09:24

附上示例程序的github地址:https://github.com/bjtudujunlin/SpringDataExample


一、Spring设计目标

Spring设计的初衷在于提供一套轻量级的应用开发框架,解决开发者在应用开发中的共性问题。这句话有两个关键字,一是“轻量级”,二是“共性问题”。为啥叫“轻量级”呢,Spring框架本身不能给你解决业务问题,也没有相关库,只是提供了一个框架,让你的程序更加健壮、易于维护,你看到的Spring MVC、Spring Data之类的都是基于Spring框架的组件,不属于Spring框架内容。那么“共性问题”有啥呢,耦合,还是耦合,日常程序开发中,有两类耦合问题经常遇见,一是类与类关系的耦合,软件开发中,类与类的“组合”关系普遍存在,比如B类有属性B1是A类的实例,在B中需要对B1进行初始化操作,Spring中的IOC容器提供了配置文件的方式对B类进行管理,包括初始化操作,配置属性B1的值等等。二是服务与服务关系的耦合,比如现在有一个数据库操作类C,现在我们想为这个类加上权限检测的功能,只有具有特定权限的人群才能调用数据库操作类的方法,一般处理是在类C中调用权限检测方法,这样就导致了服务之间的耦合,违背了“单一职责原则”,Spring提供了AOP方法,以非侵入式的方式解决这种耦合问题,保持了服务的独立性,又满足了业务的需求。

二、Spring整体架构

Spring整体架构如下图所示,核心模块是Spring IOC和Spring AOP模块,其它的模块都是基于这两个核心模块进行开发,然后以组件的方式集成进来的。这里先重点介绍核心的IOC和AOP模块,其它组件留在后面的组件环节进行详细介绍。


图 1

 

三、Spring核心模块

1.       IOC模块

IOC模块其实是一个IOC容器,提供了对Bean进行管理的功能,依赖于这个功能,解决了类与类关系的耦合。下图比较适合解释IOC容器。由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。


图 2

 

 

有了前面的基础,我们再来看看IOC(Inversion of Control)——控制反转的意思。为啥叫控制反转呢?因为它涉及到控制权在谁手里的问题,举个例子,引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在A自己手上。那么引入IOC容器之后呢,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。可以看见创建对象B的控制权由A转移到了IOC容器,控制权反转了。

还有一个平时容易混淆的概念DI(Dependence Injection)——依赖注入,这个和IOC有什么关系呢。前面我们说了IOC容器会主动创建一个对象B注入到对象A需要的地方,DI其实就是IOC实现控制反转的方法。

下面举一个IOC具体的例子,这里先采用XML的方式,这种方式比较复杂,但是能更好的理解spring的IOC配置,后续会补充java配置方式。

a)      建立maven的java工程,并添加spring-core的依赖

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-core</artifactId>

      <version>4.3.3.RELEASE</version>

</dependency>

b)      建立Person类

public class Person {

    private Stringname;

    private Stringage;

    private Regionregion;

    public String getName() {

       returnname;

    }

 

    public void setName(String name) {

       this.name =name;

    }

 

    public String getAge() {

       returnage;

    }

 

    public void setAge(String age) {

       this.age =age;

    }

 

    private Stringdiscribe;

   

    public Person(Regionregion,Stringdiscribe){

       this.region =region;

       this.discribe =discribe;

    }

   

    public void Introduce(){

       System.out.println("I am " +name +",I comefrom " +region +"my age is" +age);

       System.out.println(discribe);

    }

}

c)       建立Region类

public class Region {

    private Stringprovince;

    private Stringcity;

   

    public StringgetProvince() {

       returnprovince;

    }

 

    public void setProvince(String province) {

       this.province =province;

    }

 

    public String getCity() {

       returncity;

    }

 

    public void setCity(String city) {

       this.city =city;

    }

 

    @Override

    public String toString(){

       returnprovince +" " +city;

    }

}

 

d)      在resource下面新建spring的xml配置文件spring-config.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="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

           http://www.springframework.org/schema/aop/spring-aop.xsd

           http://www.springframework.org/schema/context">

<!-- 配置细节 -->

 

<!-- bean如果没有指定ID,会生成默认ID,形式为类名#0”0是计数 -->

<beanid="person"class="iscas.springstudy.Person">

<!--     配置构造函数,按照配置函数从左到右的顺序,ref标签表示参数为指定idbeanvalue标签表示参数值 -->

    <constructor-argref="region-a"></constructor-arg>

    <constructor-argvalue="a funny man"></constructor-arg>

<!--    配置属性,name对应属性名,value对应属性值,属性中也可以右ref标签,跟构造函数是一样的意思

        需要注意的是配置属性要求该属性必须右getter方法和setter方法-->

    <propertyname="name"value="liming"></property>

    <propertyname="age"value="19"></property>

</bean>

 

<beanid="region-a"class="iscas.springstudy.Region">

    <propertyname="province"value="beijing"></property>

    <propertyname="city"value="haidian"></property>

</bean>

</beans>

e)      加载xml文件,生成beanfactory,获取bean,并调用其中方法

XmlBeanFactory factory =newXmlBeanFactory(newClassPathResource("spring-config.xml"));

        Person person = (Person)factory.getBean("person");

       

person.Introduce();

f)        applicationcontext方式加载xml文件

beanfactory类现在已经过期了,applicationcontext类继承自beanfactory,能够提供比beanfactory更多的支持,所以在看看applicationcontext怎么调用。首先需要在maven中添加applicationcontext依赖。

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-context</artifactId>

              <version>4.3.3.RELEASE</version>

    </dependency>

代码中用applicationcontext取代beanfactory即可,改变如下

ApplicationContext application =newClassPathXmlApplicationContext("spring-config.xml");

       

        Person person = (Person)application.getBean("person");

       

person.Introduce();

2.       AOP模块

在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。下面这个图可以加深理解。


图 3

上图中,CourseService、StudentService和MiscService都属于业务服务,比如CourseService就是课程管理服务。现在需要为CourseService服务加上安全校验功能,传统的做法是创建一个安全类,然后在CourseService服务中需要安全校验的地方进行调用,这样逻辑简单,但是该方式是侵入式的,改变了CourseService服务本身的代码,CourseService服务和安全类耦合起来,比如我以后将CourseService服务换到别的地方去用,要么删除安全功能,要么把安全类也带上。同样的StudentService、MiscService都用到了同样的安全类。这里的安全类就是横切关注点。要解决前面这个侵入式的问题,我们就需要引入AOP的思想,既满足上述功能需求,又是非侵入式的。

理解AOP需要理解以下几个概念:

a)      通知(Advice),切面必须要完成的工作。在AOP术语中,切面的工作被称为通知。Spring切面支持的最小粒度是方法,可以应用5种类型的通知:

前置通知(Before):在目标方法被调用之前调用通知功能;

后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

返回通知(After-returning):在目标方法成功执行之后调用通知;

异常通知(After-throwing):在目标方法抛出异常后调用通知;

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

b)      连接点(Join point),连接点是在应用执行过程中能够插入切面的一个点。比如Spring AOP只能支持到方法粒度,所以类型的方法可以作为连接点,AOP可以在方法上插入切面,监听方法的调用,并触发通知。

c)       切点(Poincut),一个切面并不需要通知应用的所有连接点,选择的连接点就是切点。连接点表示有这个能力,而切点就是已经选择的连接点。

d)      切面(Aspect),切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。

总结起来,AOP无非就干了两件事情,选择连接点作为切点,插入切面对切点进行监听。当切点满足通知条件时,通知触发。

Spring AOP的基本概念到这儿解释完了,考虑AOP内容还有些,放在下一章节结合示例程序叙述。


1 0
原创粉丝点击