美文网首页一篇Spring带你快速入门
基于AspectJ的注解AOP开发

基于AspectJ的注解AOP开发

作者: 往事随风_0817 | 来源:发表于2019-02-14 23:36 被阅读8次

    基于AspectJ的注解AOP开发

    AspectJ开发准备

    1. 导入相关jar包
    Spring基础jar包
        1.core
        2.beans
        3.context
        4.expression    
    
    传统aop开发jar包
        1.spring-aop
        2.aopalliance----->aop联盟
    
    AspectJ相关jar包
        1.spring-aspects---->Spring与aspectJ集成jar包
        2.aspectjweaver
    
    1. 引入aop约束
    //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: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"
    >
        <!--开启aspectJ注解开发,自动代理-->
        <aop:aspectj-autoproxy/>
    
    </beans>
    

    @AspectJ通知类型

    • @before 前置通知,相当于BeforeAdvice
    • @AfterReturning后置通知,相当于AfterReturningAdvice
    • @Around环绕通知,相当于MethodInterceptor
    • @AfterThrowing异常抛出通知,相当于ThrowAdvice
    • @After最终final通知,不管是否异常,该通知都会执行
    • @DeclareParents 引介通知,相当于IntroductionInterceptor(Spring只支持对切入点的增强,了解就行了)

    在通知中通过Value属性定义切点

    通过execution函数,可以定义切点的方法切入

    语法:

    • execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

    例如:

    • 匹配所有类public方法 execution(public * *(..))
    • 匹配指定包下所有类方法execution(* cn.akwangl.dao.(..))不包含子包*
    • execution(* cn.akwangl.dao..*(..)) ..*表示包、子孙包下所有类
    • 匹配指定类所有方法execution(* cn.akwangl.service.UserService.*(..))
    • 匹配实现特定接口所有类方法execution(* cn.akwangl.dao.UserDao+.*(..))
    • 匹配所有save开头的方法execution(* save*(..))

    AspectJ简单入门Demo

    //目标对象
    public class UserDao {
    
        public void save(){System.out.println("保存用户...");}
        public void update(){System.out.println("修改用户...");}
        public void delete(){System.out.println("删除用户...");}
        public void find(){System.out.println("查询用户...");}
        public void findOne(){System.out.println("查询一个用户...");}
    }
    
    //配置AspectJ通知
    @Aspect
    public class MyAspectj {
        //前置通知
        @Before(value = "execution(* cn.akwangl.aspectj.UserDao.save())")
        //在方法中传入JoinPoint对象可以获取切点信息
        public void before(JoinPoint joinPoint){
            System.out.println("=======>权限验证..."+joinPoint);
        }
    }
    
    //Spring-XML配置
    <!--开启aspectJ注解开发,自动代理-->
    <aop:aspectj-autoproxy/>
    
    <!--配置目标-->
    <bean id="userDao" class="cn.akwangl.aspectj.UserDao"/>
    <!--配置切面-->
    <bean class="cn.akwangl.aspectj.MyAspectj"/> 
    
    //Test类
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:Spring-aspectj.xml")
    public class AspectJ_Test {
    
        @Resource(name = "userDao")
        private UserDao userDao;
    
        @Test
        public void aspectjTest() {
            userDao.save();
        }
    }
    

    @AfterReturing后置通知

    通过returning属性可以定义方法返回值,作为参数

    //userDao->update方法返回一个字符串
    public String update(){
        System.out.println("修改用户...");
        return "---返回值-->修改成功!!!";
    }
    
    //配置AspectJ通知
    //后置通知
    @AfterReturning(value = "execution(* cn.akwangl.aspectj.UserDao.update())",returning = "returning" )
    public void afterReturning(Object returning){
        System.out.println("=====>后置通知..."+returning);
    }
    
    //Test类
    @Test
    public void aspectjTest() {
        userDao.save();
        userDao.update();
    }
    

    @Around环绕通知

    around方法的返回值就是目标代理方法执行返回值
    参数为ProceedingJoinPoint可以调用拦截目标方法执行
    注意:如果不调用ProceedingJoinPoint的proceed方法,那么目标方法就被拦截了,并不会执行目标方法

    //环绕通知
    @Around(value = "execution(* cn.akwangl.aspectj.UserDao.find())")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("====环绕通知前====");
        Object proceed = joinPoint.proceed();// 执行目标方法
        System.out.println("====环绕通知后====");
        return proceed;
    }
    
    //Test类
    @Test
    public void aspectjTest() {
        userDao.save();
        userDao.update();
        userDao.find();
    }
    

    @AfterThrowing异常抛出通知

    通过设置throwing属性,可以设置发生异常对象参数

    //userDao->delete方法测试异常通知
    public void delete(){
        System.out.println("删除用户...");
        //System.out.println(1/0);
    }
    
    //AfterThrowing异常抛出通知
    @AfterThrowing(value = "execution(* cn.akwangl.aspectj.UserDao.delete())",throwing="e")
    public void afterThrowing(Throwable e){
        System.out.println("=====异常抛出通知===="+e.getMessage());
    }
    
    //Test类
    @Test
    public void aspectjTest() {
        userDao.save();
        userDao.update();
        userDao.find();
        userDao.delete();
    }
    

    注意: AfterThrowing异常抛出通知只有发生异常后才会切面,没有发生异常是不会切面的

    @After最终通知

    无论是否出现异常,最终通知总是会被执行

    //After最终通知
    @After(value = "execution(* cn.akwangl.aspectj.UserDao.findOne())")
    public void after(){
        System.out.println("最终通知==================");
    }
    
    //Test测试类
    @Test
    public void aspectjTest() {
        userDao.save();
        userDao.update();
        userDao.find();
        userDao.delete();
        userDao.findOne();
    }
    

    通过@Pointcut为切点命名

    • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
    • 切点方法:private void 无参数方法,方法名为切点名
    • 当通知多个切点时,可以使用||进行连接
    @Aspect
    public class MyAspectj {
    
        //前置通知
        //@Before(value = "myPointcut5() || myPointcut1()")->配置多个通知
        @Before(value = "myPointcut5()")
        public void before(JoinPoint joinPoint){
            System.out.println("=======>权限验证..."+joinPoint);
        }
    
        //后置通知
        @AfterReturning(value = "myPointcut2()",returning = "returning" )
        public void afterReturning(Object returning){
            System.out.println("=====>后置通知..."+returning);
        }
    
        //环绕通知
        @Around(value = "myPointcut4()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("====环绕通知前====");
            Object proceed = joinPoint.proceed();// 执行目标方法
            System.out.println("====环绕通知后====");
            return proceed;
        }
    
        //AfterThrowing异常抛出通知
        @AfterThrowing(value = "myPointcut3()",throwing="e")
        public void afterThrowing(Throwable e){
            System.out.println("=====异常抛出通知===="+e.getMessage());
        }
        //After最终通知
        @After(value = "myPointcut5()")
        public void after(){
            System.out.println("最终通知==================");
        }
    
        @Pointcut(value = "execution(* cn.akwangl.aspectj.UserDao.save())")
        private void myPointcut1(){}
        @Pointcut(value = "execution(* cn.akwangl.aspectj.UserDao.update())")
        private void myPointcut2(){}
        @Pointcut(value = "execution(* cn.akwangl.aspectj.UserDao.delete())")
        private void myPointcut3(){}
        @Pointcut(value = "execution(* cn.akwangl.aspectj.UserDao.find())")
        private void myPointcut4(){}
        @Pointcut(value = "execution(* cn.akwangl.aspectj.UserDao.findOne())")
        private void myPointcut5(){}
    
    }
    

    配置多个通知

    @Before(value = "myPointcut5() || myPointcut1()")->配置多个通知
    public void before(JoinPoint joinPoint){
        System.out.println("=======>权限验证..."+joinPoint);
    }
    

    相关文章

      网友评论

        本文标题:基于AspectJ的注解AOP开发

        本文链接:https://www.haomeiwen.com/subject/ahjueqtx.html