java - 使用注释和 lombok 对字段的切入点
问题描述
简介:我正在使用 Java 和 Spring boot 2.2.2 和 Lombok
我得到了这个示例类:
package com.example.demo.classz;
import com.example.demo.pack.MyAnnotation;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
public class FooClass {
@NonNull
@MyAnnotation
private String str;
private String uninterceptedString;
}
我想拦截对使用@MyAnnotation 注释的“get”/“set”方法的所有调用。为了管理这个我创建了这个界面:
package com.example.demo.pack;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {}
并且这个类做一些操作。
package com.example.demo.pack;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AnnotatedFieldAspect {
@Before("execution(* com.example.demo.classz.*.get*(..)) && @annotation(com.example.demo.pack.MyAnnotation)")
public void interceptRead(JoinPoint thisJoinPoint) {
log.info(thisJoinPoint.toString());
}
@Before("execution(* com.example.demo.classz.*.set*(..)) && @annotation(com.example.demo.pack.MyAnnotation) && args(newValue)")
public void interceptWrite(JoinPoint thisJoinPoint, Object newValue) {
log.info(thisJoinPoint + " -> " + newValue);
}
}
最后为了测试所有,我做了一个简单的控制器
package com.example.demo;
import com.example.demo.classz.FooClass;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/")
public String test() {
FooClass fooClass = new FooClass("Foo");
fooClass.setStr("Foo2");
fooClass.getStr();
return "Hi";
}
}
我无法激活那些切入点,我不明白为什么。你可以帮帮我吗?
我已经在 StackOverflow 上看到了一些类似的问题,如下所示: - Spring AOP Pointcut expression for annotated field - @AspectJ pointcut for all methods of a class with specific annotation
还有其他一些,但即使有他们的解决方案,我也没有成功。
一些配置的东西:
@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
build.gradle 中的依赖部分:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.session:spring-session-core'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.2.2.RELEASE'
// compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
谢谢!
解决方案
正如 kriegaex 所提到的,FooClass 不是由 Spring Container 管理的 bean。你只能拦截一个spring bean。
如果切入点表达式有效,则对代码更改进行更改将使 setter 方法被拦截。
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Component
@Scope(scopeName="prototype")
public class FooClass {
@NonNull
@MyAnnotation
private String str;
private String uninterceptedString;
}
并从应用程序上下文中获取 FooClass bean。
FooClass fooClass = ctx.getBean(FooClass.class,"Foo");
fooClass.setStr("Foo2");
fooClass.getStr();
阅读更多关于 @Component 和 @Scope 以了解所做的更改。
请注意,只有方法调用才能被 Spring AOP 拦截。在此处阅读文档
== Spring AOP 功能和目标
Spring AOP 当前仅支持方法执行连接点(建议在 Spring bean 上执行方法)。没有实现字段拦截,尽管可以在不破坏核心 Spring AOP API 的情况下添加对字段拦截的支持。如果您需要建议字段访问和更新连接点,请考虑使用 AspectJ 等语言。
出于同样的原因,以下 Spring AOP 切入点表达式在您的情况下是有效的
@Before("execution(* com.example.demo.classz.*.get*(..))")
public void interceptRead(JoinPoint thisJoinPoint) {
log.info(thisJoinPoint.toString());
}
@Before("execution(* com.example.demo.classz.*.set*(..)) && args(newValue)")
public void interceptWrite(JoinPoint thisJoinPoint, Object newValue) {
log.info(thisJoinPoint + " -> " + newValue);
}
推荐阅读
- jwt - 使用 OpenId (Cognito) 进行身份验证后,如何在 Blazor WebAssembly 中获取 id_token?
- powershell - Powershell 2 文件执行脚本
- python - 如何在txt文件python的每一行之后添加\ n
- sql - 将整数参数传递到 SQL Server 中的 ORDER BY 子句
- visual-studio-code - PlatformIO 不会在 VS Code 中创建 firmware.bin
- typescript - 我想按 id 过滤
- sql - SQL 根据其他行值排除行
- python - 从 EfficientNet Tensorflow 中提取特征
- google-bigquery - BigQuery SDK TypeError:__init__() 接受 2 个位置参数,但给出了 3 个
- python - 如何单击并拖动已插入 tkinter 画布上另一个图像顶部的图像