#Autowire returns null after adding Spring AOP - java

After adding Spring AOP to my Spring Boot project, the following aspect produces a NullPointerException on an autowired service component in my controllers:
#Aspect
#Component
#Slf4j
public class LogRequestAspect {
#Around("#annotation(org.springframework.web.bind.annotation.RequestMapping) && execution(public * *(..))")
public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes())
.getRequest();
final Object proceed = joinPoint.proceed();
log.info(
"{} {} from {},{}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr(),
request.getHeader("X-Forwarded-For"));
return proceed;
}
}
Example controller:
#RestController
public class AController {
#Autowired
AService aService;
#RequestMapping("/doSomething")
private List<Map<String, Object>> doSomething() {
return aService.doSomething();
}
}
Example service:
#Service
public class AService {
public List<Map<String, Object>> doSomething() {
List<Map<String, Object>> results = new ArrayList<>();
return results;
}
}
Example configuration:
#EnableAspectJAutoProxy
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) {
}
}
As soon as i remove the aspect, everything works perfectly.
Any idea what i'm missing here?

Spring AOP, by default, works using proxies. IN this case a class based proxy is being used because no interface has been implemented. A class based proxy extends the actual class and overrides all the methods to apply the interceptors/aspects.
However a private method cannot be overriden in a subclass and as such your controller method will be invoked on the proxy instead of the proxied object. The proxy never has anything injected and hence the aService field is always null on there.
To fix make the method public or protected so that a subclass can override the method and eventually the method will be called on the proxied instance instead of the proxy.

Related

Spring ProxyFactoryBean #Autowired not working

I need to intercept methods from a interface, and found this implementation of MethodInterceptor, which I tested on a new spring app and worked.
The problem is, I can't seem to get it working on the spring application I need it to.
#Configuration
public class TestMethodConfig {
#Autowired
private TestService testService;
#Bean
#Primary
public ProxyFactoryBean testProxyFactoryBean() {
ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
testProxyFactoryBean.setTarget(testService);
testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
return testProxyFactoryBean;
}
}
#Service
public class TestServiceImpl implements TestService{
#Override
public void testMethod(String test) {
System.out.println("testService String");
}
}
public interface TestService{
void testMethod(String test);
}
#RestController
public class Controller {
#Autowired
private TestService testProxyFactoryBean;
#GetMapping(value = "/test")
public void test(){
testProxyFactoryBean.testMethod("valor");
}
}
#Component
public class TestMethodInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before method");
System.out.println("invocation: " + Arrays.toString(invocation.getArguments()));
Object retVal = invocation.proceed();
System.out.println("after method");
return retVal;
}
}
I used Spring Actuator to check the beans relations, and I found that the #Autowired TestService on Controller should be getting assigned to testProxyFactoryBean, but its getting assigned to the TestServiceImpl bean instead, so I believe there is a problem creating the proxy.
In short
I don't know how/why it was:
on a new spring app and worked.
but:
I can't seem to get it working on the spring application I need it to.
..can probably be fixed!
Make it consistent
Or:
#Configuration
public class TestMethodConfig {
#Autowired
private TestService testService;
}
...
// !!
public class TestServiceImpl implements TestService{
#Override
public void testMethod(String test) {
System.out.println("testService String");
}
}
...
#Service // !!!
public interface TestService{
void testMethod(String test);
}
...
#RestController
public class Controller {
#Autowired
private TestService testProxyFactoryBean;
...
Or: Impl!
(Use Interface and Impl consistently!)
In Detail
6.4. Using the ProxyFactoryBean to Create AOP Proxies
esp. Proxying Interfaces.
So with "least impact" (and java config), it should be:
#Configuration
public class TestMethodConfig {
// !!! Impl from component-scan (#Service), NOT interface:
#Autowired
private TestServiceImpl testServiceImpl; // or define custom, or "inline"...
#Bean
#Primary // only if you need it, better would be: distinct!
public ProxyFactoryBean testProxyFactoryBean() {
ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
// !!! set proxyInterface as documented:
testProxyFactoryBean.setProxyInterface(TestService.class);
testProxyFactoryBean.setTarget(testServiceImpl);
testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
// ...
return testProxyFactoryBean;
}
}
..enjoy! ;)

Spring Boot - Mock Interceptors

