@param注解的⽤法解析_@Pointcut的12种⽤法,你知道⼏
种?
本⽂继续AOP,⽬前⼿动Aop中三种⽅式已经介绍2种了,本⽂将介绍另外⼀种:AspectJProxyFactory,可能⼤家对这个⽐较陌⽣,但
是@Aspect这个注解⼤家应该很熟悉吧,通过这个注解在spring环境中实现aop特别的⽅便。
⽽AspectJProxyFactory这个类可以通过解析@Aspect标注的类来⽣成代理aop代理对象,对开发者来说,使创建代理变得更简洁了。
先了解⼏个概念
⽂中会涉及⼏个概念,先了解⼀下。
target
⽤来表⽰⽬标对象,即需要通过aop来增强的对象。
proxy
代理对象,target通过aop增强之后⽣成的代理对象。
AspectJ
AspectJ是什么?
AspectJ是⼀个⾯向切⾯的框架,是⽬前最好⽤,最⽅便的AOP框架,和spring中的aop可以集成在⼀起使⽤,通过Aspectj提供的⼀些功
能实现aop代理变得⾮常⽅便。
AspectJ使⽤步骤
1.创建⼀个类,使⽤@Aspect标注
2.@Aspect标注的类中,通过@Pointcut定义切⼊点
3.@Aspect标注的类中,通过AspectJ提供的⼀些通知相关的注解定义通知
4.使⽤
先来个案例,感受⼀下AspectJ是多么的⽅便。
来个类
package com.javacode2018.st1;public class Service1 { public void m1() { System.out.println("我是 m1 ⽅法"); } public void m2() { Syst
通过AspectJ来对Service1进⾏增强,来2个通知,⼀个前置通知,⼀个异常通知,这2个通知需要对Service1中的所有⽅法⽣效,实现如
下:
package com.javacode2018.st1;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.a
@1:类上使⽤@Aspect标注
@2:通过@Pointcut注解标注在⽅法上⾯,⽤来定义切⼊点
@3:使⽤@Before标注在⽅法上⾯,定义了⼀个前置通知,通过value引⽤了上⾯已经定义的切⼊点,表⽰这个通知会对Service1中的所有⽅法⽣效,在通知中可以通过这个类名.⽅法名()引⽤@Pointcut定义的切⼊点,表⽰这个通知对这些切⼊点有效,若@Before 和@Pointcut在⼀个类的时候,直接通过⽅法名()引⽤当前类中定义的切⼊点
@4:这个使⽤@AfterThrowing定义了⼀个异常通知,也是对通过value引⽤了上⾯已经定义的切⼊点,表⽰这个通知会对Service1中的所有⽅法⽣效,若Service1中的⽅法抛出了Exception类型的异常,都会回调afterThrowing⽅法。
来个测试类
package com.javacode2018.aop.demo9;import com.javacode2018.st1.Aspect1;import com.javacode2018.st1.Service1;import org.jun 运⾏输出
前置通知,execution(void com.javacode2018.st1.Service1.m1())我是 m1 ⽅法前置通知,execution(void com.javacode2018.st1.Servic
使⽤是不是特⽅便。
AspectJProxyFactory原理
@Aspect标注的类上,这个类中,可以通过通过@Pointcut来定义切⼊点,可以通过@Before、@Around、@After、
@AfterRunning、@AfterThrowing标注在⽅法上来定义通知,定义好了之后,将@Aspect标注的这个类交给AspectJProxyFactory来
解析⽣成Advisor链,进⽽结合⽬标对象⼀起来⽣成代理对象,⼤家可以去看⼀下源码,⽐较简单,这⾥就不多解释了。
本⽂的重点在@Aspect标注的类上,@Aspect中有2个关键点⽐较重要
@Pointcut:标注在⽅法上,⽤来定义切⼊点,有11种⽤法,本⽂主要讲解这11种⽤法。
@Aspect类中定义通知:可以通过@Before、@Around、@After、@AfterRunning、@AfterThrowing标注在⽅法上来定义通
知,这个下⼀篇介绍。
@Pointcut的12种⽤法
作⽤
⽤来标注在⽅法上来定义切⼊点。
定义
格式:@ 注解(value=“表达标签 (表达式格式)”)
如:
param name@Pointcut("execution(* com.javacode2018.st1.Service1.*(..))")
表达式标签(10种)
execution:⽤于匹配⽅法执⾏的连接点
within:⽤于匹配指定类型内的⽅法执⾏
this:⽤于匹配当前AOP代理对象类型的执⾏⽅法;注意是AOP代理对象的类型匹配,这样就可能包括引⼊接⼝也类型匹配
target:⽤于匹配当前⽬标对象类型的执⾏⽅法;注意是⽬标对象的类型匹配,这样就不包括引⼊接⼝也类型匹配
args:⽤于匹配当前执⾏的⽅法传⼊的参数为指定类型的执⾏⽅法
@within:⽤于匹配所以持有指定注解类型内的⽅法
@target:⽤于匹配当前⽬标对象类型的执⾏⽅法,其中⽬标对象持有指定的注解
@args:⽤于匹配当前执⾏的⽅法传⼊的参数持有指定注解的执⾏
@annotation:⽤于匹配当前执⾏⽅法持有指定注解的⽅法
bean:Spring AOP扩展的,AspectJ没有对于指⽰符,⽤于匹配特定名称的Bean对象的执⾏⽅法
10种标签组成了12种⽤法
1、execution
使⽤execution(⽅法表达式)匹配⽅法执⾏。
execution格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
其中带 ?号的 modifiers-pattern?,declaring-type-pattern?,hrows-pattern?是可选项
ret-type-pattern,name-pattern, parameters-pattern是必选项
modifier-pattern? 修饰符匹配,如public 表⽰匹配公有⽅法
ret-type-pattern 返回值匹配,* 表⽰任何返回值,全路径的类名等
declaring-type-pattern? 类路径匹配
name-pattern ⽅法名匹配,* 代表所有,set*,代表以set开头的所有⽅法
(param-pattern) 参数匹配,指定⽅法参数(声明的类型),(..)代表所有参数,(*,String)代表第⼀个参数为任何值,第⼆个为String类型,(..,String)代表最后⼀个参数是String类型
throws-pattern? 异常类型匹配
举例说明
类型匹配语法
很多地⽅会按照类型的匹配,先来说⼀下类型匹配的语法。
⾸先让我们来了解下AspectJ类型匹配的通配符:
*:匹配任何数量字符
..:匹配任何数量字符的重复,如在类型模式中匹配任何数量⼦包;⽽在⽅法参数模式中匹配任何数量参数(0个或者多个参数) +:匹配指定类型及其⼦类型;仅能作为后缀放在类型模式后边
2、within
⽤法
within(类型表达式):⽬标对象target的类型是否和within中指定的类型匹配
匹配原则
案例
有2个类,⽗⼦关系
⽗类C1
package com.javacode2018.st2;public class C1 { public void m1() { System.out.println("我是m1"); } public void m2() { System.out.prin ⼦类C2
package com.javacode2018.st2;public class C2 extends C1 { @Override public void m2() { super.m2(); } public void m3() { System.来个Aspect类
package com.javacode2018.st2;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Befo 注意@1匹配的类型是C1,也就是说被代理的对象的类型必须是C1类型的才⾏,需要和C1完全匹配
下⾯我们对C2创建代理
@Testpublic void test2(){ C2 target = new C2(); AspectJProxyFactory proxyFactory = new AspectJProxyFactory(); proxyFactory.setTarget(target); proxyF 运⾏输出
我是m1我是m2我是m3
原因是⽬标对象是C2类型的,C2虽然是C1的⼦类,但是within中表达式指定的是要求类型必须是C1类型的才匹配。
如果将within表达式修改为下⾯任意⼀种就可以匹配了
@Pointcut("within(C1+)") @Pointcut("within(C2)")
再次运⾏输出
execution(void com.javacode2018.st2.C1.m1())我是m1execution(void com.javacode2018.st2.C2.m2())我是m2execution(void com.jav
3、this
⽤法
this(类型全限定名):通过aop创建的代理对象的类型是否和this中指定的类型匹配;注意判断的⽬标是代理对象;this中使⽤的表达式必须
是类型全限定名,不⽀持通配符。
匹配原则
如:this(x),则代理对象proxy满⾜下⾯条件时会匹配x.getClass().Class());
案例
来个接⼝
package com.javacode2018.st3;public interface I1 { void m1();}
来个实现类
package com.javacode2018.st3;public class Service3 implements I1 { @Override public void m1() { System.out.println("我是m1"); }}
来个@Aspect类
package com.javacode2018.st3;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotati
测试代码
@Testpublic void test3() { Service3 target = new Service3(); AspectJProxyFactory proxyFactory = new AspectJProxyFactory(); proxyFactory.setTarg
运⾏输出
我是m1proxy是否是jdk动态代理对象:trueproxy是否是cglib代理对象:falsefalse
从输出中可以看出m1⽅法没有被增强,原因:this表达式要求代理对象必须是Service3类型的,输出中可以看出代理对象并不是Service3
类型的,此处代理对象proxy是使⽤jdk动态代理⽣成的。
我们可以将代码调整⼀下,使⽤cglib来创建代码
proxyFactory.setProxyTargetClass(true);
再次运⾏,会发现m2被拦截了,结果如下
execution(void com.javacode2018.st3.Service3.m1())我是m1proxy是否是jdk动态代理
对象:falseproxy是否是cglib代理对象:truetrue
4、target
⽤法
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论