Spring框架核心理论 AOP|DI

来源:互联网 发布:若水堂淘宝 编辑:程序博客网 时间:2024/06/08 02:34

利用Spring和Hibernate框架做工程也有一段时间了;

现在对于Spring的核心 AOP(Aspect Oriented Programming)和 DI(Dependency Injection), 即 面向切面编程 依赖注入做一个简单的自己的理解。如果理解的不到位,希望和大家多多讨论交流。

1. AOP(Aspect Oriented Programming 面向切面编程)

说AOP之前,首先简单说下为什么会有AOP这个东西的出现。我们都知道当你设计一个系统的时候,会有很多的功能模块,而具体功能模块都涵盖各自的类和方法;举个例子,现在要设计一个模拟晚会的系统,如果按照功能模块的思想,就应该是按照节目的类型(比如音乐类型,小品类型,相声类型,舞蹈类型等)生成不同的接口或者类,具体每个节目只需要去继承相应的接口或类即可。但是如果你具体去实现这些方法时候就会发现,有很多方法其实是所有节目都通用的,比如说在节目开始前,观众需要入座,节目结束后,观众需要进行反馈(鼓掌或者鼓倒掌);所以我们就可以想象成这是一个横穿过所有功能模块(类)的一个切面,如下图。 这样整个系统就可以被划分为核心关注点横切关注点。所以我们发现AOP其实就是OOP(Object-Oriented Programing,面向对象编程)的一个补充完善。

切面