I'm having an issue while testing Rest request.
However test is being intercepted by one of my interceptors.
In short I want to mock interceptors behaviour rather than calling actual implementation.
For simplicity purposes to visualise the issue here is a simply Get request
#Controller
public class HiController {
#ResponseBody
#GetMapping(value = "/hi", produces = {"application/json"})
public String hi() {
return "hi";
}
}
with simple interceptor and config file
#Component
public class LoggingInterceptor implements HandlerInterceptor {
#Override
public final boolean preHandle(HttpServletRequest request, final HttpServletResponse response, final Object handler) {
System.out.println("Logging token interceptor");
return true;
}
}
#Component
public class InterceptorAppConfig implements WebMvcConfigurer {
#Autowired
private LoggingInterceptor loggingTokenInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingTokenInterceptor);
}
}
the purpose of test was to call rest endpoint and to mock interceptor
#RunWith(SpringRunner.class)
#WebMvcTest(HiController.class)
public class HiControllerTest extends MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
RestTemplate restTemplate;
#Mock
private HiController controller;
private static final String AUTHENTICATE_USER = "/hi";
#Test
public void mandatoryFieldMissing() throws Exception {
this.mockMvc.perform(get(AUTHENTICATE_USER))
.andExpect(content().string(containsString("hi")))
.andExpect(status().isOk());
}
}
#RunWith(SpringRunner.class)
public abstract class MvcTest {
#MockBean
LoggingInterceptor loggingTokenInterceptor;
}
When I run this test ^ LoggingInterceptor is still being called.
How can I configure tests to have mock interceptors?
You are using #MockBean. From the documentation,
If a bean, compatible with the declared class exists in the context, it replaces it by the mock. If it is not the case, it adds the mock in the context as a bean.
The mocked bean does not have behavior. Realize that by mocking the bean, the onus is on you to define the behavior.
You can either disable mocking the bean(to allow for default behavior)
Mockito.doCallRealMethod().when(loggingTokenInterceptor).preHandle(Mockito.any(), Mockito.any(), Mockito.any());
Or you can define the behavior for the mocked bean yourself.

Alternative to ApplicationContext.getBean() in Spring

I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}

Why are #Bean Generics creation methods on a superclass called later than ones on the child-class in Spring Boot?

I have a spring boot base abstract config class that creates a bean. If I then inherit from it, the bean will be created later than my controller (which needs to auto-wire it and thus fails). Note: it does get created, just after the controller. So it can't be auto-wired but has to be found via appContext.getBean( BeanType.class )
If I instead override the bean method in the child class, then it's created before the controller and it can be auto-wired.
How can i fix this and make the super-class bean definition load at the same time as the child class?
#SpringBootApplication
public class ChildConfig extends ParentConfig<PCTestBean>
{
public ChildConfig()
{
super();
}
#Override
public PCTestBean getT()
{
return new PCTestBean();
}
}
public abstract class ParentConfig<T>
{
public ParentConfig() {}
#Bean
public T createTestBean()
{
return getT();
}
public abstract T getT();
}
public class PCTestBean
{
}
#RestController
#RequestMapping( "/client" )
public class MyController
{
#Autowired
private PCTestBean pcTestBean;
#RequestMapping( "/get" )
#ResponseBody
public String getClient(HttpServletRequest request) throws Exception
{
return pcTestBean.toString();
}
}
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
#ContextConfiguration(
classes = {
ChildConfig.class
}
)
public class TestConfigs
{
#LocalServerPort
private String port;
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setUp() throws Exception
{
mockMvc = MockMvcBuilders
.webAppContextSetup( context )
.build();
}
#Test
public void testValidCall() throws Exception
{
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get( new URI( "http://localhost:" + port + "/client/get" ) )
)
.andExpect( MockMvcResultMatchers.status().isOk() ).andReturn();
System.out.println( result.getResponse().getContentAsString() );
}
}
When Spring scans your configuration class, ChildConfig, it discovers this inherited method
#Bean
public T createTestBean() {
return getT();
}
and registers a bean definition for it. That bean definition contains metadata about the type of the bean. That type is inferred from the return type of the method. In this case, it's resolved to Object because the type variable T has no bounds in its declaration and because Spring doesn't try to resolve it based on the type argument provided in ChildConfig's extends ParentConfig<PCTestBean> clause.
When Spring then tries to process the
#Autowired
private PCTestBean pcTestBean;
injection target, it looks for a PCTestBean bean, which it doesn't think it has, because the metadata is lacking. IF the bean hasn't been initialized through some other forced order, then Spring has no other information to go on and thinks the bean doesn't exist.
When you change your code to
instead override the bean method in the child class
the return type of the method is PCTestBean which Spring can then match to the #Autowired injection requirement, find (and initialize) the bean, and inject it.
By the time you use ApplicationContext#getBean(Class), the PCTestBean has been initialized. Spring can therefore rely on the actual type of the instance. It'll more or less loop through all beans and check whether beanClass.isInstance(eachBean), returning the one that matches (or failing if more than one does).
Pankaj, in their answer, suggests using #DependsOn (it was wrong when they suggested it, before you edited your question). That can help establish the order I mentioned earlier.
I don't how extensive your configuration class is that you think you need generics to abstract some behavior away, but I would suggest just dropping the generic behavior and be explicit in each class.
Try DependsOn annotation, it guarantees that the child bean should be created after the parent bean
#Configuration
public class ChildConfig extends ParentConfig
{
public ChildConfig()
{
super();
}
#DependsOn("parentConfig")
#Override
public TestBean createTestBean()
{
return super.createTestBean();
}*/
}
public abstract class ParentConfig
{
public ParentConfig() {}
#Bean (name ="parentConfig")
public TestBean createTestBean()
{
return new TestBean();
}
}

