首页 > 解决方案 > 如何使用 Spring Before Advice 增加方法中使用的参数

问题描述

我从我的 Java 语言老师那里得到了一项任务,即在建议之前使用 spring AOP 来增加 Restful web 服务中的计数器(id)。找不到怎么做。我们使用 spring.io 中默认的 restful 应用程序。这是我修改后的代码:

应用:

package sut.ist012m.Ruygo.hello_goodbye;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)

public class Application {

    public static void main(String[] args) {

        final ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
        final Controller Controller = run.getBean(Controller.class);
    }
}

控制器:

package sut.ist012m.Ruygo.hello_goodbye;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {

    private static final String hellotemplate = "Hello, %s!";
    private static final String adrtemplate = "Welcome to %s";
    public AtomicLong counter1 = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name,
                             @RequestParam(value="city", defaultValue="Moscow") String city) {
        return new Greeting(counter1.incrementAndGet(),
                String.format(hellotemplate, name),
                    String.format(adrtemplate, city));
    }

}

问候:

package sut.ist012m.Ruygo.hello_goodbye;

public class Greeting {
    private final long id;
    private final String content;
    private final String adrcontent;

    public Greeting( long id,
                     String content,
                     String adrcontent) {
        this.id = id;
        this.content = content;
        this.adrcontent = adrcontent;
    }

    public long getId() {

        return id;
    }

    public String getContent() {

        return content;
    }

    public String getAdrcontent() {

        return adrcontent;
    }

CounterAspect(由我编写):

package sut.ist012m.Ruygo.hello_goodbye;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CounterAspect {
    long id;
    @Before(value="execution(* sut.ist012m.Ruygo.hello_goodbye.Greeting.*(..)) ")
    public void beforeAdvice(JoinPoint joinPoint){

     }

}

以正常方式,然后您刷新网页,您会看到 id=1,id=2,id=3... 我们希望看到 id=1,11,21,23 等等。我不明白在“public void beforeAdvice”中写什么。Spring 不控制“Greeting”类可以吗?

标签: javaspringspring-bootaopaspectj

解决方案


Spring AOP 只会建议 spring beans。因此,您对“ Spring 不控制“会议”类是否可以? ”(我猜您的意思是 Greeting 在这里)的担忧是有效的。不行,因为您正在尝试建议 Greeting bean。

另请注意,使用 Spring AOP 您不能建议构造函数。参考

如果您的拦截需求包括目标类中的方法调用甚至构造函数,请考虑使用 Spring 驱动的原生 AspectJ 编织,而不是 Spring 的基于代理的 AOP 框架。

要更好地理解以下代码,您必须通读以下参考资料。

@RequestScope

Spring AOP 参考文档

使用 Spring AOP 有多种方法可以实现您的要求,我只是按照您的建议进行操作。

修改了 GreetingController 并自动装配了一个有范围请求的问候 bean。

@RestController
public class GreetingController {
    private static final String hellotemplate = "Hello, %s!";
    private static final String adrtemplate = "Welcome to %s";

    @Autowired
    Greeting greeting;

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name,
            @RequestParam(value = "city", defaultValue = "Moscow") String city) throws Exception {
        greeting.setAdrcontent(String.format(adrtemplate, city));
        greeting.setContent(String.format(hellotemplate, name));
        Greeting g = getTarget(greeting);
        return g;
    }

    // Get the actual target object to avoid exception when returning
    // Comment out this method to observe what happens.
    private Greeting getTarget(Greeting greeting) throws Exception {
        while (AopUtils.isAopProxy(greeting) && greeting instanceof Advised) {
            Object target = ((Advised) greeting).getTargetSource().getTarget();
            greeting = (Greeting) target;
        } 
        return greeting;
    }
}

将 Greeting 类修改为 Spring bean。

@Component
@RequestScope
public class Greeting {
    private long id;
    private String content;
    private String adrcontent;
    
    // Getters and setters
}

反方面

@Aspect
@Component
public class CounterAspect {

    public AtomicLong counter1 = new AtomicLong(1);

    @Before(value = "execution(* sut.ist012m.Ruygo.hello_goodbye.Greeting.*(..)) && target(greeting)")
    public void beforeAdvice(Greeting greeting) {
        if (greeting.getId() == 0) {
            greeting.setId(counter1.getAndAdd(10));
            System.out.println(greeting.getId());
        }
    }

}

此通知针对Greeting bean上的任何公共方法调用执行,并通过参数使实际的Greeting对象可用于通知greeting。(参考:https ://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-ataspectj-advice-params-passing )。

作为工作的一部分,您可以尝试不同的方法来优化此代码。


推荐阅读