Spring AOP的实现原理与应用场景

概述

AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。
AOP(Aspect-OrientedProgramming,面向方面编程):是OOP的补充和完善。OOP引入了封装、继承、多态性等建立一种对象层次结构(从上到下的关系)。当需要为分散的对象引入公共行为的时候(从左到右的关系),OOP就显得无能为力。例如:日志功能。日志代码往往水平的散步所有对象层次中,与对象的核心功能毫无关系。这种代码被称为横切(cross-cutting)代码还有像安全性、异常处理、透明的持续性等都称为横切代码。在OOP设计中,它们导致了大量代码的重复,不利于模块的重用。

AOP与OOP相反,利用“横切”技术将影响多个类的公共行为封装到一个可重用模块,称为Aspect。简单点,就是将那些与业务无关,却被业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

实现原理

Spring中AOP底层的实现其实是基于JDK的动态代理和cglib动态创建类进行动态代理来实现的,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。**

基于JDK的动态代理的原理是:
需要用到的几个关键成员
InvocationHandler (你想要通过动态代理生成的对象都必须实现这个接口)
真实的需要代理的对象(帮你代理的对象)
Proxy对象(是JDK中java.lang.reflect包下的)

下面是具体如何动态利用这三个组件生成代理对象

(1)首先你的真是要代理的对象必须要实现InvocationHandler 这个接口,并且覆盖这个接口的invoke(Object proxyObject, Method method, Object[] args)方法,这个Invoker中方法的参数的proxyObject就是你要代理的真实目标对象,方法调用会被转发到该类的invoke()方法, method是真实对象中调用方法的Method类,Object[] args是真实对象中调用方法的参数

(2)然后通过Proxy类去调用newProxyInstance(classLoader, interfaces, handler)方法,classLoader是指真实代理对象的类加载器,interfaces是指真实代理对象需要实现的接口,还可以同时指定多个接口,handler方法调用的实际处理者(其实就是帮你代理的那个对象),代理对象的方法调用都会转发到这里,然后直接就能生成你想要的对象类了。

AOP的概念和定义

Aspect(切面):一种跨多对象的横向模块化关系。切面作为顾问和拦截应用于Spring中
Joinpoint(连接点):一个连接点总是代表一个方法执行。
Advice(通知):被AOP框架使用的特定接合点。不同的通知类型包括:“around”,"before"和"throws"通知。通知类型在下面介绍,通知模块就如同一个拦截器,维持一系列拦截器围绕着连接点。
Pointcut(切入点):当一个通知将被激活的时候,会指定一些连接点。切入点(切入点)是一个匹配连接点的断言或者表达式
AOP proxy(AOP代理):Spring提供了两种方式生成代理对象:JDKProxy和Cglib具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
Weaving(织入):聚合切面以生成一个通知对象。这可以在编译时(比如使用AspectJ编译器)或在运行时完成。Spring在运行时完成编织。
Introduction(引入):向一个通知类中添加方法或成员变量。Spring允许你向任何通知类中引入新接口。例如,你可能会使用一个引入,以使得任何实现IsModified接口的对象简单的缓存。
Target object(标签对象):包含接合点的对象。也被通知对象或者代理对象所引用。

通知类型
通知(advice)主要有以下5种类型:

前置通知(Before Advice): 在连接点之前执行的Advice,不过除非它抛出异常,否则没有能力中断执行流。使用 @Before 注解使用这个Advice。
返回通知(After Retuning Advice): 在连接点正常结束之后执行的Advice。例如,如果一个方法没有抛出异常正常返回。通过 @AfterReturning 关注使用它。
抛出(异常)后执行通知(After Throwing Advice): 如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。通用 @AfterThrowing 注解来使用。
后置通知(After Advice): 无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行在结束后执行这些Advice。通过 @After 注解使用。
围绕通知(Around Advice):通知围绕一个接合点(如一个方法调用)。这是最强有力的通知类型。围绕通知将会在方法调用之前或之后完成自定义行为。他们能够选择是否通过接合点,或者返回他们自己特定的值,甚至抛出异常。通过 @Around 注解使用。

使用方式举例

首先,我们定义一个接口Sleepable和这个接口的实现Human,代码如下:

public interface Sleepable {
    public void sleep();
}

public class Human implements Sleepable {

    @Override
    public void sleep() {
        System.out.println("我要睡觉了!");
    }
}

1.< aop:aspect>:定义切面(切面包括通知和切点)时,只需要定义一般的bean就行
< aop:aspect>大多用于日志,缓存

/定义切面
public class SleepHelperAspect{
    public void beforeSleep(){
        System.out.println("睡觉前要脱衣服!");
    }

    public void afterSleep(){
        System.out.println("起床后要穿衣服!");
    }
}
//aop配置
<bean id="sleepHelperAspect" class="com.ghs.aop.SleepHelperAspect"></bean>

<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:aspect ref="sleepHelperAspect">
<!--前置通知-->
<aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/>
<!--后置通知-->
<aop:after method="afterSleep" pointcut-ref="sleepPointcut"/>
</aop:aspect>
</aop:config>

<bean id="human" class="com.ghs.aop.Human"/>

2.< aop:advisor>:定义通知器(通知器跟切面一样,也包括通知和切点)时,通知必须实现Advice接口。< aop:advisor>大多用于事务管理。

//定义通知
public class SleepHelper implements MethodBeforeAdvice,AfterReturningAdvice{
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2)
            throws Throwable {
        System.out.println("睡觉前要脱衣服!");
    }

    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2,
                               Object arg3) throws Throwable {
        System.out.println("起床后要穿衣服!");
    }
}

//aop配置
<bean id="sleepHelper" class="com.ghs.aop.SleepHelper"></bean>

<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:advisor advice-ref="sleepHelper" pointcut-ref="sleepPointcut"/>
</aop:config>

<bean id="human" class="com.ghs.aop.Human"/>

事务管理配置

<!-- 会重复读,不会脏读事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" timeout="120" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>

<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="..."/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
发布了29 篇原创文章 · 获赞 0 · 访问量 1641

猜你喜欢

转载自blog.csdn.net/glamour2015/article/details/104448542