Spring AOP使用

2019-03-10  本文已影响0人  仿若尘土

[TOC]

1. 概念

AOP(Aspect Oriented Programming,面向切面编程),可使各业务逻辑分离,降低耦合,提高复用,增加开发效率。
应用于日志记录、性能统计、安全控制、权限管理、事务处理、异常处理和资源池管理等。

2. 概念

3. 切入点

3.1 匹配语法

  1. java.lang.String:匹配String类型;
  2. java.*.String:匹配java包下的任何“一级子包”下的String类型;如匹配java.lang.String,但不匹配java.lang.ss.String
    3.java..*:匹配java包及任何子包下的任何类型;如匹配java.lang.Stringjava.lang.annotation.Annotation
  3. java.lang.*ing:匹配任何java.lang包下的以ing结尾的类型;
  4. java.lang.Number+:匹配java.lang包下的任何Number的自类型;如匹配java.lang.Integer,也匹配java.math.BigInteger

3.2 匹配逻辑

可以使用且(&&)、或(||)、非(!)来组合切入点表达式。由于在XML中使用“&&”需要使用转义字符“&&”来代替之,所以很不方便,因此Spring ASP 提供了and、or、not来代替&&、||、!。

3.3 切入点表达式

@Before("execution(* com.cui.springShizhan.ch4.Test.test(String)) && args(testName)")
    public void before(String testName) {
        System.out.println("before");
        System.out.println("para:" + testName);
    }
image.png

4. 实例

        <!--spring的context上下文即IoC容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--spring aop依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--spring测试依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--spring aop依赖AspectJ-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>

注:比如引入aspectjweaver,否则会报错Spring AOP报错:java.lang.NoClassDefFoundError: org/aspectj/lang/JoinPoint

package com.cui.springShizhan.aop;

/**
 * 接口定义
 */
public interface IDateOperationService {
    int save();

    int query() throws Exception;
}

接口实现:

package com.cui.springShizhan.aop;

/**
 * 实现接口,使用JDK动态代理,否则使用CGLIB代理
 */
public class DateOperationServiceImpl implements IDateOperationService {
    @Override
    public int save() {
        System.out.println("----保存到数据库----");
        return 1;
    }

    @Override
    public int query() throws Exception {
        System.out.println("----从数据库中得到查询结果----");
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            throw new Exception();
        }
        return 9;
    }
}
package com.cui.springShizhan.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * AOP 切面相关定义
 */
public class AopAspect {
    /**
     * 前置通知:在连接点之前执行
     * @param joinPoint 连接点
     */
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("前置通知: " + methodName + "执行前");
    }

    /**
     * 后置通知:在连接点之后执行
     * @param joinPoint 连接点
     */
    public void after(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("后置通知:" + methodName + "执行后");
    }

    /**
     * 返回后通知:在return后执行这段逻辑,异常退出不执行
     * @param joinPoint
     * @param result    返回结果
     */
    public void afterReturn(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("返回后通知: " + methodName + "已正常return, result: " + result);
    }

    /**
     * 抛出异常后通知:抛出异常后执行这段逻辑
     * @param joinPoint
     * @param ex    异常类型
     */
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("抛出异常后通知:method: " + methodName + "抛出异常,异常为: " + ex);
    }

    /**
     * 环绕通知:可实现前面4种通知
     * @param proceedingJoinPoint
     * @return
     */
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;
        String methodName = proceedingJoinPoint.getSignature().getName();
        try {
            System.out.println("环绕通知 → 前置通知: " + methodName + "执行前");
            result = proceedingJoinPoint.proceed();
            System.out.println("环绕通知 → 返回后通知: " + methodName + "已正常return, result: " + result);
        } catch (Throwable throwable) {
            //异常通知
            System.out.println("环绕通知 → 抛出异常后通知:method: " + methodName + "抛出异常,异常为: " + throwable);
            throw new RuntimeException(throwable);
        }

        System.out.println("环绕通知 → 后置通知:" + methodName + "执行后");
        return result;
    }
}
<?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" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="dateOperationService" class="com.cui.springShizhan.aop.DateOperationServiceImpl"/>

    <!--定义切面-->
    <bean id="aopAspect" class="com.cui.springShizhan.aop.AopAspect"/>

    <aop:config>
        <!--id:唯一标识符,ref:切面,order:执行顺序-->
        <aop:aspect id="aopTest" ref="aopAspect" order="1">
            <!--定义切点-->
            <aop:pointcut id="savePoint" expression="execution(* com.cui.springShizhan.aop.*.save(..))"></aop:pointcut>
            <aop:pointcut id="queryPoint" expression="execution(* com.cui.springShizhan.aop.*.query(..))"/>

            <aop:before method="before" pointcut-ref="savePoint"/>
            <aop:after method="after" pointcut-ref="savePoint"/>
            <aop:after-returning method="afterReturn" returning= "result" pointcut-ref="savePoint"/>
            <aop:around method="around" pointcut-ref="savePoint"/>

            <!--异常定义-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="queryPoint" throwing="ex"/>
            <aop:around method="around" pointcut-ref="queryPoint"/>
        </aop:aspect>
    </aop:config>
</beans>
package com.cui.springShizhan.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:springshizhan/applicationContext-aop.xml");
        IDateOperationService dateOperationService = (IDateOperationService) applicationContext.getBean("dateOperationService");
        System.out.println("-------------------save开始-----------------");
        dateOperationService.save();
        System.out.println("-------------------save结束-----------------\n\n");

        System.out.println("-------------------query开始-----------------");
        dateOperationService.query();
        System.out.println("-------------------query结束-----------------\n\n");
    }
}
-------------------save开始-----------------
前置通知: save执行前
环绕通知 → 前置通知: save执行前
----保存到数据库----
环绕通知 → 返回后通知: save已正常return, result: 1
环绕通知 → 后置通知:save执行后
返回后通知: save已正常return, result: 1
后置通知:save执行后
-------------------save结束-----------------


-------------------query开始-----------------
环绕通知 → 前置通知: query执行前
----从数据库中得到查询结果----
环绕通知 → 抛出异常后通知:method: query抛出异常,异常为: java.lang.Exception
抛出异常后通知:method: query抛出异常,异常为: java.lang.RuntimeException: java.lang.Exception
Exception in thread "main" java.lang.RuntimeException: java.lang.Exception
    at com.cui.springShizhan.aop.AopAspect.around(AopAspect.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy4.query(Unknown Source)
    at com.cui.springShizhan.aop.Client.main(Client.java:15)
Caused by: java.lang.Exception
    at com.cui.springShizhan.aop.DateSaveServiceImpl.query(DateSaveServiceImpl.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at com.cui.springShizhan.aop.AopAspect.around(AopAspect.java:58)

5. 参考

  1. Spring Aop详尽教程
  2. Spring AOP--返回通知,异常通知和环绕通知
  3. spring doc
  4. springAOP中的target、this、within的区别
上一篇下一篇

猜你喜欢

热点阅读