Spring学习笔记(三)

来源:互联网 发布:qq群优化排名 编辑:程序博客网 时间:2024/06/04 19:30

九、AOP

关于AOP(面向切面编程),我们可能都不是很陌生,它是继OOP之后的又一重要编程思想。AOP专注于编程过程中的某一方面事务,将这些事务切分成为更细的切面,于是可以实现解耦,使每一层之间变成松耦合的结构。AOP刚提出的时候,由于太难理解,应用不是很广泛,而Spring框架对它进行了一个最好的实现和诠释,从而使得AOP这种变成思想为更多人所熟知,另外一方面,AOP也成为Spring的核心技术和独特之处。今天就学习一下Spring中的AOP。
(1)代理
由于AOP的概念太过抽象,所以就使得它很难理解,但是我们可以试着用一句话来总结一下,AOP就是:用代理对象实现代码的增强。什么意思呢?举个例子。想象一个业务场景,就是DAO层存储用户信息,我们可能需要做如下工作:权限验证,开启事务,操作数据库,提交事务。对于程序员来说,只有权限验证和操作数据库才是最重要的部分,而开启事务和提交事务,其实代码是固定的,但是每次都需要重新写一遍,这样就很麻烦。那么我们可以怎么做呢?假设一个简单的情况,我们会用一个类来实现权限验证,用另一个类来操作数据库,然后再用一个类把事务的开启和提交抽取出来,为了使我们专注于最重要的业务逻辑,我们可以创造一个代理对象,将用户写好的几个方面的事务组合在一起,最后我们真正得到的就是那个代理对象。于是,从这个例子里我们可以发现操作数据库的类是我们的目标类,而权限验证和事务管理的类分别只负责了整个业务过程中的某一个方面,于是这两个类就被叫做“切面”,它们拥有的方法被叫做“通知”。那么接下来,我们就来实现以下这个例子。为了使我们能专注于AOP本身,也让代码量更小,我就不使用hibernate操作数据库了,转而输出一句话来模拟操作数据库操作。
第一步,写各种负责事务的类。

//1、TeacherDao 接口package com.tt.jdkproxy;public interface TeacherDao {    public void saveTeacher();    public void deleteTeacher();}
//2、TeacherDao 接口的实现类TeacherDaoImpl package com.tt.jdkproxy;public class TeacherDaoImpl implements TeacherDao {    public void deleteTeacher() {        // TODO Auto-generated method stub        System.out.println("delete teacher");    }    public void saveTeacher() {        // TODO Auto-generated method stub        System.out.println("save teacher");    }}
//3、事务管理类package com.tt.jdkproxy;public class Transaction {    public void beginTransaction(){        System.out.println("begin transaction");    }    public void commitTransaction(){        System.out.println("commit transaction");    }}

第二步,也是最重要的一步,写一个代理类,通过它将各个类组合在一起,实现代码的增强。

package com.tt.jdkproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class TeacherInterceptor implements InvocationHandler{    private Object target;    private Transaction transaction;    public TeacherInterceptor(Object target,Transaction transaction){        this.target = target;        this.transaction = transaction;    }    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        // TODO Auto-generated method stub        this.transaction.beginTransaction();        method.invoke(target, args);        this.transaction.commitTransaction();        return null;    }   }

通过这段代码我们可以发现,写一个代理类大致是这么几步:1、实现InvocationHandler接口;2、保存目标类(在这个例子里就是PersonDaoImpl);3、写一个带参数的构造方法;4、实现invoke方法,并在这个方法中将各种功能组合在一起,实现完整功能。
第三步,写个测试类来测试一下吧!

package com.tt.jdkproxy;import java.lang.reflect.Proxy;import org.junit.Test;public class TestProxy {    @Test    public void testProxy(){        Object target = new TeacherDaoImpl();        Transaction transaction = new Transaction();        TeacherInterceptor teacherInterceptor = new TeacherInterceptor(target,transaction);        TeacherDao teacherDao  = (TeacherDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), teacherInterceptor);        teacherDao.saveTeacher();    }}

于是通过这种方式我们就得到了PersonDaoImpl的代理对象,而在PersonDaoImpl类中则不需要再关心关于事务的事情,专心编写操作数据库的代码即可。

实现了这个例子之后我们会问,这是我们自己手动实现的“AOP”,那么Spring是如何实现AOP的呢?接下来我们就用Spring来改造一下之前的那个例子。
第一步,写配置文件:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:aop="http://www.springframework.org/schema/aop"     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-2.5.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd           http://www.springframework.org/schema/context           http://www.springframework.org/schema/context/spring-context-2.5.xsd"><bean id="teacherDao" class="com.tt.jdkproxy.spring.TeacherDaoImpl"></bean>      <bean id="transaction" class="com.tt.jdkproxy.spring.Transaction"></bean>      <aop:config>        <aop:pointcut expression="execution(* com.tt.jdkproxy.spring.TeacherDaoImpl.*(..))" id="teacher"/>        <aop:aspect ref="transaction">            <aop:before method="beginTransaction" pointcut-ref="teacher"/>            <aop:after-returning method="commitTransaction" pointcut-ref="teacher"/>        </aop:aspect>      </aop:config></beans>

我们看到,在配置文件中做的最重要的几件事是:1、把所有的bean放入Spring容器中,2、配置<aop:config> </aop:config> 信息。

第二步,修改测试类,测试类代码如下:

package com.tt.jdkproxy.spring;import java.lang.reflect.Proxy;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestProxy {    @Test    public void testProxySpring(){        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        TeacherDao teacherDao = (TeacherDao)context.getBean("teacherDao");        teacherDao.saveTeacher();    }}

对比之下,我们就可以发现前后两个测试类的区别,其一是可以直接get对象,这是因为创建对象这件事由Spring框架完成了,其二,最重要的是我们发现创建代理对象的代码没有了,但是TeacherDao teacherDao = (TeacherDao)context.getBean("teacherDao"); 这样得到的对象却还是代理对象,这是怎么回事呢?于是总结一下,我们就可以发现在由Spring实现的AOP的过程是这样的:
第一,我们自己写好负责具体事务的类(TeacherDaoImpl和Transaction),并通过配置文件的配置将它们放入Spring容器中。
第二,在配置文件中配置好各个切面。
第三,Spring容器帮我们把各个切面组合在一起,创建一个代理对象来实现AOP。

这样一路分析下来,我们就可以对Spring的AOP有一个比较清晰的认识了。

0 0