首页 > 解决方案 > 使用注释和 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'
    }
}

谢谢!

标签: javaspringspring-bootspring-aoplombok

解决方案


正如 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 以了解所做的更改。

@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);
    }

推荐阅读