要具体了解AOP,首先要知道几个术语:

  • Advice (通知) :很简单,就是上图中切面所要完成的工作,其中包含5中类型的通知:
    Before——在方法被调用之前调用通知。
    After——在方法完成后调用通知,无论方法执行是否成功。
    After-returning——在方法成功执行后调用通知。
    After-throwing——在方法抛出异常后调用通知。
    Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

  • Join point(连接点):是应用执行过程中能够插入切面的一个点,这个点会有很多,比如调用方法时、抛出异常时、甚至修改字段时,切面代码可以利用这些点插入到应用的正常流程中。

  • Point cut(连接点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。

  • Aspect(切面): point cut和advice结合起来就是aspect,就是我们图中这样一个切面,它类似于一个类,但它代表的更多是对象间横向的关系。

  • introduce(引入): 它允许我们向现有的类添加新的方法或属性。

  • Weaving(织入): 它是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以织入。
    编译期——切面在目标类编译时被织入,这种方式需要特殊的编译器,比如AspectJ。
    类加载期——切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5 的 LTW(load-time weaving)支持以这种方法织入切面,但这种织入方式违背了POJO的意愿,效率高,但是也要付出代价。
    运行期——切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方法织入切面的。

    至于Spring具体的AOP实现,可以使用注解和xml的方法。这方面会在之后的博客中写到,这里只是做一个大概的理论描述。

AOP的优势在于传统的很多设计模式都是利用类的多态,继承,接口对每个组件的责任进行分离,从而能设计出灵活、可扩展、可重用的组件。然而由于对象封装的特殊性,“设计模式”的只能在接口与抽象中大做文章,而对于对象内部则无能为力,可以抽象的理解成从上到下。AOP则可以深入到对象的内部中去,从左至右,彻底分离出核心关注点和横切关注点,让专注于某一功能的工作能够更深入,而不必花费大量心力在其他横切专注点上,比如日志管理,每一个功能方向的专家都能够根据自己的需求处理日志,不需要重复地改写日志。

从上面对AOP的描述,相信应该已经对AOP有了一个大致的了解,总之使用AOP能够大大降低代码的冗余性和组件之间的耦合性,减少开发成本,让开发人员能够专注于具体功能的逻辑设计,适合于大型的软件系统设计和开发。

2. DI (Dependency Injection 依赖注入)

其实依赖注入并不复杂,它能让代码变得更简单,更容易理解,并且易于测试,降低耦合性。

首先说明一下耦合:耦合是有两面性的;一方面,紧密耦合的代码难以测试,理解和复用;另一方面,一定程度的耦合也是必须的。不同的类之间必须要以适当的方式进行交互,不然什么也做不了。

所以我们发现类之间的依赖是必须的,主要是对于依赖关系比较复杂,就会显得代码非常复杂;从而引入了依赖注入。

依赖注入的基本原则是:应用的类不应该负责查找其协作对象。配置对象的工作交给Spring来负责,即Spring来负责管理类之间的这个依赖协作关系。

通过依赖注入,对象的依赖关系将由负责协调系统中各个对象的第三方组件在创建对象时设定。对象无需自行创建或管理他们的依赖关系——依赖关系将被自动注入到需要它们的对象中去。

Spring提供注入的方式有三种:构造器注入,setter注入和接口注入,因为现在已经很少使用接口注入,所以主要说下构造器注入和setter注入。

  • 构造器注入: 其实很简单,即在某个类中实现构造方法,要注入的类作为参数传入;
package cn.glzaction.service.impl;import java.util.List;import cn.glzaction.service.interfaces.PersonDaoIF;import cn.glzaction.service.interfaces.PersonServiceIF;public class PersonServiceBean implements PersonServiceIF{//自定义类    private PersonDaoIF personDaoBean;//String类型    private String name;//集合类型      private List list;//构造器       public PersonServiceBean(PersonDaoBean personDaoBean,String name,List list){        this.personDaoBean = personDaoBean;        this.name = name;        this.list = list;    }//方法,用于显示    public void display(){        personDaoBean.add();        System.out.println(name);        System.out.println(list);    }}

相应spring的配置文件:

<bean id="personDao" class="cn.glzaction.service.impl.PersonDaoBean"></bean ><!--构造器方式注入--><bean id="personService" class="cn.glzaction.service.impl.PersonServiceBean">    <constructor-arg index="0" type="cn.glzaction.service.impl.PersonDaoBean" ref="personDao"/>    <constructor-arg index="1" type="java.lang.String" value="glzaction"/>    <constructor-arg index="2" type="java.util.List">        <list>            <value>list1</value>            <value>list2</value>            <value>list3</value>        </list>    </constructor-arg></bean>

coustructor-arg 是构造器标签元素,index是可选参数,type也是可选参数,如果注入的是对象,就用ref;如果注入的是基本类型或String,就用value;

  • setter方式注入: 就是通过setXX的方式注入;
package cn.glzaction.service.impl;import cn.glzaction.service.interfaces.PersonDaoIF;import java.util.*;public class PersonDaoBean implements PersonDaoIF {    private String name;    private Integer id;    private List list;    private Map map;    public void setName(String name) {        this.name = name;    }    public void setId(Integer id) {        this.id = id;    }    public void setList(List list) {        this.list = list;    }    public void setMap(Map map) {        this.map = map;    }    @Override    public void add() {        // TODO Auto-generated method stub        System.out.println(map);        System.out.println(list);        System.out.println(id);        System.out.println(name);    }}

相应spring的配置:

<bean id="personDao" class="cn.glzaction.service.impl.PersonDaoBean">    <property name="name" type="java.lang.String" value="glzaction"/>    <property name="id" type="java.lang.Integer" value="1"/>    <property name="list" type="java.util.List">        <list>            <value>list1</value>            <value>list2</value>            <value>list3</value>        </list>    </property>    <property name="map" type="java.util.Map">        <map>            <entry key="key1" value="value1"></entry>            <entry key="key2" value="value2"></entry>        </map>    </property></bean>

setter注入采用的是property标签元素。

基于Spring框架,两种注入方法,setter注入方法比较常用。


综上,基本介绍了下Spring的两个核心的理论:依赖注入和面向切面编程;他们都很好的降低了代码的冗余性,减少组件之间的耦合;后期会具体介绍AOP的一些代码实现。

希望大家多多交流指正!


该文引用了《Spring实战》的部分内容和http://glzaction.iteye.com/的依赖注入代码。

0 0