哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步
一位上进心十足的【Java ToB端大厂领域博主】!
喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决
✨ 如果有对【java】感兴趣的【小可爱】,欢迎关注我❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
————————————————如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。
由于项目很忙,最近很少有时间更新文章和大家分享,已经很及没更新文章了,让各位久等了。最近忙里偷闲抽空分享一些aop的知识。详细大家对这个很熟悉但也陌生,没有系统的整理过这个知识。
本文主要介绍spring aop中9种切入点表达式的写法,相信不少同学跟我一样,没有系统的整理过aop中的pointcut的表达式。今天我们就抽空讲解一下pointcut表达式的用法和含义。
Spring AOP支持的AspectJ表达式概览:
参数说明
符号 | 含义 |
execution() | 表达式的主体; |
第一个”*“符号 | 表示返回值的类型任意; |
com.sample.service.impl | AOP所切的服务的包名 |
包名后面的”..“ | 表示当前包及子包 |
第二个”*“符号 | 表示类名,*即所有类 |
.*(..) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
基本语法格式为: execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
下面是官网中的一些实例:
Aspect Oriented Programming with Spring :: Spring Framework
execution(public * *(..))
execution(* set*(..))
execution(* com.xyz.service.AccountService.*(..))
拦截 AccountService(类、接口)中定义的所有方法
execution(* com.xyz.service.*.*(..))
拦截 com.xyz.service包中所有类中任意方法,不包含子包中的类
execution(* com.xyz.service..*.*(..))
拦截 com.xyz.service包或者子包中定义的所有方法
// 带?的表示可选
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
下面是execution的简单例子:
有两个IService接口分别有m1和m2方法,现在
ServiceImpl实现两个接口
实现切面Interceptor 切点如下
@Pointcut("execution(* com.ms.aop.execution.ServiceImpl.*(..))")
Interceptor1
public interface IService {
void m1();
}
public interface IService2 {
void m2();
}
package com.ms.aop.execution;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class Interceptor1 {
@Pointcut("execution(* com.ms.aop.execution.ServiceImpl.*(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
log.info("方法执行之前");
Object result = invocation.proceed();
log.info("方法执行完毕");
return result;
}
}
@Slf4j
@Component
public class ServiceImpl implements IService, IService2 {
@Override
public void m1() {
log.info("切入点m1的execution测试!");
}
@Override
public void m2() {
log.info("切入点m2的execution测试!");
}
}
测试类
@ComponentScan(basePackageClasses={Client.class})
@EnableAspectJAutoProxy
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
IService2 service = annotationConfigApplicationContext.getBean(IService2.class);
IService service1 = annotationConfigApplicationContext.getBean(IService.class);
service.m2();
service1.m1();
}
}
执行结果:
15:07:00.304 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
15:07:00.304 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m2的execution测试!
15:07:00.304 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕
15:07:00.305 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
15:07:00.305 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m1的execution测试!
15:07:00.305 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕
分析:
执行结果
17:34:43.297 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
17:34:43.307 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m2的execution测试!
17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕
17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
17:34:43.308 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m1的execution测试!
17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕
使用cglib方式和jdk代理的方式效果是一致的。
实现某些的排除:@Pointcut切入点排除某一些类或者方法不进行拦截
// 扫描controller层
@Pointcut("execution(* com.xx.web.controller..*.*(..)) ")
public void includePointcat() {
}
// 排除controller类
@Pointcut("execution(* com.xx.web.controller.TempController.*(..)) ")
public void excludePointcut() {
}
//切面配置
@AfterReturning("includePointcat() && !excludePointcut()")
public void saveSysLog(JoinPoint joinPoint) throws IOException {
String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
String methodName = joinPoint.getSignature().getName();
logger.info("{}.{} start", className, methodName);
}
includePointcat:切入点为controller下所有类。
excludePointcut:切入点为controller下TempController类。
saveSysLog:切入点为满足 includePointcat且不满足excludePointcut的切入点的范围
表达式格式:包名.* 或者 包名..*
within(com.xyz.service.*)
拦截service包中任意类的任意方法
within(com.xyz.service..*)
拦截service包及子包中任意类的任意方法
within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等
目标对象使用aop之后生成的代理对象必须是指定的类型才会被拦截,注意是目标对象被代理之后生成的代理对象和指定的类型匹配才会被拦截
this(com.xyz.service.AccountService)
例如下面的例子
package com.ms.aop.jthis.demo1;
public interface IService {
void m1();
}
package com.ms.aop.jthis.demo1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ServiceImpl implements IService {
@Override
public void m1() {
log.info("切入点this测试!");
}
}
package com.ms.aop.jthis.demo1;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class Interceptor1 {
@Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")
public void pointcut() {
}
@Around("pointcut()")
public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
log.info("方法执行之前");
Object result = invocation.proceed();
log.info("方法执行完毕");
return result;
}
}
package com.ms.aop.jthis.demo1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@ComponentScan(basePackageClasses = {Client.class})
@EnableAspectJAutoProxy
@Slf4j
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
IService service = annotationConfigApplicationContext.getBean(IService.class);
service.m1();
log.info("{}", service instanceof ServiceImpl);
}
}
注意这里的代理对象类型的定义
@Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")
结果:
10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.ServiceImpl - 切入点this测试!
10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.Client - false
target(com.xyz.service.AccountService)
目标对象为AccountService类型的会被代理
package com.ms.aop.target;
public interface IService {
void m1();
}
package com.ms.aop.target;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ServiceImpl implements IService {
@Override
public void m1() {
log.info("切入点target测试!");
}
}
package com.ms.aop.target;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class Interceptor1 {
@Pointcut("target(com.ms.aop.target.ServiceImpl)")
public void pointcut() {
}
@Around("pointcut()")
public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
log.info("方法执行之前");
Object result = invocation.proceed();
log.info("方法执行完毕");
return result;
}
}
package com.ms.aop.target;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@ComponentScan(basePackageClasses = {Client.class})
@EnableAspectJAutoProxy
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
IService service = annotationConfigApplicationContext.getBean(IService.class);
service.m1();
}
}
执行结果
10:49:01.674 [main] INFO com.ms.aop.target.Interceptor1 - 方法执行之前
10:49:01.674 [main] INFO com.ms.aop.target.ServiceImpl - 切入点target测试!
10:49:01.674 [main] INFO com.ms.aop.target.Interceptor1 - 方法执行完毕
@Pointcut("args(com.ms.aop.args.demo1.UserModel)")
匹配只有一个参数,且类型为 com.ms.aop.args.demo1.UserModel
args(type1,type2,typeN)
@Pointcut("args(com.ms.aop.args.demo1.UserModel,..)")
匹配第一个参数类型为 com.ms.aop.args.demo1.UserModel的所有方法, ..
表示任意个参数
@target(com.ms.aop.jtarget.Annotation1)
目标对象中包含 com.ms.aop.jtarget.Annotation1注解,调用该目标对象的任意方法都会被拦截
@within(com.ms.aop.jwithin.Annotation1)
声明有 com.ms.aop.jwithin.Annotation1注解的类中的所有方法都会被拦截
@annotation(com.ms.aop.jannotation.demo2.Annotation1)
被调用的方法包含指定的注解
注意:是 方法参数所属的类型上有指定的注解,不是方法参数中有注解
@args(com.ms.aop.jargs.demo1.Anno1)
@args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2)
@args(com.ms.aop.jargs.demo2.Anno1,..)
下面是切面的一个项目应用 实现服务日志的记录
@Aspect
@Component
@Slf4j
public class SealServiceControllerAspect {
@Autowired
private InterfaceLogDao interfaceLogDao;
/**
* 日志入库异步模式,线程池用fk pool
*/
private static ForkJoinPool LOG_THREAD_POOL = new ForkJoinPool(4);
@Pointcut("" +
"execution(* com.xx.seal.RestSignContractResource.*(..))" +
"|| execution(* com.xx.controller.seal.ContractProcessSignResource.*(..))"
)
public void pointCuts() {
}
@Around("pointCuts()")
public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
final InterfaceLogPO po = new InterfaceLogPO();
Object[] inParam = invocation.getArgs();
JSONArray inParams = new JSONArray();
if (inParam != null) {
Arrays.stream(inParam).forEach(p -> {
try {
if (p instanceof String||
p instanceof Number ||
p instanceof Boolean
){
inParams.add(p);
}else if (JSONUtils.isArray(p)) {
try {
inParams.add(JSONArray.fromObject(p));
} catch (Exception e) {
log.warn("==>this aspect[{}] can not get input param ", invocation.getSignature().getName());
}
} else {
try {
inParams.add(JSONObject.fromObject(p));
} catch (Exception e) {
log.warn("==>this aspect[{}] can not get input param ", invocation.getSignature().getName());
}
}
} catch (Exception e) {
log.warn("==>aspect error :can not fetch args --->{}", e.getMessage());
}
});
}
if (invocation.getTarget().getClass().getName().endsWith("Resource") ||
invocation.getTarget().getClass().getName().endsWith("Controller")
) {
po.setCategory("REST");
} else {
po.setCategory("SERVICE");
}
po.setAction(invocation.getTarget().getClass().getName() + "@" + invocation.getSignature().getName());
po.setActionDesc("");// 从swagger的@Api注解中取
po.setInputParam(inParams.toString());
po.setTs(new Date());
po.setCallStatus("OK");
po.setUserId(InvocationInfoProxy.getUserid());
po.setUserName(InvocationInfoProxy.getUsername());
Object result = null;
try {
result = invocation.proceed();
} catch (Throwable throwable) {
po.setCallStatus("ERR");
StringBuilder sb = new StringBuilder( throwable.getMessage()+"\n");
sb.append(ExceptionUtils.getFullStackTrace(throwable)).append("\n");
po.setErrorMessage(sb.toString());
throw throwable;
} finally {
if (result != null) {
if (result instanceof String ||
result instanceof Number ||
result instanceof Boolean){
po.setOutputResult(result.toString());
}else if (JSONUtils.isArray(result)) {
try {
po.setOutputResult(
JSONArray.fromObject(result).toString()
);
} catch (Exception e) {
log.warn("==>this aspect[{}] can not get output result ", invocation.getSignature().getName());
}
} else {
try {
po.setOutputResult(
JSONObject.fromObject(result).toString()
);
} catch (Exception e) {
log.warn("==>this aspect[{}] can not get output result", invocation.getSignature().getName());
}
}
/*
这部分以后要改造成基于接口的插件式!!!
*/
if (result instanceof Result && ((Result) result).getData() != null) {
//后续考虑引入策略模式
if (((Result) result).getData() instanceof ResultContractProcessDTO
) {
String bizKey = ((ResultContractProcessDTO) ((Result) result).getData()).getProcessId();
po.setBizKey(bizKey);
} else {
try {
JSONObject outputResult = JSONObject.fromObject(((Result) result).getData());
po.setBizKey(outputResult.getString("id"));
} catch (Exception e) {
log.warn("==>this aspect[{}] can not get biz key", invocation.getSignature().getName());
}
}
}
if (result instanceof ResultContractProcessDTO){
String bizKey = ((ResultContractProcessDTO) result).getProcessId();
po.setBizKey(bizKey);
}
}
interfaceLogDao.save(po);
}
return result;
}
}
希望这个文章能让大家有所收获,哪怕有一点点的收获,这样我写这个文章也就值得了。
创作不易,请给小编点个赞吧,一个字一个字敲下来很费时间和精力的