zoukankan      html  css  js  c++  java
  • Spring之AspectJ

    时间:2017-2-4 21:12

    ——AspectJ简介

    1、AspectJ是一个基于Java语言的AOP框架。
    2、Spring2.0以后新增了对AspectJ切点表达式的支持。
    3、@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面。

    4、新版本Spring框架,建议使用AspectJ方式来开发AOP。
    5、使用AspectJ需要导入Spring AOP和AspectJ相关jar包:
        spring-aop-3.2.0.RELEASE.jar
        com.springsource.org.aopalliance-1.0.0.jar
     
        spring-aspects-3.2.0.RELEASE.jar
        com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
     
    ——通过配置启用@AspectJ切面

    <?xml version="1.0" encoding="UTF-8"?>
        <!-- 开启AspectJ自动代理-->
        <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
        <aop:aspectj-autoproxy />

    </beans>
     
    ——在通知中通过value属性定义切点

    1、通过execution函数,可以定义切点的方法切入
    2、语法:
        execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
    3、例如
        1)匹配所有类public方法:execution(public * *(..))
        2)匹配指定包下所有类方法:execution(* cn.itcast.dao.*(..)) 不包含子包
        3)execution(* cn.itcast.dao..*(..))  ..*表示包、子孙包下所有类
        4)匹配指定类所有方法:execution(* cn.itcast.service.UserService.*(..))
        5)匹配实现特定接口所有类方法,包含子类:execution(* cn.itcast.dao.GenericDAO+.*(..))
        6)匹配所有save开头的方法:execution(* save*(..))
     
    ——基于注解的开发

    1、导包
        *   AspectJ依赖AOP环境。
            >    spring-aop-3.2.0.RELEASE.jar
            >   com.springsource.org.aopalliance-1.0.0.jar
     
        *   导入AspectJ的jar包
            >   spring-aspects-3.2.0.RELEASE.jar
            >   com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

    2、编写目标类
        UserDao
        提供增删改查方法。

    3、使用AspectJ添加注解
        /**
         * 切面类
         * 切点 + 增强的结合
         * @author WYC
         *
         */
        // 用来定义切面类
        @Aspect
        public class MyAspect {
            // 对UserDao类的add()方法应用前置增强
            @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
            public void before(){
                System.out.println("前置增强");
            }
        }

    4、创建applicationContext.xml
        1)引入AOP的约束
        2)打开标签:<aop:aspectj-autoproxy />
            表示自动生成代理,该标签底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理。

           <!-- 开启AspectJ自动代理 -->
            <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
            <aop:aspectj-autoproxy />
     
     
            <!-- 配置目标类 -->
            <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
     
            <!-- 配置切面类(增强类) -->
            <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />

    5、示例代码

    UserDao目标类:

    public class UserDao {
        public void add() {
            System.out.println("添加用户");
        }
     
        public void update() {
            System.out.println("修改用户");
        }
     
        public void delete() {
            System.out.println("删除用户");
        }
     
        public void find() {
            System.out.println("查询用户");
        }
    }

    ----------------------------------------------------------------------------------------------------------------------------

    MyAspect切面类:

    /**
     * 切面类
     * 切点 + 增强的结合
     * @author WYC
     *
     */
    // 用来定义切面类
    @Aspect
    public class MyAspect {
        // 对UserDao类的add()方法应用前置增强
        @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
        // JoinPoint表示切点方法的描述,打印结果为:execution(void com.wyc.spring3.demo1.UserDao.add())
        public void before(JoinPoint joinPoing){
            System.out.println("前置增强" + joinPoing);
        }
    }

    ----------------------------------------------------------------------------------------------------------------------------

    XML配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
        xsi:schemaLocation="
     
        <!-- 开启AspectJ自动代理 -->
        <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
        <aop:aspectj-autoproxy />
     
        <!-- 配置目标类 -->
        <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
     
        <!-- 配置增强类 -->
        <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />
     
    </beans>

    ----------------------------------------------------------------------------------------------------------------------------

    测试代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo1 {
     
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao;
     
        @Test
        public void fun1(){
            userDao.add();
            userDao.delete();
            userDao.update();
            userDao.find();
        }
    }

    ——AspectJ的通知类型

    1、@Before 前置通知,相当于BeforeAdvice
        在目标方法之前执行,前置通知无法阻止目标对象执行。 
     
    2、@AfterReturning 后置通知,相当于AfterReturningAdvice
        后置通知可以获得方法的返回值。 
     
    3、@Around 环绕通知,相当于MethodInterceptor
        可以在方法执行前后执行增强方法,环绕通知可以阻止目标方法执行,可以得到目标方法的返回值。 
     
    4、@AfterThrowing抛出通知,相当于ThrowAdvice
        当抛出异常时,会执行该通知。
     
    5、@After 最终final通知,不管是否异常,该通知都会执行
     
    6、@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)


    示例代码:

    UserDao目标类:

    public class UserDao {
        public void add() {
            System.out.println("添加用户");
        }
     
        public int update() {
            System.out.println("修改用户");
            return 123;
        }
     
        public void delete() {
            System.out.println("删除用户");
        }
     
        public void find() {
            System.out.println("查询用户");
            throw new RuntimeException("哈哈哈");
        }
    }

    ----------------------------------------------------------------------------------------------------------------------------

    MyAspect切面类:

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
     
    /**
     * 切面类
     * 切点 + 增强的结合
     * @author WYC
     *
     */
    // 用来定义切面类
    @Aspect
    public class MyAspect {
        // 对UserDao类的add()方法应用前置增强
        @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
        public void before(JoinPoint joinPoing){
            System.out.println("前置增强..." + joinPoing);
        }
     
     
        // 后置通知
        @AfterReturning(value="execution(* com.wyc.spring3.demo1.UserDao.update(..))", returning="returnVal")
        // 参数名必须和注解中的配置相同,returnVal会得到方法执行后的返回值
        public void afterReturing(Object returnVal){
            System.out.println("后置增强... " + returnVal);
        }
     
     
        // 环绕通知
        @Around(value = "execution(* com.wyc.spring3.demo1.UserDao.find(..))")
        // ProceedingJoinPoint,获得切点方法
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
            System.out.println("环绕前增强...");
     
            // 调用目标方法,该方法会返回目标方法的返回值
            Object obj = proceedingJoinPoint.proceed();
            System.out.println("环绕后增强...");
            return obj;
        }
     
     
        // 异常通知,throwing="e"表示获取一场对象,e表示异常对象的名称
        @AfterThrowing(value="execution(* com.wyc.spring3.demo1.UserDao.find())", throwing="e")
        public void afterThrowing(Throwable e){
            // 显示异常信息
            System.out.println("抛出异常... " + e.getMessage());
        }
     
     
        // 最终通知
        @After(value="execution(* com.wyc.spring3.demo1.UserDao.*())")
        public void after(){
            System.out.println("最终通知....");
        }
    }

    ----------------------------------------------------------------------------------------------------------------------------

    XML配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
        xsi:schemaLocation="
     
        <!-- 开启AspectJ自动代理 -->
        <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
        <aop:aspectj-autoproxy />
     
        <!-- 配置目标类 -->
        <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
        <!-- 配置增强类 -->
        <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />
     
    </beans>

    ---------------------------------------------------------------------------------------------------------------------------- 

    测试代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo1 {
     
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao;
     
        @Test
        public void fun1(){
            userDao.add();
            userDao.delete();
            userDao.update();
            userDao.find();
        }
    }

    ---------------------------------------------------------------------------------------------------------------------------- 

    打印结果:

    前置增强...execution(void com.wyc.spring3.demo1.UserDao.add())
    添加用户
    最终通知....
    删除用户
    最终通知....
    修改用户
    最终通知....
    后置增强... 123
    环绕前增强...
    查询用户
    最终通知....
    抛出异常... 哈哈哈

    ——切点的注解

    定义一个方法,添加注解表达式,然后在其他方法的注解中使用:类名.方法  即可。

    示例代码:

    /**
     * 切面类
     * 切点 + 增强的结合
     * @author WYC
     *
     */
    // 用来定义切面类
    @Aspect
    public class MyAspect {
        // 对UserDao类的add()方法应用前置增强
        @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
        public void before(JoinPoint joinPoing){
            System.out.println("前置增强..." + joinPoing);
        }
     
        // 后置通知
        @AfterReturning(value="execution(* com.wyc.spring3.demo1.UserDao.update(..))", returning="returnVal")
        // 参数名必须和注解中的配置相同
        public void afterReturing(Object returnVal){
            System.out.println("后置增强... " + returnVal);
        }
     
        // 环绕通知
        @Around(value = "MyAspect.myPointcut()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
            System.out.println("环绕前增强...");
     
            // 调用目标方法
            Object obj = proceedingJoinPoint.proceed();
            System.out.println("环绕后增强...");
            return obj;
        }
     
        // 异常通知
        @AfterThrowing(value="MyAspect.myPointcut()", throwing="e")
        public void afterThrowing(Throwable e){
            // 显示异常信息
            System.out.println("抛出异常... " + e.getMessage());
        }
     
        // 最终通知
        @After("MyAspect.myPointcut()")
        public void after(){
            System.out.println("最终通知....");
        }
     
        /*
         * 定义一个通用的表达式
         */
        @Pointcut("execution(* com.wyc.spring3.demo1.UserDao.find())")
        private void myPointcut(){
     
        }
    }

    ——Advisor和Aspect的区别


    1、Advisor:Spring传统意义上的切面,支持一个切点和一个通知的组合。
    2、Aspect:可以支持多个切点和多个通知的组合。 


    ——基于XML的开发

    1、编写被增强的类:
        *   ProductDao

    2、定义切面


    3、配置applicationContext.xml
        1)前置通知:
            代码:
                public void before(){
                    System.out.println("前置通知...");
                }
            配置:
                <!-- 定义AOP配置 -->
                <aop:config>
                    <!-- 定义切点 -->
                    <!-- 前置通知 -->
                    <!-- 给指定表达式起名 -->
                    <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.add(..))" id="beforePointcut"/>
     
                    <!-- 定义切面 -->
                    <aop:aspect ref="myAspectXML">
                        <!-- 给切面类的指定方法添加表达式 -->
                        <!-- 前置通知 -->
                        <aop:before method="before" pointcut-ref="beforePointcut"/>
                    </aop:aspect>
                </aop:config>


        2)后置通知
            代码:
                public void afterReturning(Object returnVal){
                    System.out.println("后置增强... " + returnVal);
                }
            配置:
                <!-- 定义AOP配置 -->
                <aop:config>
                    <!-- 定义切点 -->
                    <!-- 给指定表达式起名 -->
                    <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.find(..))" id="afterReturning"/>

     
                    <!-- 定义切面 -->
                    <aop:aspect ref="myAspectXML">
                        <!-- 给切面类的指定方法添加表达式 -->
                        <!-- 后置通知 -->
                        <aop:after-returning method="afterReturning" pointcut-ref="afterReturning" returning="returnVal"/>
                    </aop:aspect>
                </aop:config>

        3)环绕通知
            代码:
                public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
                    System.out.println("环绕前增强...");
                    Object result = proceedingJoinPoint.proceed();
                    System.out.println(result);
                    System.out.println("环绕后增强...");
                    return result;
                }

            配置:
                <!-- 定义AOP配置 -->
                <aop:config>
                    <!-- 定义切点 -->
                    <!-- 给指定表达式起名 -->
                    <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.delete(..))" id="myPointcut"/>

     
                    <!-- 定义切面 -->
                    <aop:aspect ref="myAspectXML">
                        <!-- 给切面类的指定方法添加表达式 -->
                        <!-- 环绕通知 -->
                        <aop:around method="around" pointcut-ref="myPointcut"/>
                    </aop:aspect>
                </aop:config>

        4)异常通知
            代码:
                public void afterThrowing(Throwable e){
                    System.out.println("异常通知..." + e.getMessage());
                }

            配置:
                <!-- 定义AOP配置 -->
                <aop:config>
                    <!-- 定义切点 -->
                    <!-- 给指定表达式起名 -->
                    <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>

     
                    <!-- 定义切面 -->
                    <aop:aspect ref="myAspectXML">
                        <!-- 异常通知 -->
                        <aop:after-throwing method="afterThrowing" pointcut-ref="afterThrowing" throwing="e"/>
                    </aop:aspect>
                </aop:config>

        5)最终通知
            代码:
                public void after(){
                    System.out.println("最终通知...");
                }
     
            配置:
                <aop:config>
                    <!-- 定义切点 -->
                    <!-- 给指定表达式起名 -->
                    <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>

     
                    <!-- 定义切面 -->
                    <aop:aspect ref="myAspectXML">
                        <!-- 最终通知 -->
                        <aop:after method="after" pointcut-ref="afterThrowing"/>
                    </aop:aspect>
                </aop:config>

    4、示例代码

    ProductDao目标类:

    public class ProductDao {
        public void add() {
        System.out.println("添加商品");
    }
     
    public void update() {
        System.out.println("修改商品");
        // throw new RuntimeException("哈哈哈哈哈");
    }
     
    public Object delete() {
        System.out.println("删除商品");
        return "删除成功";
    }
     
    public int find() {
        System.out.println("查询商品");
        return 123;
    }

    ------------------------------------------------------------------------------------------------------------------

    MyAspectXML切面类:

    import org.aspectj.lang.ProceedingJoinPoint;
     
    /**
     * 切面类
     * 
     * @author WYC
     * 
     */
    public class MyAspectXML {
        // 前置通知
        public void before() {
            System.out.println("前置增强...");
        }
     
        // 后置通知
        public void afterReturning(Object returnVal) {
            System.out.println("后置增强... " + returnVal);
        }
     
        // 环绕通知
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕前增强...");
            Object result = proceedingJoinPoint.proceed();
            System.out.println(result);
            System.out.println("环绕后增强...");
            return result;
        }
     
        // 异常通知
        public void afterThrowing(Throwable e) {
            System.out.println("异常通知..." + e.getMessage());
        }
     
        // 最终通知
        public void after() {
            System.out.println("最终通知...");
        }
    }

    ------------------------------------------------------------------------------------------------------------------

    applicationContext.xml配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
        xsi:schemaLocation="
     
        <!-- 定义目标类 -->
        <bean id="productDao" class="com.wyc.spring3.demo2.ProductDao" />
     
        <!-- 定义切面类 -->
        <bean id="myAspectXML" class="com.wyc.spring3.demo2.MyAspectXML" />
     
        <!-- 定义AOP配置 -->
        <aop:config>
            <!-- 定义切点 -->
            <!-- 前置通知 -->
            <!-- 给指定表达式起名 -->
            <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.add(..))" id="beforePointcut"/>
            <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.*(..))" id="afterReturning"/>
            <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.delete(..))" id="myPointcut"/>
            <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>
     
            <!-- 定义切面 -->
            <aop:aspect ref="myAspectXML">
                <!-- 给切面类的指定方法添加表达式 -->
                <!-- 前置通知 -->
                <aop:before method="before" pointcut-ref="beforePointcut"/>
     
                <!-- 后置通知 -->
                <aop:after-returning method="afterReturning" pointcut-ref="afterReturning" returning="returnVal"/>
     
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="myPointcut"/>
     
                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrowing" pointcut-ref="afterThrowing" throwing="e"/>
     
                <!-- 最终通知 -->
                <aop:after method="after" pointcut-ref="afterThrowing"/>
            </aop:aspect>
        </aop:config>
     
    </beans>


    ------------------------------------------------------------------------------------------------------------------

    测试代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext2.xml")
    public class SpringDemo2 {
      @Autowired
      @Qualifier("productDao")
      private ProductDao productDao;
     
      @Test
      public void fun1() {
        productDao.add();
        productDao.update();
        productDao.delete();
        productDao.find();
      }
    }


    ——总结

    为了简化开发,引入了AspectJ的支持。

    1、基于注解开发
        开启XML中支持AOP的配置。

        @Aspect:切面注解

        @Before:前置通知
        @Around:环绕通知
        @AfterReturing:后置通知
        @AfterThrowing:抛出异常通知
        @After:最终通知

        @Pointcut:切点注解
            可以简化开发,不需要每一个注解都写表达式。

    2、基于XML开发
        1)引入AOP的名称空间。
        2)<aop-config>
                <!-- 定义切点 -->
                <aop:pointcut id="" expression="" />

                <!-- 定义切面,并引入切点 -->
                <aop:aspect ref="">
                    <aop:brfore />
                </aop:aspect>
              </aop-config>

  • 相关阅读:
    java之认识基本数据类型及其封装类装箱和拆箱总结
    java之集合类特性对比分析列表
    <转>泛型的内部原理:类型擦除以及类型擦除带来的问题
    java之集合类框架的简要知识点:泛型的类型擦除
    java之方法覆盖的坑
    java入门概念个人理解之访问修饰符
    blog开篇
    卓越管理的实践技巧(4)如何才能给予有效的反馈 Guide to Giving Effective Feedback
    权限管理系统之组织管理
    卓越管理的实践技巧(3)推动团队管理的要点 Facilitation Essentials for Managers
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375494.html
Copyright © 2011-2022 走看看