Spring AOP advice not getting executed - java

I'm trying to get advice executed, and it isn't working. I am trying to do it without an application context.
This is my Rest Controller:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import aspect.exception.GreetingsNotFoundException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Aspect class:
#Aspect
//#ComponentScan
public class AfterThrowingException implements ThrowsAdvice {
// Obtain a suitable logger.
private static Logger logger = LoggerFactory.getLogger(AfterThrowingException.class);
#Before("execution(hello.GreetingController.greeting()")
public void logBefore(JoinPoint joinPoint){
System.out.println("Inside AfterThrowingException.logBefore()");
}
Configuration class:
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public AfterThrowingException afterThrowingException() {
return new AfterThrowingException();
}
}

The format of execution pointcut is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
Your controller method takes parameters, but it is not in the definition
Try:
#Before("* execution(hello.GreetingController.greeting(..)")
You need to proceed after completing of #Before advice
jointPoint.proceed();
Your names are out of sync

Related

How to write test case for Spring boot controller using spock framework

I have written a simple rest application using Spring boot, now I want to write a unit test case for my controllers using the Spock framework. I already tried like this
, but it did not Succeed
My controller class
public class AbcController extends BaseController{
private final AbcService abcService;
public UserController(AbcService abcService) {
this.abcService= abcService;
}
#GetMapping("/all")
public ResponseEntity<List<AbcDto>> getAll(#RequestHeader(name = "Authorization")
String accessToken) {
return getResponseEntity (abcService.getAll(accessToken));
}
}
Service
public class AbcServiceImpl implementsAbcService {
public List<AbcDto> getAll(String accessKey) {
try {
List<AbcDto> abc= AbcServiceClient.getAll (accessKey);
return users;
} catch (FeignException exception) {
throw exception;
}
}
}
My test case
class AbcControllerSpecification extends Specification{
def "Should test getAll"() {
given: "mock service"
AbcDto abc= new AbcDto(null, "cm#gmail.com", true, true, null);
List<AbcDto> response = Arrays.asList(abc)
AbcController controller
ResponseEntity response
controller = new AbcController(abcService: service)
AbcUserServiceClient abcServiceClient = Mock() {
getUAll("bearer") >> response
}
AbcServiceImpl abcService = new AbcServiceImpl(abcServiceClient);
when: "call abcService getAll"
response = controller.getAll(abcServiceClient);
then:"return all List"
result == response
}
}
Have a look at the examples (WebMVC, SpringBean) in the Spock repository. Also make sure that you have a dependency on the spock-spring module in addition to the spock-core dependency.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class HelloWorldService {
#Value("${name:World}")
private String name;
public String getHelloMessage() {
return "Hello " + this.name;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
#RestController
public class HelloWorldController {
private HelloWorldService service;
#Autowired
public HelloWorldController(HelloWorldService service) {
this.service = service;
}
#RequestMapping("/")
public String hello() {
return service.getHelloMessage();
}
}
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
#WebMvcTest
class WebMvcTestIntegrationSpec extends Specification {
#Autowired
MockMvc mvc
#SpringBean
HelloWorldService helloWorldService = Stub()
def "spring context loads for web mvc slice"() {
given:
helloWorldService.getHelloMessage() >> 'hello world'
expect: "controller is available"
mvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(status().isOk())
.andExpect(content().string("hello world"))
}
}

How to create a collection of objects in SpringBoot without having the database?

I am new to SpringBoot. I don't know how to create a few objects of the same type in the way that enables to use this objects later for example in the controller.
Let's say I would like to create collection/list of objects (let's say collection of Rabbits) when the application starts:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// I would like to create here a collection
// or a list of objects (let's say collection of Rabbits)
}
}
I would like to have the possibility to use this objects later in the controller to get some information (obtained for example by index in the list).
What is the right way to keep state of my model without having a database?
Ignoring synchronization issues.
You can create a List and inject into your controllers.
or what I like to do is wrap that in a repository. This insulates you from the underlying datasource and it can be changed later.
Note that synchronization is important for this type of data structure as you can have many threads updating the repository.
package com.example.demo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
#Bean
public RabbitRepository rabbitRepository() {
RabbitRepository rabbitRepository = new RabbitRepository();
rabbitRepository.add("Bugs");
rabbitRepository.add("Flopsy");
return rabbitRepository;
}
public static class RabbitRepository {
private List<Rabbit> rabbits = Collections.synchronizedList(new ArrayList<Rabbit>());
public List<Rabbit> getAll() {
return rabbits;
}
public Rabbit add(String rabbitName) {
Rabbit rabbit = new Rabbit(rabbitName);
this.rabbits.add(rabbit);
return rabbit;
}
public Optional<Rabbit> findById(int id) {
return this.rabbits.stream().filter(r-> r.getId() == id).findFirst();
}
}
public static class Rabbit {
private final String name;
private final int id;
private static AtomicInteger counter = new AtomicInteger();
public Rabbit(String name) {
super();
this.name = name;
this.id = counter.incrementAndGet();
}
public String getName() {
return name;
}
public int getId() {
return this.id;
}
}
#RestController
#RequestMapping("/rabbits")
public static class RabbitController {
private final RabbitRepository repository;
public RabbitController(final RabbitRepository repository) {
this.repository = repository;
}
#GetMapping
public List<Rabbit> getAll() {
return repository.getAll();
}
#PostMapping("/{name}")
//You can also use requestparam / requestbody and probably should
public Rabbit addRabbit(#PathVariable("name") String name) {
return repository.add(name);
}
#GetMapping("/id/{id}")
public Optional<Rabbit> findById(#PathVariable("id") int id) {
return repository.findById(id);
}
}
}
Curl tests
➜ ~ curl localhost:8080/rabbits
[{"name":"Bugs","id":1},{"name":"Flopsy","id":2}]%
➜ ~ curl localhost:8080/rabbits/id/2
{"name":"Flopsy","id":2}%
➜ ~ curl -XPOST localhost:8080/rabbits/Babs
{"name":"Babs","id":3}%
➜ ~ curl localhost:8080/rabbits
[{"name":"Bugs","id":1},{"name":"Flopsy","id":2},{"name":"Babs","id":3}]%
Using spring you need to create objects in the context of spring, otherwise spring created instances can't find them. A solution can be putting them in a class annotated with #Configuration and retrieve them with the annotation #Autowired.
The annotation #Configuration:
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
You can use it as follow:
#Configuration
public class MyConfiguration {
#Bean
public MyClass getMyClass() {
MyClass myClass = ...
...
return myClass;
}
}
#Controller
public class MyController {
#Autowired
private MyClass myClass;
public void method() {
// Use myClass instance as you like
}
}
You can also generate a standard java.util.List, but in this case is better to give a name to the bean generated, for example:
#Configuration
public class MyConfiguration {
#Bean("myname") // To give an explicit name to the List
public List getMyList() {
List myList = ...
...
return myList;
}
}
#Controller
public class MyController {
#Autowired
#Qualifier("myname") // To retrieve a List with a specific name
private List myList;
public void method() {
// Use myList instance as you like
}
}
You need something like this i guess !
#Component
public class RabbitListHolder {
private List<Rabbit> rabbits = new ArrayList<Rabbit>
public void initializeList(){
rabbits.add(new Rabbit('white', 3));
...
}
}
#Controller
public class RabbitsRessource{
#Autowired
RabbitListHolder rabbitListHolder;
...
#GetMapping("/rabbits")
public List<Rabbits> getRabbits(){
rabbitListHolder.initializeList();
return rabbitListHolder.getRabbits();
}
}

