Spring学习笔记五: AOP入门

来源:互联网 发布:web php工程师招聘 编辑:程序博客网 时间:2024/06/05 15:11

一、AOP术语

  1. 切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。
  2. 连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。
  3. 通知:切面的实际实现,他通知系统新的行为。如在日志通知包含了实
    现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。
  4. 切入点:定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。
  5. 引入:为类添加新方法和属性。
  6. 目标对象:被通知的对象。既可以是你编写的类也可以是第三方类。

  7. 代理:将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变。

  8. 织入:将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上:
    编译期:切面在目标对象编译时织入.这需要一个特殊的编译器.
    类装载期:切面在目标对象被载入JVM时织入.这需要一个特殊的类载入器.
    运行期:切面在应用系统运行时织入。
    这里写图片描述

二、Spring AOP实现

在spring中所有的通知都是以java类的形式编写的。切入点定义在配置文件中编写,所以切面代码和配置文件对我们来说都很熟悉。
对于其他框架(Aspectj),需要特定的语法编写,如果使用的话,还需学习新的语言。
spring的运行时通知对象
spring在运行期创建代理,不需要特殊的编译器.
spring有两种代理方式:

  1. 若目标对象实现了若干接口,spring使用JDKjava.lang.reflect.Proxy类代理。该类让spring动态产生一个新类,它实现了所需的接口,织入了通知,并且代理对目标对象的所有请求。

  2. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。使用该方式时需要注意:
    2.1对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
    对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。
    2.2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。

spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的结果。

三、创建通知

通知类型 接口 描述 Around MethodInterceptor 拦截对目标方法调用 Before MethodBeforeAdvice 在目标方法调用前调用 After AfterReturningAdvice 在目标方法调用后调用 Throws ThrowsAdvice 当目标方法抛出异常时调用

1、前置通知

public interface MethodBeforeAdvice{       void before(Method m,Object[] os ,Object target)       {         }   }

该接口提供了获得目标方法、参数和目标对象的机会。不能够改变运行时参数,即不能替换参数对象和目标对象。
注意:在方法结束后不返回任何值东西。原因是该接口返回后,目标方法将会被调用,应该返回目标对象的返回值。该接口唯一能阻止目标方法被调用的途径是抛出异常或(System.exit())。

实现代码如下:

package jiankunking.aop;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class MyMethodBeforeAdvice implements MethodBeforeAdvice{    /**     * method: 被调用方法名字     * args: 给method传递的参数     * target: 目标对象     */    public void before(Method method, Object[] args, Object target)            throws Throwable    {        System.out.println("*****************************************"+"");        System.out.println("MethodBeforeAdvice在目标方法调用前调用..." + method.getName());    }}

这里写图片描述
ProxyFactoryBean是一个在BeanFactory中显式创建代理对象的中心类,可以给它一个要实现的接口、一个要代理的目标对象、一个要织入的通知,并且他将创建一个崭新的代理对象。

2、后置通知

同前置通知类似:

public interface AfterReturningAdvice{      public void afterReturning(Object returnValue,Method      m,Object[] os,Object target);  }

实现代码如下:

public class MyAfterReturningAdvice implements AfterReturningAdvice{    /**     * Callback after a given method successfully returned.     *     * @param returnValue the value returned by the method, if any     * @param method      method being invoked     * @param args        arguments to the method     * @param target      target of the method invocation. May be {@code null}.     * @throws Throwable if this object wishes to abort the call.     *                   Any exception thrown will be returned to the caller if it's     *                   allowed by the method signature. Otherwise the exception     *                   will be wrapped as a runtime exception.     */    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable    {        System.out.println("AfterReturningAdvice..." + method.getName());    }}

3、环绕通知

public interface MethodInterceptor extends Interceptor{    Object invoke(MethodInvocation invocation);}

该接口同前两种通知有两个重要区别:

  1. 该通知能够控制目标方法是否真的被调用。通过invocation.proceed()方法来调用。
  2. 该通知可以控制返回的对象。可以返回一个与proceed()方法返回对象完全不同的对象。但要谨慎使用。

实现代码如下:

public class MyMethodInterceptor implements MethodInterceptor{    /**     * Implement this method to perform extra treatments before and     * after the invocation. Polite implementations would certainly     * like to invoke {@link Joinpoint#proceed()}.     *     * @param invocation the method invocation joinpoint     * @return the result of the call to {@link Joinpoint#proceed();     * might be intercepted by the interceptor     * @throws Throwable if the interceptors or the target object     *                   throws an exception     */    public Object invoke(MethodInvocation invocation) throws Throwable    {        System.out.println("MethodInterceptor...调用方法前执行" + invocation.getClass());        Object obj = invocation.proceed();        System.out.println("MethodInterceptor...调用方法后执行" + invocation.getClass());        return obj;    }}

