个人主页: 几分醉意的CSDN博客_传送门
上节我们介绍了什么是AOP、Aspectj框架的前置通知@Before传送门,这篇文章将继续详解Aspectj框架的其它注解。
文章目录
💖Aspectj框架介绍
AOP技术思想的实现:使用框架实现AOP。实现AOP的框架有很多。有名的两个
1. Spring:Spring框架实现AOP思想中的部分功能。Spring框架实现AOP的操作比较繁琐,笨重。
2. Aspectj:独立的框架,专门做AOp的,功能最强大的。属于Eclipse。
而我下面主要介绍的就是Aspectj框架来实现Aop,Aspectj框架可以使用注解和xml配置文件两种方式实现AOP。
✨JoinPoint通知方法的参数
切面类中的通知方法,可以有参数,但是必须是JoinPoint。
JoinPoint: 表示正在执行的业务方法。 相当于反射中 Method
使用要求:必须是参数列表的第一个
作用:获取方法执行时的信息,例如方法名称, 方法的参数集合
下面我们直接实战,注意下面用的是上一节的前置通知的业务接口和实现类。
切面类
@AspectpublicclassMyAspect{@Before(value ="execution(* *..SomeServiceImpl.do*(..) )")publicvoidmyBefore2(JoinPoint jp){//获取方法的定义System.out.println("前置通知中,获取目标方法的定义:"+ jp.getSignature());System.out.println("前置通知中,获取方法的名称"+jp.getSignature().getName());//获取方法执行时的参数Object[] args = jp.getArgs();//返回的是一个数组 里面存放的是所有参数for(Object arg : args){System.out.println("前置通知,获取方法的参数是"+arg);}//切面的代码。System.out.println("===前置通知,切面的功能,在目标方法之前先执行==:"+newDate());System.out.println("");}}
测试
@Testpublicvoidtest(){//如果没有加入代理的处理:// 1)目标方法执行时,没有切面功能的。// 2) service对象没有被改变//加入代理的处理:// 1)目标方法执行时,有切面功能的。// 2) service对象是改变后的 代理对象 com.sun.proxy.$Proxy8String s ="applicationContext.xml";ApplicationContext ctx =newClassPathXmlApplicationContext(s);SomeService service =(SomeService)ctx.getBean("someService");
service.doSome("ll",22);}
//JoinPoint:哪个目标对象方法执行时,它就代表哪个方法
//例如这里doSome执行时,它就代表doSome,然后可以获取这个方法的信息
执行结果:
拓展:
✨后置通知@AfterReturning
*@AfterReturning:后置通知
* 属性:value 切入点表达式
* returning 自定义的变量,表示目标方法的返回值的。
* 自定义变量名称必须和通知方法的形参名一样
* 位置:在方法的上面
** 特点:
*1.在目标方法之后,执行的。
*2.能获取到目标方法的执行结果。
*3.不会影响目标方法的执行
** 方法的参数:
* Object res: 表示目标方法的返回值,使用res接收doOther的调用结果。
* Object res=doOther();** 后置通知的执行顺序
* Object res = SomeServiceImpl.doOther(..); 先执行业务方法
*myAfterReturning(res); 在执行后置通知
** 思考:
*1 doOther方法返回是String ,Integer ,Long等基本类型,
* 在后置通知中,修改返回值, 是不会影响目标方法的最后调用结果的。
*2 doOther返回的结果是对象类型,例如Student。
* 在后置通知方法中,修改这个Student对象的属性值,会不会影响最后调用结果?
*
下面通过举例的方式,带大家理解后置通知。
首先业务接口添加doOther方法,然后实现它的实现类
publicinterfaceSomeService{voiddoSome(String name,Integer age);StringdoOther(String name,Integer age);}
publicclassSomeServiceImplimplementsSomeService{@OverridepublicvoiddoSome(String name,Integer age){System.out.println("业务方法doSome(),创建商品订单");}@OverridepublicStringdoOther(String name,Integer age){System.out.println("执行业务方法doOther,处理库存");return"abcd";}}
创建切面类
@AspectpublicclassMyAspect{//定义方法,表示切面的具体功能/*
后置通知方法的定义
1)方法是public
2)方法是void
3)方法名称自定义
4)方法有参数,推荐使用Object类型
*/@AfterReturning(value ="execution(* *..SomeServiceImpl.doOther(..))",
returning ="res")publicvoidmyAfterReturning(JoinPoint jp ,Object res){System.out.println("后置通知,在目标方法之后,执行的。能拿到执行结果:"+res);//修改目标方法的返回值if(res !=null){
res ="HELLO Aspectj";}System.out.println("后置通知,修改res后"+res);}}
测试:
@Testpublicvoidtest02(){String config="applicationContext.xml";ApplicationContext ctx =newClassPathXmlApplicationContext(config);SomeService service =(SomeService) ctx.getBean("someService");String ret = service.doOther("zhangsan",20);System.out.println("test02中调用目标方法的结果:"+ret);}
执行结果:
✨环绕通知@Around
特点介绍:
*@Around:环绕通知
* 属性:value 切入点表达式
* 位置:在方法定义的上面
** 返回值:Object ,表示调用目标方法希望得到执行结果(不一定是目标方法自己的返回值)
* 参数: ProceedingJoinPoint, 相当于反射中 Method。
* 作用:执行目标方法的,等于Method.invoke()** public interface ProceedingJoinPoint extends JoinPoint {}** 特点:
*1.在目标方法的前和后都能增强功能
*2.控制目标方法是否执行
*3.修改目标方法的执行结果。
*
@AspectpublicclassMyAspect{//定义方法,表示切面的具体功能/*
环绕置通知方法的定义
1)方法是public
2)方法是必须有返回值, 推荐使用Object类型
3)方法名称自定义
4)方法必须有ProceedingJoinPoint参数,
*/@Around("execution(* *..SomeServiceImpl.doFirst(..))")publicObjectmyAround(ProceedingJoinPoint pjp)throwsThrowable{Object methodReturn =null;System.out.println("执行了环绕通知,在目标方法之前,输出日志时间=="+newDate());//执行目标方法 ProceedingJoinPoint,表示doFirst
methodReturn = pjp.proceed();//method.invoke(),表示执行doFirst()方法本身if( methodReturn !=null){
methodReturn ="环绕通知中,修改目标方法原来的执行结果";}System.out.println("环绕通知,在目标方法之后,增加了事务提交功能");//return "HelloAround,不是目标方法的执行结果";//返回目标方法执行结果。没有修改的。return methodReturn;}}
测试:
@Testpublicvoidtest02(){String config="applicationContext.xml";ApplicationContext ctx =newClassPathXmlApplicationContext(config);SomeService service =(SomeService) ctx.getBean("someService");String ret = service.doFirst("zhangsan");System.out.println("ret调用目标方法的结果:"+ret);}
执行结果:
✨异常通知@AfterTrowing
业务接口和实现类:
publicinterfaceSomeService{voiddoSecond(String name);}
@ServicepublicclassSomeServiceImplimplementsSomeService{@OverridepublicvoiddoSecond(String name){System.out.println("执行业务方法doSecond,处理库存"+(10/0));}}
切面类:
@AspectpublicclassMyAspect{//定义方法,表示切面的具体功能/*
异常通知方法的定义
1)方法是public
2)方法是没有返回值。是void
3)方法名称自定义
4)方法有参数是Exception
*//**
* @AfterThrowing:异常通知
* 属性: value 切入点表达式
* throwing 自定义变量,表示目标方法抛出的异常。
* 变量名必须和通知方法的形参名一样
* 位置:在方法的上面
* 特点:
* 1. 在目标方法抛出异常后执行的, 没有异常不执行
* 2. 能获取到目标方法的异常信息。
* 3. 不是异常处理程序。可以得到发生异常的通知, 可以发送邮件,短信通知开发人员。
* 看做是目标方法的监控程序。
*
* 异常通知的执行
* try{
* SomeServiceImpl.doSecond(..)
* }catch(Exceptoin e){
* myAfterThrowing(e);
* }
*/@AfterThrowing(value ="execution(* *..SomeServiceImpl.doSecond(..))",throwing ="ex")publicvoidmyAfterThrowing(Exception ex){System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:"+ex.getMessage());/*
异常发生可以做:
1.记录异常的时间,位置,等信息。
2.发送邮件,短信,通知开发人员
*/}}
测试:
@Testpublicvoidtest01(){String config="applicationContext.xml";ApplicationContext ctx =newClassPathXmlApplicationContext(config);SomeService service =(SomeService) ctx.getBean("someService");
service.doSecond("lisi");}
执行结果:
✨ 最终通知@After
业务接口和实现类:
publicinterfaceSomeService{voiddoThird();}
@ServicepublicclassSomeServiceImplimplementsSomeService{@OverridepublicvoiddoThird(){System.out.println("执行了业务方法doThird()");}}
切面类:
@AspectpublicclassMyAspect{//定义方法,表示切面的具体功能/*
最终通知方法的定义
1)方法是public
2)方法是没有返回值。是void
3)方法名称自定义
4)方法没有参数
*//**
* @After:最终通知
* 属性: value 切入点表达式
* 位置: 在方法的上面
* 特点:
* 1. 在目标方法之后执行的。
* 2. 总是会被执行。
* 3. 可以用来做程序最后的收尾工作。例如清除临时数据,变量。 清理内存
*
* 最终通知
* try{
* SomeServiceImpl.doThird(..)
* }finally{
* myAfter()
* }
*/@After(value ="execution(* *..SomeServiceImpl.doThird(..))")publicvoidmyAfter(){System.out.println("最终通知,总是会被执行的");}}
测试:
@Testpublicvoidtest01(){String config="applicationContext.xml";ApplicationContext ctx =newClassPathXmlApplicationContext(config);SomeService service =(SomeService) ctx.getBean("someService");
service.doThird();}
执行结果:
✨@Pointcut定义和管理切入点注解
@AspectpublicclassMyAspect{@Before(value ="mypt()")publicvoidmyBefore(){System.out.println("前置通知,在目标方法之前先执行的");}@After(value ="mypt()")publicvoidmyAfter(){System.out.println("最终通知,总是会被执行的");}/**
* @Pointcut: 定义和管理切入点,不是通知注解。
* 属性: value 切入点表达式
* 位置: 在一个自定义方法的上面, 这个方法看做是切入点表达式的别名。
* 其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了
*/@Pointcut("execution(* *..SomeServiceImpl.doThird(..))")privatevoidmypt(){//无需代码}}
✨总结
AOP是一种动态的技术思想,目的是实现业务功能和非业务功能的解耦合。业务功能是独立的模块,其他功能也是独立的模块。例如事务功能,日志等等。让这些事务,日志功能是可以被复用的。
当目标方法需要一些功能时,可以在不修改,不能修改源代码的情况下,使用aop技术在程序执行期间,生成代理对象,通过代理执行业务方法,同时增加功能。
💖投票传送门(欢迎伙伴们投票)
版权归原作者 几分醉意. 所有, 如有侵权,请联系我们删除。