Secondary type dependency injection does not work in spring boot

As per the documentation, spring boot will automatically check the bean class object created in any classes annotated with #Configuration & will override the default bean of that class & return the object with any properties that are injected as it is defined. But when i test this application in junit, it does not return any value that is being injected. All my classes are defined in the same package My code is as below,
//Engine class
package com.test.simpletest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class Engine {
private String msg;
public Engine() {
System.out.println("Engine class is being called");
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
//Test configuration class
package com.test.simpletest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TestConfiguration{
#Bean
public Engine engine() {
Engine eng = new Engine();
eng.setMsg("Message is being called");
return eng;
}
}
//Spring boot main app
package com.test.simpletest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SimpleTestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleTestExampleApplication.class, args);
}
}
//JUnit Test class
package com.test.simpletest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SimpleTestExampleApplicationTests {
#Autowired
private Engine engine;
#Test
public void contextLoads() {
engine.getMsg();
//Both above and below approach does not work
// ApplicationContext apx = new
AnnotationConfigApplicationContext(TestConfiguration.class);
// Engine engine = (Engine)apx.getBean(Engine.class);
// engine.getMsg();
}
}
Please help me in finding a solution to the above problem.
DemoApplication
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Engine
public class Engine {
private String msg;
public Engine() {
System.out.println("Engine class is being called");
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
TestConfiguration
#Configuration
public class TestConfiguration {
#Bean
public Engine getEngine() {
Engine eng = new Engine();
eng.setMsg("Message is being called");
return eng;
}
}
DemoApplicationTests
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestConfiguration.class)
public class DemoApplicationTests {
#Autowired
private Engine engine;
#Test
public void contextLoads() {
System.out.println("engine : " + engine.getMsg());
}
}
Output
Engine class is being called
engine : Message is being called
Can you please remove #Component from Engine class and try again. I guess it’s should work fine.

Custom deserializer for requests with content-type application/x-www-form-urlencoded in Spring Boot

I want to write a custom deserializer for some parameters in the requests of type application/x-www-form-urlencoded like used in case of requests of type application/json, with #JsonDeserialize(using = AbcDeserializer.class) annotation. I am using spring boot and Jackson, although I figured out that Jackson is not used here.
I tried figuring out how spring deserializes object by default. But couldn't find a way.
How does spring deserialize a request of type application/x-www-form-urlencoded by default?
Can I override this deserialization, preferrably by using some annotation on parameters that need special handling?
My solution is based on custom ConditionalGenericConverter. It works with #ModelAttribute. Let's see whole implementation.
Application bootstrap example.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication
public class DemoApplication {
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Base64JsonToObjectConverter());
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Here is custom annotation.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Base64Encoded {
}
Next we need implementation of the converter. As you can see, converter converts only String -> Object, where Object field must be annotated with Base64Encoded annotation.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Base64;
import java.util.Collections;
import java.util.Set;
#Component
public class Base64JsonToObjectConverter implements ConditionalGenericConverter {
private final ObjectMapper objectMapper;
private final Base64.Decoder decoder;
public Base64JsonToObjectConverter() {
this.objectMapper = new ObjectMapper();
this.decoder = Base64.getDecoder();
}
#Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return targetType.hasAnnotation(Base64Encoded.class);
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Object.class));
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
try {
byte[] decodedValue = this.decoder.decode(string);
return this.objectMapper.readValue(decodedValue, targetType.getType());
} catch (IllegalArgumentException | IOException e) {
throw new ConversionFailedException(sourceType, targetType, source, e);
}
}
}
Here is an example of POJO (see the annotated field) and REST controller.
import com.example.demo.Base64Encoded;
public class MyRequest {
private String varA;
#Base64Encoded
private B varB;
public String getVarA() {
return varA;
}
public void setVarA(String varA) {
this.varA = varA;
}
public B getVarB() {
return varB;
}
public void setVarB(B varB) {
this.varB = varB;
}
}
import com.example.demo.domain.MyRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DemoController {
#RequestMapping(path = "/test", method = RequestMethod.POST)
public MyRequest test(#ModelAttribute MyRequest myRequest) {
return myRequest;
}
}

