• Spring两种实现AOP的方式


    有两种实现AOP的方式:xml配置文件的方式和注解的形式

    我们知道通知Advice是指对拦截到的方法做什么事,可以细分为

    前置通知:方法执行之前执行的行为。

    后置通知:方法执行之后执行的行为。

    异常通知:方法抛出异常时的行为。

    最终通知:方法执行成功失败都会执行的行为,finally。

    环绕通知:前后都要做,如事务

    相关依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

    需要加功能的类

    public class UserService {
    
        public void saveUser() {
            System.out.println("save a user...");
            //throw new RuntimeException();
        }
    
    }

    要加的功能Advice

    public class LogAdvice {
        //前置通知
        public void before() {
            System.out.println("== before ==");
        }
    
        //最终通知
        public void after() {
            System.out.println("== after ==");
        }
    
        //后置通知
        public void afterReturning() {
            System.out.println("== afterReturning ==");
    
        }
        //异常通知
        public void afterThrowing() {
            System.out.println("== afterThrowing ==");
        }
    
        //环绕通知֪
        public void around(ProceedingJoinPoint joinPoint) {
            try {
                System.out.println("== around start ==");
                joinPoint.proceed(); // 执行原方法
                System.out.println("== around end ==");
            } catch (Throwable e) {
           //这里是处理还是抛出,情况不一样,涉及到会不会调用afterThrowing System.out.println(
    "== around afterThrowing =="); } } }

    配置文件实现AOP功能

    <?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:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
    
    
        <!-- 配置Service对象-->
        <bean id="userService"  class="com.winner.test.UserService"/>
    
        <!-- 配置一个通知对象 -->
        <bean id="logAdvice" class="com.winner.test.LogAdvice"/>
    
        <!-- AOP有关的配置都 在aop:config中 -->
        <aop:config>
            <!-- 声明一个切面 ref对应的通知对象中有前置通知,后置通知等各种通知,各种通知的method属性的值
            就是这个通知对中的方法名
            -->
            <aop:aspect ref="logAdvice">
                <!-- 声明切入点 -->
                <aop:pointcut id="myPointcut" expression="execution(public * *(..))"/>
                <!-- 指定在某切入点执行某操作 -->
                <!--指定before通知方法为,logAdvice.before(),引用切入点myPointcut -->
                <aop:before method="before" pointcut-ref="myPointcut"/>
                <!--指定before通知方法为,logAdvice.after(),引用切入点myPointcut -->
                <aop:after method="after" pointcut-ref="myPointcut"/>
                <!--指定before通知方法为,logAdvice.afterReturning(),引用切入点myPointcut -->
                <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
                <!--指定before通知方法为,logAdvice.afterThrowing(),引用切入点myPointcut -->
                <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
                <!--指定before通知方法为,logAdvice.around(),引用切入点myPointcut -->
                <aop:around method="around" pointcut-ref="myPointcut" />
            </aop:aspect>
        </aop:config>
    
    </beans>

    测试

    public class MainTest {
    
        @Test
        public void testUserService() throws Exception {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) ac.getBean("userService");
            System.out.println(userService.getClass());
    
            userService.saveUser();
            System.out.println();
        }
    }

    执行结果

    没有异常的情况

    == before ==
    == around start ==
    save a user...
    == around end ==
    == afterReturning ==
    == after ==

    有异常的情况

    == before ==
    == around start ==
    save a user...
    == around afterThrowing ==
    == afterReturning ==
    == after ==

    切入点表达式

    格式:

    execution(修饰符? 返回值类型 类的全限定名? 方法名(参数))

    这其中的问号后缀(?)表示可选的表达式元素。让我们来分析

    execution(* com.winner.test.UserService.*(..))

    表达式:星号*表示任何返回类型(ret- type-pattern,返回类型模式),后面跟着一个全限定类名(declaring-type-pattern,声明类型模式)。我们在这个类名后 又跟另一个星号*(..),这表示一个任意名称、任意数量(包括零)和任意参数类型的方法。

    <!-- 声明切入点 -->
    <!-- cn.itcast.oa.service包中所有类的所有public方法 -->
    <aop:pointcut expression="execution(public * cn.winner.oa.service.*.*(..))" id="myPointcut" />
    
    <!-- 所模块的service包中所有类的所有方法 -->
    <aop:pointcut expression="execution(* cn.winner.oa.*.service.*.*(..))" id="myPointcut" />
                
    <!-- cn.itcast.oa.service的所有子包中所有类的所有方法 -->
    <aop:pointcut expression="execution(* cn.winner.oa.service.*.*.*(..))" id="myPointcut" />
    
    <!-- 所有业务层的以save开头的方法 -->
    <aop:pointcut expression="execution(* cn.winner.oa.service.*.save*(..))" id="myPointcut" />
    
                
    <!-- 可以使用 &amp;&amp; 与 || 与! 或是 and or not 进行多个表达式的组合 -->
    <!-- 所有业务层的以save或delete开头的方法--> 
    <aop:pointcut expression="execution(* save*(..)) || execution(* delete*(..))" id="myPointcut" />
    
    <!-- 所有业务层的以save或delete开头的方法-->
    <aop:pointcut expression="execution(* save*(..)) or execution(* delete*(..))" id="myPointcut" />
    
    <!-- 所有业务层的所有不是查询的方法-->
    <aop:pointcut expression="execution(* *(..)) and !execution(* query*(..))" id="myPointcut" />
    
    <!-- 所有业务层的所有不是查询的方法 -->
    <aop:pointcut expression="execution(* *(..)) and not execution(* query*(..))" id="myPointcut" />
    
    <aop:pointcut expression="!execution(* query*(..))" id="myPointcut" />

    使用注解实现AOP

    为了在Spring配置中使用@AspectJ切面,你首先必须启用Spring对@AspectJ切面配置的支持,并确保自动代理:

    <!--启用Spring对@AspectJ的支持 -->
    <!-- 使用基于注解的方式配置切面,需要有下面这个配置 -->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

    beans.xml

    <?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:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
    
    
        <!-- 配置Service对象-->
        <bean id="userService"  class="com.winner.test.UserService"/>
    
        <!-- 配置一个通知对象 -->
        <bean id="logAdvice" class="com.winner.test.LogAdvice"/>
    
        <!--启用Spring对@AspectJ的支持 -->
        <!-- 使用基于注解的方式配置切面,需要有下面这个配置 -->
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    
    </beans>

    切面:

    @Aspect
    public class LogAdvice {
    
        // 声明一个切入点。本方法不能有返回值与参数
        @Pointcut("execution(* *(..))")
        private void myPointcut() {
        }
    
        // 前置通知,在原方法执行之前
        @Before("myPointcut()")
        public void before() {
            System.out.println("== before ==");
        }
    
        // 最终通知,在原方法执行之后
        // 如果原方法有异常,也会执行
        @After("myPointcut()")
        public void after() {
            System.out.println("== after ==");
        }
    
        // 后置通知,在原方法执行之后
        // 如果原方法有异常,则不执行。
        // 方法一:@AfterReturning("myPointcut()")
        // 方法二:@AfterReturning(pointcut = "myPointcut()")
        // 可以使用returning参数指定返回值的对应的参数名,Spring就会在调用本方法时把返回值传给指定名称的参数
        @AfterReturning(pointcut = "myPointcut()", returning = "returnValue")
        public void afterReturning(Object returnValue) {
            System.out.println("== afterReturning ==");
        }
    
        // 异常通知,在出现异常之后
        // @AfterThrowing("myPointcut()")
        @AfterThrowing(pointcut = "myPointcut()", throwing = "ex" )
        public void afterThrowing(Exception ex) {
            System.out.println("== afterThrowing ==");
        }
    
        // 环绕通知,在原方法调用的前后执行。
        // 在原方法执行出异常后,环绕通知的后面部分不会执行。
        // 一定要在执行完原方法后,从本方法中返回结果。
        @Around("myPointcut()")
        public void around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("== 环绕通知(前) ==");
            Object result = joinPoint.proceed(); // 执行原方法
            System.out.println("== 环绕通知(后) ==");
        }
    }
  • 相关阅读:
    MFC对话框编程四:输入焦点的传递
    lua笔记 一个C++调用lua函数的类的实现
    [置顶] 设计模式之享元模式
    扩展实现PHP Benchmark
    TLD如何运行OpenTLD(TLD_HY20121127)
    [Oracle] Kill Session 经验总结
    php 该如何获取从百度搜索进入网站的关键词
    php+js实现百度地图多点标注的方法
    laraveladmin 报错 Disk [admin] not configured, please add a disk config in `config/filesystems.php`....
    PHP获取当前页面的url
  • 原文地址:https://www.cnblogs.com/winner-0715/p/5427293.html
Copyright © 2020-2023  润新知