Spring框架——AOP代理

来源:互联网 发布:enjoy it 编辑:程序博客网 时间:2024/05/22 03:16

我们知道AOP代理指的就是设计模式中的代理模式。一种是静态代理,高效,但是代码量偏大;另一种就是动态代理,动态代理又分为SDK下的动态代理,还有CGLIB的动态代理。Spring AOP说是实现了AOP联盟定制的标准化接口,也就是说人家的AOP做得很规范,很国际化。我也看了别人写的,Spring框架下的AOP内容确实很多,不过我争取长话短说,把文章篇幅控制好,并且条理清楚一些。

注:本文只关注AOP的切入点,不会涉及任何实战内容,关于实战的内容可以看我的其它文章,O(∩_∩)O~

AOP核心概念

虽然我们自己写AOP会轻松很多,但是Spring框架下,对AOP做了非常细致的划分,因此对概念要有一定的了解。

aop_a

1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
就如图所示,程序流程会按顺序执行,而切面就像一个平面,在这个流程中间插入。
3、连接点(joinpoint)
被拦截到的点,它可以是程序执行过程中的任意一点,而AOP主要做的是方法上的拦截。
4、切入点(pointcut)
对连接点进行拦截的定义,切面与流程的交点。
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

AOP的增强分类

在上一篇文章,接触到了增强这个词,增强指的就是代理,指的是在程序流程中,嵌入一些新的内容。通常认为Spring支持5种类型增强,不过我觉得得重新定义一下比较好,我认为应当是3种增强,2种拦截。

前置增强:org.springframework.aop.BeforeAdvice代表前置增强,不过BeforeAdvice是一个空接口,MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强。

后置增强:org.springframework.aop.AfterReturningAdvice代表后增强,表示在目标方法执行后实施增强。

异常抛出增强:org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强。

环绕增强(方法拦截):org.aopalliance.intercept.MethodInterceptor代表环绕增强,对于这个增强,我有自己的看法,从直译上看,它应该叫方法拦截,也就是说实现这个接口,可以不让我们的方法执行。

引介增强:org.springframework.aop.IntroductionInterceptor代表引介增强,IntroductionInterceptor的方法只有一个Class参数,不好使用,实际使用可以直接继承DelegatingIntroductionInterceptor类。它也是一种拦截方法,可以使用表示在目标类中添加一些新的方法和属性。

源码

环绕增强(拦截器)

这个和传统的JDK代理和CGLIB代理最类似,这里你可以获取到你需要调用的方法,你要是想放弃使用前置增强和后置增强,将它们的代码全部放在这里执行,这也是可以的。

具体的使用呢,就比如说:你要做一件很耗内存的操作,执行那个操作之前,你要判断内存容量,要是内存不足了,就放弃对方法的使用,这样的代码逻辑就可以放到这里。

