0
votes

I got a task from my Java-language teacher to increase counter (id) in Restful web-service, using spring AOP before advice. Can't find how to do it. We use the default restful application from spring.io. Here is my modified code:

Application:

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

Controller:

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

}

Greeting:

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(written by me):

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){

     }

}

In normal way then you refresh web-page you see id=1,id=2,id=3... We want to see id=1,11,21,23 and so on. I can't understand what to write in "public void beforeAdvice". And is it OK that Spring doesn't control the "Greeting" class?

2

2 Answers

1
votes

Spring AOP will only advice spring beans . So your concern that "And is it OK that Spring doesn't control the "Meeting" class?" ( I guess you meant Greeting here) is valid. It is not OK , since you are trying to advice a Greeting bean.

Also note that using Spring AOP you cannot advice a constructor . Reference

If your interception needs include method calls or even constructors within the target class, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework.

To understand the following code better , you must read through the following references.

@RequestScope

Spring AOP reference documentation

There are multiple ways to implement your requirement using Spring AOP , I am just following the idea you had to advice.

Modified the GreetingController and autowired a greeting bean which has a scope request.

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

Modified the Greeting class to be a Spring bean.

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

CounterAspect

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

}

This advice executes for any public method invocation on Greeting bean and makes the actual Greeting object available to the advice through the greeting parameter. (Refer : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-ataspectj-advice-params-passing ).

You can try out different ways to optimize this code as part of your work.

0
votes

It's not possible to modify arguments/parameters using before advice, you have to use around advice to modify arguments.

@Around(value="execution(* sut.ist012m.Ruygo.hello_goodbye.Greeting.*(..)) ")
public void aroundAdvice(ProceedingJoinPoint pjp){
   Object[] args = pjp.getArgs();
   // you can modify it by modifying args
   args[0] = .....; // modify your first parameter (id)
   args[1] = .....; // modify your second parameter (content)
   args[2] = .....; // modify your third parameter (adrcontent)
   // call proceed method from pjp and pass args as its parameter
   pjp.proceed(args);
}