Spring AOP Logging and Cache

I'm logging method input and output parameters by a simple Aspect.
package com.mk.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
#Aspect
#Component
public class LoggingAspect {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
}
which is attached by this Annotation.
package com.mk.cache;
public #interface LoggedIO {
}
I use this mechanism to log inputs and outputs of methods like this (notice #LoggedIO):
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
#Service
#LoggedIO
public class CachedService {
private static final Logger LOGGER = LoggerFactory.getLogger(CachedService.class);
#Cacheable("myCacheGet")
public int getInt(int input) {
LOGGER.info("Doing real work");
return input;
}
}
I also use Spring Cache. Here is the example application.
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CacheApplication implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheApplication.class);
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
#Autowired
private CachedService cachedService;
#Override
public void run(String... args) throws Exception {
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
}
}
The output looks like this:
LOG by AOP - invoking getInt([1])
Doing real work
LOG by AOP - result of getInt=1
cachedService.getInt(1)=1
cachedService.getInt(1)=1
My problem is, that when I call LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1)); for the second time, the cached value is used, but the input and output parameters are not logged, as the cache is the first wrapper. Is it possible to somehow configure the LoggingAspect to be the first wrapper, so I will be able to use both AOP logging and both Spring Cache?
Just implement spring Ordered interface and in getOrder() method return 1.
#Aspect
#Component
public class LoggingAspect implements Ordered {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
#Override
public int getOrder() {
return 1;
}
}
Read more here. Chapter 11.2.7

Categories