4、异常通知

public interface ThrowsAdvice{}

该接口为标识性接口,没有任何方法,但实现该接口的类必须要有如下形
式的方法:

public void afterThrowing(Throwable throwable){}public void afterThrowing(Method m,Object[] os,Objecttarget,Exception throwable){}

第一个方法只接受一个参数:需要抛出的异常。
第二个方法接受异常、被调用的方法、参数以及目标对象。

实现代码如下:

public class myThrowsAdvice implements ThrowsAdvice{    /**     * 对应在 target 执行异常时增强处理     * @param m     * @param os     * @param target     * @param throwable     */    public void afterThrowing(Method m, Object[] os, Object target, Exception throwable)    {        System.out.println("ThrowsAdvice出大事了..." + throwable.getMessage());    }}

5、引入通知(待续)

以前定义的通知类型是在目标对象的方法被调用的周围织入。引入通知给目标对象添加新的方法和属性。

四、定义切入点

如果不能表达在应用系统的什么地方应用通知的话,通知将毫无用处,这就是切入点的用处。切入点决定了一个特定的类的特定方法是否满足一定的规则。若符合,通知就应用到该方法上。

以上4种通知的配置文件:

<?xml version="1.0" encoding="utf-8"?><beans xmlns="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-2.5.xsd">    <!-- 配置被代理的对象 -->    <bean id="test1Service" class="jiankunking.aop.Test1Service">        <property name="name" value="小土豆"/>    </bean>    <!--前置通知-->    <bean id="myMethodBeforeAdvice" class="jiankunking.aop.MyMethodBeforeAdvice"/>    <!--后置通知-->    <bean id="myAfterReturningAdvice" class="jiankunking.aop.MyAfterReturningAdvice"/>    <!--环绕通知-->    <bean id="myMethodInterceptor" class="jiankunking.aop.MyMethodInterceptor"/>    <!-- 配置代理对象 -->    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 代理接口集 -->        <property name="proxyInterfaces">            <list>                <value>jiankunking.aop.TestServiceInterface</value>                <value>jiankunking.aop.TestServiceInterface2</value>            </list>        </property>        <!-- 把通知织入到代理对象  -->        <property name="interceptorNames">            <list>                <!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也可以把通知看出拦截器,struts2核心拦截器 -->                <value>myMethodBeforeAdvice</value>                <!--织入后置通知-->                <value>myAfterReturningAdvice</value>                <!--织入环绕通知-->                <value>myMethodInterceptor</value>                <!--织入异常通知-->                <value>myThrowsAdvice</value>            </list>        </property>        <!-- 配置被代理对象,可以指定 -->        <property name="target" ref="test1Service"/>    </bean></beans>

以上四种通知的具体案例参考demo:Spring Aop 演示demo

是不是很想看一下具体拦截的效果,快点来下载demo吧

1. 正则表达式切入点(RegexpMethodPointcut):

这里写图片描述

2. NameMatchMethodPointcutAdvisor (根据方法名匹配织入)

配置文件:

<?xml version="1.0" encoding="utf-8"?><beans xmlns="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-2.5.xsd">    <!-- 配置被代理的对象 -->    <bean id="test1Service" class="jiankunking.aop.Test1Service">        <property name="name" value="小土豆"/>    </bean>    <!--前置通知-->    <bean id="myMethodBeforeAdvice" class="jiankunking.aop.MyMethodBeforeAdvice"/>    <!--定义前置通知的切入点-->    <bean id="nameMatchMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="advice" ref="myMethodBeforeAdvice"/>        <property name="mappedName" value="sayHello" />        <!--使用下面的mappedNames不知道为什么无法起到拦截的作用-->        <!--<property name="mappedNames">-->            <!--<list>-->                <!--<value>-->                    <!--sayHello-->                <!--</value>-->            <!--</list>-->        <!--</property>-->    </bean>    <!-- 配置代理对象 -->    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 代理接口集 -->        <property name="proxyInterfaces">            <list>                <value>jiankunking.aop.TestServiceInterface</value>                <value>jiankunking.aop.TestServiceInterface2</value>            </list>        </property>        <!-- 把通知织入到代理对象  -->        <property name="interceptorNames">            <list>                <!--使用自定义切入点来控制前置通知-->                <value>nameMatchMethodPointcutAdvisor</value>            </list>        </property>        <!-- 配置被代理对象,可以指定 -->        <property name="target" ref="test1Service"/>    </bean></beans>

具体代码参考(AOP根据方法名匹配织入演示demo)):
http://download.csdn.net/detail/xunzaosiyecao/9584181

作者:jiankunking 出处:http://blog.csdn.net/jiankunking
0 1