Spring配置切面(AOP)

AOP,面向接口的编程,实际上是代理模式的实现。

参考:代理模式(Proxy)

 

 


 

一、使用Scheme-based方式配置

需要实现接口重写指定方法,来确定通知所在位置。

(一)、前置通知和后置通知

切点类:

public class Client {
    public void sayHi(){
        System.out.println("Client.sayHi");
    }
}

通知类:

  前置通知实现接口:MethodBeforeAdvice,在实现方法中可获取方法对象,参数数组,调用对象

  后置通知实现接口:AfterReturningAdvice,在实现方法中可获取方法对象,参数数组,调用对象,返回值

public class TestBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("执行前置通知");
    }
}

xml配置:

   <!--切点类-->
   <bean class="com.lurenjia.test.Client" id="client"></bean>
   <!--前置通知对象-->
    <bean id="mybefore" class="com.lurenjia.advice.TestBeforeAdvice"></bean>
    <!--后置通知对象-->
    <bean id="after" class="com.lurenjia.advice.TestAfterAdvice"></bean>
    <!--配置一个aop-->
    <aop:config>
        <!--切点-->
        <aop:pointcut id="mypoint" expression="execution(* com.lurenjia.test.Client.sayHi())"/>
        <!--配置一个前置通知-->
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"></aop:advisor>
        <!--配置一个后置通知-->
        <aop:advisor advice-ref="after" pointcut-ref="mypoint"></aop:advisor>
    </aop:config>

(二)、异常通知

  异常通知类的实现方法不为接口提供,是spring文档提供的。(奇奇怪怪)

切点类:

  一般在service中都采取抛出异常,从而使得spring进行捕获异常进行事务处理。

public class Client {
    public void doSomething2() throws Exception{
        int a = 5/0;
    }
}

通知类:

  自行指定方法名,可在参数中获取方法对象,参数对象,执行对象,异常对象,(或者只获取异常对象)

public class TestThrowAdvice implements ThrowsAdvice {
    /**
     * 指定此方法名才能被调用*/
    public void afterThrowing(Method method,Object[] args,Object target,Exception e){
        System.out.println("获取到发生异常的方法:"+method.getName());
        System.out.println("获取到参数:"+args);
        System.out.println("获取到调用对象:"+target);
        System.out.println("获取到异常:"+e.getMessage());
    }
}

xml配置:

   <!--切点类-->
   <bean class="com.lurenjia.test.Client" id="client"></bean>
    <!--异常通知对象-->
    <bean class="com.lurenjia.advice.TestThrowAdvice" id="throw"></bean>
    <!--配置一个aop-->
    <aop:config>
        <!--切点-->
        <aop:pointcut id="myponint2" expression="execution(* com.lurenjia.test.Client.doSomething2())"/>
        <!--配置一个异常通知-->
        <aop:advisor advice-ref="throw" pointcut-ref="myponint2"></aop:advisor>
    </aop:config>

(三)、环绕通知

  在方法前后都织入通知。

切点类:

public class Client {
    public void sayHi(){
        System.out.println("Client.sayHi");
    }
}

通知类:

public class TestArround implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //前置处理
        System.out.println("前置处理了");

        //执行方法
        Object proceed = methodInvocation.proceed();

        //后置处理
        System.out.println("后置处理了");
        //返回结果
        return proceed;
    }
}

xml配置:

   <!--切点类-->
   <bean class="com.lurenjia.test.Client" id="client"></bean>
   <!--环绕通知对象-->
    <bean id="arround" class="com.lurenjia.advice.TestArround"></bean>
    <aop:config>
        <!--切点-->
        <aop:pointcut id="point1" expression="execution(* com.lurenjia.test.Client.sayHi())"/>
        <!--配置一个环绕通知-->
        <aop:advisor advice-ref="arround" pointcut-ref="point1"></aop:advisor>
    </aop:config>

二、使用AspectJ方式配置

不需要实现接口,完全自定义方法,所以需要在xml中配置方法位置。

(一)、配置通知

切点类:

public class Client {
    public void sayHi() throws Exception{
        //int i = 7/0;
        System.out.println("执行方法");
    }
}

通知类:

public class TestAspectjAdvice {

    public void myBefore(){
        System.out.println("before:前置通知!");
    }

    public void myAfter(){
        System.out.println("after:finally后置通知!");
    }

    public void myFnalliy(){
        System.out.println("after-returning:后置通知!");
    }

    public void myThrow(){
        System.out.println("after-throwing:异常通知!");
    }
public Object myArround(ProceedingJoinPoint joinPoint) throws Throwable { //前置操作 System.out.println("around:前置操作"); //执行方法 Object result = joinPoint.proceed(); //后置操作 System.out.println("around:后置操作"); //返回值返回 return result; } }

xml配置:

    <!--切点类-->
    <bean class="com.lurenjia.test.Client" id="client"></bean>
    <!--通知类-->
    <bean class="com.lurenjia.advice.TestAspectjAdvice" id="advice"></bean>
    <!--配置一个切面-->
    <aop:config>
        <!--使用的通知类-->
        <aop:aspect ref="advice">
            <!--切点方法-->
            <aop:pointcut id="point1" expression="execution(* com.lurenjia.test.Client.sayHi())"/>
            <!--前置通知-->
            <aop:before method="myBefore" pointcut-ref="point1"></aop:before>
            <!--后置通知-->
            <aop:after method="myAfter" pointcut-ref="point1"></aop:after>
            <!--finally通知-->
            <aop:after-returning method="myFnalliy" pointcut-ref="point1"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="myThrow" pointcut-ref="point1"></aop:after-throwing>
            <!--环绕通知-->
            <aop:around method="myArround" pointcut-ref="point1"></aop:around>
        </aop:aspect>
    </aop:config>

(二)、获取参数

切点类:

public class Client {
    public void sayHi(String name,int age) throws Exception{
        System.out.println("执行方法");
    }
}

通知类:

public class TestAspectjAdvice {

    public void myBefore(String name,int age){
        System.out.println("before:前置通知!");
        System.out.println("获取到参数:"+name+"-"+age);
    }
}

xml配置:

    <!--切点类-->
    <bean class="com.lurenjia.test.Client" id="client"></bean>
    <!--通知类-->
    <bean class="com.lurenjia.advice.TestAspectjAdvice" id="advice"></bean>
    <!--配置一个切面-->
    <aop:config>
        <!--使用的通知类-->
        <aop:aspect ref="advice">
            <!--切点方法,声明参数类型和名称-->
            <aop:pointcut id="point1"
                          expression="execution(* com.lurenjia.test.Client.sayHi(String,int)) and args(name,age)"/>

            <!--前置通知,配置参数名必须与上面声明的一样-->
            <aop:before method="myBefore" pointcut-ref="point1" arg-names="name,age"></aop:before>
        </aop:aspect>
    </aop:config>

 

posted @ 2023-02-28 12:41  在博客做笔记的路人甲  阅读(423)  评论(0编辑  收藏  举报