Mocking Spring bean's method behavior breaks aspects

I searched SO and found bunch of other questions that looked similar but not exactly, so I'll ask another one.
I have Spring application and say I created custom aspect (looking for CatchMe annotation) to log exceptions in a specific way. I want to test the aspect by mocking the behavior of one of my Spring #Service class's method so it throws exception when it is called. Then in another method, annotated with my custom annotation #CatchMe, I call the first method. What I expect to happen is the exception to get logged. Unfortunatelly the exception is thrown but the aspect is not triggered. So how can I make the aspect to get triggered in this test using Mockito?
Note: I've checked those (plus a bunch more):
Unit testing Spring #Around AOP methods
Spring Aspect not triggered in unit test
Spring: cannot inject a mock into class annotated with the #Aspect annotation
but most of them are Controller related and not Service related and I want to test only the service.
The Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {BeanConfig.class})
public class MyServiceTest {
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(service, "serviceDep", serviceDep);
}
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Services
#Service
public class MyService {
#Autowired
private MyServiceDependency serviceDep;
#CatchMe
public void execute() {
serviceDep.process();
}
}
#Service
public class MyServiceDependency {
public Object process() {
// may throw exception here
}
}
Configuration and Aspect
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.example.services"})
public class BeanConfig { .. }
#Aspect
#Component
public class CatchMeAspect {
#Around("#annotation(CatchMe)")
public Object catchMe(final ProceedingJoinPoint pjp) throws Throwable {
try {
pjp.proceed();
} catch (Throwable t) {
// fency log
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CatchMe {}
EDIT: The functionality works but I want to verify it with the test.
Actually it is working as expected, however you are running in a side effect of proxy based AOP, especially class based proxies in this case.
Currently you are setting the field on the proxy and not on the actual object inside the proxy. Which is what you actually want. To obtain the actual instance use AopTestUtils.getUltimateTargetObject and then use that in the ReflectionTestUtils.setField method.
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MyService serviceToInject = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setField(serviceToInject, "serviceDep", serviceDep);
}
However I think that approach is wrong, when you start messing around like this there is a better way. Simply use Spring to inject the mock. Create a specific #Configuration class for this test case. Make it a internal public static class and for the dependency add a mocked #Bean.
#Configuration
#Import(BeanConfig.class)
public static class TestBeanConfig {
#Bean
public MyServiceDependency myServiceDependency() {
return Mockito.mock(MyServiceDependency.class);
}
}
Now in your test class you can simply #Autowire both beans and not need to use reflection or whatever to set dependencies.
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyServiceDependency serviceDep;
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Which will take care of the correct dependencies.
I had the same problem as #nyxz and this is intentional, see https://github.com/spring-projects/spring-boot/issues/7243.
Inspired by #M. Deinum following solution worked for me with Spring Boot 2.3.4.RELEASE and JUnit 5.
We will just provide a mocked bean without #MockedBean
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyServiceTest {
#Autowired
private MyService service;
#Test
public void test() {
service.execute();
}
static class TestBeanConfig {
#Bean
#Primary
public MyServiceDependency myServiceDependency() {
MyServiceDependency myServiceDependency = Mockito.mock(MyServiceDependency.class)
// Add behavior of mocked bean here
return myServiceDependency;
}
}
}

Categories