/** * 这是一个拦截器,在某些情况下,选择放弃对方法的使用,其实在这里编写前置代理和后置代理也可以,但是Spring既然提供了其他接口,参照接口实现更加规范 * @author ChenSS * @2017年1月1日 */public class MyMethodInterceptor implements MethodInterceptor{    @Override    public Object invoke(MethodInvocation dao) throws Throwable {        System.out.println("MyMethodInterceptor执行");        // TODO 方法执行前要做的事情        //如果说,不执行下面这一行代码,则目标方法将不执行,其他的增强效果也全部失效        Object result= dao.proceed();        // TODO 方法执行后要做的事情        return result;    }}

前置代理

/** * 前置增强,也就是说这些代码会在你需要调用的那个方法之前执行 * @author ChenSS * @2017年1月1日 */public class MyMethodBeforeAdvice implements MethodBeforeAdvice{    @Override    public void before(Method method, Object[] args, Object target) throws Throwable {        System.out.println(method.getName()+"方法执行之前");    }}

后置增强

/** * 后置增强,这些代码会在你需要调用的那个方法之后执行 * @author ChenSS * @2017年1月1日 */public class MyAfterReturningAdvice implements AfterReturningAdvice{    @Override    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {        System.out.println(method.getName()+"方法执行结束");    }}

异常抛出增强

/** * 这个接口实际是一个空的接口,但是方法体还是得参照规范写,具体捕捉什么异常要写清楚 * @author ChenSS * @2017年1月1日 */public class MyThrowsAdvice implements ThrowsAdvice{    /**     * 笼统地使用了Exception,可以做具体的异常捕捉      */    public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {        System.out.println(method.getName()+"出现异常");    }}

引介增强

/** * 这个呢,也是一种拦截方法,也可以拦截方法的执行, * @author ChenSS * @2017年1月1日 */public class MyIntroductionInterceptor extends DelegatingIntroductionInterceptor{    private static final long serialVersionUID = 2582891122340903718L;    public Object invoke(MethodInvocation mi) throws Throwable {        System.out.println("引介增强启动拦截");        return super.invoke(mi);    }}

测试函数

需要被代理的类,还是我最喜欢的Dao。

/** * 测试用接口 * @author ChenSS * @2017年1月1日 */public interface BaseDao {    public long queryId() ;    public String queryName();}/** * 测试用Dao * @author ChenSS * @2017年1月1日 */public class UserDao implements BaseDao{    public long queryId() {        System.out.println("queryId方法执行中");        System.out.println("故意制造一个算术异常");        return 2/0;    }    public String queryName() {        System.out.println("queryName方法执行中");        return "xiaoming";    }}

主函数

/** * 测试函数 *  * @author ChenSS * @2017年1月1日 */public class Test {    public static void main(String[] args) {        ProxyFactory factory = new ProxyFactory();        // 把我们写的四种代理全部放进去        factory.addAdvice(new MyMethodInterceptor());        factory.addAdvice(new MyMethodBeforeAdvice());        factory.addAdvice(new MyAfterReturningAdvice());        factory.addAdvice(new MyThrowsAdvice());        // 引介增强比较特殊,需要提供接口        factory.addAdvice(new MyIntroductionInterceptor());        factory.setInterfaces(BaseDao.class.getInterfaces());        // 需要代理的那个类        factory.setTarget(new UserDao());        try {            UserDao dao = (UserDao) factory.getProxy();            System.out.println(dao.queryName());            System.out.println();            System.out.println(dao.queryId());        } catch (Exception e) {            // TODO: handle exception        }    }}

测试结果如下:

aop_b

AOP的实战案例介绍

这一部分的内容是后面补充的内容,关于XML配置AOP代理这一块暂时就没办法介绍了。使用XML配置AOP的话,还需要两个Jar包(版本号仅供参考):aopalliance-1.0.jar、aspectjweaver-1.8.3.jar。它主要关注的是方法名,根据方法名做适当的拦截和增强。

AOP典型的使用案例大体有这两个:后台的日志打印、数据库的事务支持。关于日志,大家可以去看一下log4j;至于数据库事务,我给出Hibernate整合Spring的配置文件。

<?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:aop="http://www.springframework.org/schema/aop"    xmlns:tx="http://www.springframework.org/schema/tx"    xsi:schemaLocation="http://www.springframework.org/schema/beans                        http://www.springframework.org/schema/beans/spring-beans.xsd                        http://www.springframework.org/schema/tx                        http://www.springframework.org/schema/tx/spring-tx.xsd                        http://www.springframework.org/schema/aop                        http://www.springframework.org/schema/aop/spring-aop.xsd">    <!-- 配置SessionFactory -->    <bean id="sessionFactory"        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">        <property name="configLocation" value="hibernate.cfg.xml" />    </bean>    <!-- 配置一个事务管理器 -->    <bean id="transactionManager"        class="org.springframework.orm.hibernate4.HibernateTransactionManager">        <property name="sessionFactory" ref="sessionFactory" />    </bean>    <!-- 事务管理 -->     <aop:config>         <aop:pointcut id="txMethod" expression="execution(* com.dao.impl.*Dao.*(..))" />        <aop:advisor advice-ref="txAdvice" pointcut-ref="txMethod" />    </aop:config>    <!-- AOP切面声明事务管理 -->    <tx:advice id="txAdvice" transaction-manager="transactionManager">        <tx:attributes>            <tx:method name="save*" propagation="REQUIRED" />            <tx:method name="update*" propagation="REQUIRED" />            <tx:method name="add*" propagation="REQUIRED" />            <tx:method name="delete*" propagation="REQUIRED" />            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />            <tx:method name="*" />        </tx:attributes>     </tx:advice></beans>  

发现研究框架花了太多时间了,原计划年前将Spring框架写清楚,但是发现内容真的好多啊,暂时也只能写到这了,之后我得回归安卓了,做一些安卓的研究,这些天有点荒废安卓了。

各位新年快乐哈,不过文章发表似乎已经1月2号了,O(∩_∩)O哈哈~

0 0