@Pointcut使用@annotation带参数
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
@Pointcut使⽤@annotation带参数
⽬录
AOP的基本概念
建议先阅读
1. Advice(通知、切⾯):某个连接点所采⽤的处理逻辑,也就是向连接点注⼊的代码, AOP在特定的切⼊点上执⾏的增强处理。
@Before:标识⼀个前置增强⽅法,相当于BeforeAdvice的功能.
@After: final增强,不管是抛出异常或者正常退出都会执⾏.
@AfterReturning:后置增强,似于AfterReturningAdvice, ⽅法正常退出时执⾏.
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice.
@Around:环绕增强,相当于MethodInterceptor.
2. JointPoint(连接点):程序运⾏中的某个阶段点,⽐如⽅法的调⽤、异常的抛出等。
3. Pointcut(切⼊点): JoinPoint的集合,是程序中需要注⼊Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切
⼊点表达式。
4. Advisor(增强):是PointCut和Advice的综合体,完整描述了⼀个advice将会在pointcut所定义的位置被触发。
5. @Aspect(切⾯): 通常是⼀个类的注解,⾥⾯可以定义切⼊点和通知
6. AOP Proxy:AOP框架创建的对象,代理就是⽬标对象的加强。
Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接⼝,后者
基于⼦类。
<aop:aspectj-autoproxy/> // 开启⾃动代理
<aop:config proxy-target-class="true">
<aop:pointcut id="servicePointcut" expression="execution(* com.cpic..*Service.*(..))" />
<aop:advisor pointcut-ref="servicePointcut" advice-ref="txAdvice" order="3" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="list*" read-only="true" />
<!-- log⽅法会启动⼀个新事务 -->
<tx:method name="log*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" />
</tx:attributes>
</tx:advice>
// OK所以⼀个Spring增强(advisor)=切⾯(advice)+切⼊点(PointCut)
Pointcut(切⼊点)
表⽰式(expression)和签名(signature)
//Pointcut表⽰式
@Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
//Point签名
private void log(){}
由下列⽅式来定义或者通过 &&、 ||、 !、的⽅式进⾏组合:
execution:⽤于匹配⽅法执⾏的连接点;
within:⽤于匹配指定类型内的⽅法执⾏;
this:⽤于匹配当前AOP代理对象类型的执⾏⽅法;注意是AOP代理对象的类型匹配,这样就可能包括引⼊接⼝也类型匹配;
target:⽤于匹配当前⽬标对象类型的执⾏⽅法;注意是⽬标对象的类型匹配,这样就不包括引⼊接⼝也类型匹配;
args:⽤于匹配当前执⾏的⽅法传⼊的参数为指定类型的执⾏⽅法;
@within:⽤于匹配所以持有指定注解类型内的⽅法;
@target:⽤于匹配当前⽬标对象类型的执⾏⽅法,其中⽬标对象持有指定的注解;
@args:⽤于匹配当前执⾏的⽅法传⼊的参数持有指定注解的执⾏;
@annotation:⽤于匹配当前执⾏⽅法持有指定注解的⽅法;
execution格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
其中后⾯跟着“?”的是可选项
括号中各个pattern分别表⽰:
修饰符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern):可以为*表⽰任何返回值, 全路径的类名等
类路径匹配(declaring-type-pattern?)
⽅法名匹配(name-pattern):可以指定⽅法名或者代表所有, set 代表以set开头的所有⽅法
参数匹配((param-pattern)):可以指定具体的参数类型,多个参数间⽤“,”隔开,各个参数也可以⽤"*" 来表⽰匹配任意类型的参数,".."表⽰零个或多个任意参数。
如(String)表⽰匹配⼀个String参数的⽅法;(*,String) 表⽰匹配有两个参数的⽅法,第⼀个参数可以是任意类型,⽽第⼆个参数是String类型。
异常类型匹配(throws-pattern?)
execution例⼦
任意公共⽅法的执⾏:execution(public * *(..))
任何⼀个以“set”开始的⽅法的执⾏:execution(* set*(..))
AccountService 接⼝的任意⽅法的执⾏:execution(* com.xyz.service.AccountService.*(..))
定义在service包⾥的任意⽅法的执⾏: execution(* com.xyz.service..(..))
定义在service包和所有⼦包⾥的任意类的任意⽅法的执⾏:execution(* com.xyz.service...(..))
第⼀个表⽰匹配任意的⽅法返回值, ..(两个点)表⽰零个或多个,第⼀个..表⽰service包及其⼦包,第⼆个表⽰所有类, 第三个*表⽰所有⽅法,第⼆个..表⽰⽅法的任意参数个数
定义在pointcutexp包和所有⼦包⾥的JoinPointObjP2类的任意⽅法的执⾏:execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))") pointcutexp包⾥的任意类: within(com.test.spring.aop.pointcutexp.*)
pointcutexp包和所有⼦包⾥的任意类:within(com.test.spring.aop.pointcutexp..*)
实现了Intf接⼝的所有类,如果Intf不是接⼝,限定Intf单个类:this(com.test.spring.aop.pointcutexp.Intf)
当⼀个实现了接⼝的类被AOP的时候,⽤getBean⽅法必须cast为接⼝类型,不能为该类的类型
带有@Transactional标注的所有类的任意⽅法:
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
带有@Transactional标注的任意⽅法:@annotation(org.springframework.transaction.annotation.Transactional)
@within和@target针对类的注解,@annotation是针对⽅法的注解
参数带有@Transactional标注的⽅法:@args(org.springframework.transaction.annotation.Transactional)
参数为String类型(运⾏是决定)的⽅法: args(String)
JoinPoint
常⽤的⽅法:
Object[] getArgs:返回⽬标⽅法的参数
Signature getSignature:返回⽬标⽅法的签名
Object getTarget:返回被织⼊增强处理的⽬标对象
Object getThis:返回AOP框架为⽬标对象⽣成的代理对象
当使⽤@Around处理时,需要将第⼀个参数定义为ProceedingJoinPoint类型,该类是JoinPoint的⼦类。
织⼊
@AfterReturning(pointcut="execution(* com.abc.service.*.access*(..)) && args(time, name)",
returning="returnValue")
public void access(Date time, Object returnValue, String name) {
System.out.println("⽬标⽅法中的参数String = " + name);
System.out.println("⽬标⽅法中的参数Date = " + time);
System.out.println("⽬标⽅法的返回结果returnValue = " + returnValue);
}
表达式中增加了args(time, name)部分,意味着可以在增强处理的签名⽅法(access⽅法)中定义"time"和"name"两个属性。
这两个形参的类型可以随意指定(access⽅法中指定),但⼀旦指定了这两个参数的类型,则这两个形参类型将⽤于限制该切⼊点只匹配第⼀个参数类型为Date,第⼆个参数类型为String的⽅法(⽅法参数个数和类型若有不同均不匹配);access⽅法只需要满⾜"time", "name"参数的顺序和pointcut中args(param1, param2)的顺序相同即可,"returnValue"位置顺序⽆所谓。
//将被access⽅法匹配
public String accessAdvice(Date d, String n) {
System.out.println("⽅法:accessAdvice");
return "aa";
}
切⾯执⾏顺序
⼀个⽅法只被⼀个Aspect类拦截
正常:
异常:
同⼀个⽅法被多个Aspect类拦截
优先级⾼的切⾯类⾥的增强处理的优先级总是⽐优先级低的切⾯类中的增强处理的优先级⾼。
在“进⼊”连接点时,最⾼优先级的增强处理将先被织⼊(eg.给定的两个不同切⾯类Before增强处理中,优先级⾼的那个会先执⾏);在“退出”连接点时,最⾼优先级的增强处理会最后被织⼊(eg.给定的两个不同切⾯类After增强处理中,优先级⾼的那个会后执⾏)。
eg.优先级为1的切⾯类Bean1包含了@Before,优先级为2的切⾯类Bean2包含了@Around,虽然@Around优先级⾼于@Before,但由于Bean1的优先级⾼于Bean2的优先级,因此Bean1中的@Before先被织⼊。
Spring提供了如下两种解决⽅案指定不同切⾯类⾥的增强处理的优先级:
1. 让切⾯类实现org.springframework.core.Ordered接⼝:实现该接⼝的int getOrder()⽅法,该⽅法返回值越⼩,优先级越⾼
2. 直接使⽤@Order注解来修饰⼀个切⾯类:使⽤这个注解时可以配置⼀个int类型的value属性,该属性值越⼩,优先级越⾼
同⼀个切⾯类⾥的两个相同类型的增强处理在同⼀个连接点被织⼊时,Spring AOP将以随机的顺序来织⼊这两个增强处理,没有办法指定它们的织⼊顺序。
即使给这两个 advice 添加了 @Order 这个注解,也不⾏!
eg.
spring的xml开启AOP配置:
<aop:config proxy-target-class="false" />
<aop:aspectj-autoproxy />
<bean id="opLogAspectj" class="com.noob.aspectj.OpLogAspectj"></bean>
package com.noob.aspectj;
import lombok.extern.slf4j.Slf4j;
import ng.ProceedingJoinPoint;
import ng.annotation.Around;
import ng.annotation.Aspect;
import ng.annotation.Pointcut;
import ng.reflect.MethodSignature;
import com.noob.annotation.OpLog;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLog {
/**
* 操作类型
*/
int opModule();
/**
* 是否批量
*/
String batch();
/**
* 操作来源(页⾯+操作)
*/
String source();
}———————————————————————————————————————————————————————————————————————————————@Aspect
@Slf4j
public class OpLogAspectj {
@Pointcut(value = "@annotation(com.noob.annotation.OpLog)")
public void methodPointcut(){}
/**
* 对带注解@OpLog的⽅法进⾏切⾯,并获取到注解的属性值
*/
@Around(value= "methodPointcut() && @annotation(opLog)" , argNames="opLog")
public Object around(ProceedingJoinPoint point, OpLog opLog) throws Throwable{
Object obj = null;
Object[] args = point.getArgs();
try {
obj = point.proceed(args);
} catch (Throwable e) {
log.error("⽅法执⾏异常", e);
}
long endTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) point.getSignature();
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
return obj;
}
@Pointcut(value = "@annotation(org.apache.shiro.authz.annotation.RequiresPermissions)")
public void methodPointcut2(){}
/**
* 配置指定位置指定类型的参数
*/
@Around(value= "methodPointcut2() && (args(request, ..) || args(.., request))")
public Object around2(ProceedingJoinPoint point, HttpServletRequest request) throws Throwable{
Object obj = null;
Object[] args = point.getArgs();
try {
SsoUser ssoUser = SsoSession.getCurrentUser(request);
obj = point.proceed(args);
} catch (Throwable e) {
log.error("⽅法执⾏异常", e);
}
return obj;
}
}
进⼊切⾯的两种⽅法:
@OpLog(batch = YesOrNo.NO, source = FuncPointEnum.GROUP_ADD)
public Response<Integer> add(RuleGroupModifyReq addModel) {
// 业务逻辑
}———————————————————————————————————————————————————————————————————————————————@RequestMapping(value = "/delete")
@ResponseBody
@RequiresPermissions("rule:delete")
public Response<Integer> delete(
@RequestBody Map<String, String> params,
HttpServletRequest request) {
// 业务逻辑
}。