Immutable spring request scoped beans - java

I'd like to be able to instantiate a request-scoped bean which is also immutable by using constructor parameters.
Something like the following (which of course doesn't work):
RequestContextFactory.java
package org.springframework.samples.mvc.requestscope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.RequestScope;
#Configuration
public class RequestContextFactory {
#Bean
#RequestScope
public RequestContext getRequestContext(TestBean bean) {
return new RequestContext(bean);
}
}
MyController.java
package org.springframework.samples.mvc.requestscope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class MyController {
#Autowired
RequestContextFactory requestContextFactory;
#RequestMapping("/my")
public #ResponseBody String simple(TestBean bean) {
RequestContext requestContext = requestContextFactory.getRequestContext(bean);
return "Hello world!";
}
}
Spring complains that it cannot autowire a TestBean bean to create RequestContext.
How can I achieve immutability of a request-scoped bean which needs constructor parameters only known in the controller?
I'd like to be able to inject RequestContext into other beans (either request scope or other scopes). Is this an antipattern? Should something like RequestContext (or any other object with request lifecycle) be in the signature of all call hierarchy under the controller?
Note:
I thought of as a solution like for example having a RequestContext with default constructor and an init(...) method which can be called only once (would throw the second time). I don't like it.

Related

Value cannot injected into service class spring boot

I already try to search through stackoverflow, and I don't think I find the solution I want...
Also I try to use answer https://stackoverflow.com/questions/45970442/spring-boot-value-returning-null and still doesn't work...
Here is my controller class
package com.vincent.springoauth.controller;
import com.vincent.springoauth.model.GiftCardRequest;
import com.vincent.springoauth.model.GiftCardResponse;
import com.vincent.springoauth.service.InCommGiftCardServiceImpl;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/api/gift-card")
public class GiftCardController{
#PostMapping("/activate")
public #ResponseBody
GiftCardResponse activate(GiftCardRequest request) {
GiftCardServiceImpl giftCardService = new GiftCardServiceImpl("");
return giftCardService.activate(request);
}
}
And here is my service class
package com.vincent.springoauth.service;
import com.vincent.springoauth.model.GiftCardRequest;
import com.vincent.springoauth.model.GiftCardResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
#Log4j2
public class GiftCardServiceImpl {
private final String baseEndpoint;
public GiftCardServiceImpl( #Value("${webserviceurl}")String baseEndpoint){
this.baseEndpoint = baseEndpoint;
}
public String accessToken() {
log.info("Access oauth token url address: " + baseEndpoint);
// will be use that base endpoint to manipulate stuff later
return "abcdefg";
}
public GiftCardResponse activate(GiftCardRequest request) {
log.info("Calling token ...");
accessToken();
log.info("Incomm Pre Auth Service");
// Generate preAuth request;
//RetailTransactionGenericRequestWrapper retailTransactionGenericRequest = buildRequest(request);
//log.info("RetailTransactionGenericRequest: " + retailTransactionGenericRequest);
GiftCardResponse response = GiftCardResponse.builder().responseCode("0").responseMessage("Success").build();
return response;
}
}
And in my application.properties I have following line webserviceurl=https://localhost/giftcard
The issue that in my service class the webserviceurl return null. How can I fix this?
In your controller you are creating your own instance of the service and so Spring is unaware of it and cannot inject the value. Your service class is annotated as a Service so Spring will create an instance, which will have the value injected but that is not the instance that your controller is using.
Instead you should declare that service as an instance variable in your controller and use either Autowire annotation on that instance variable or use constructor autowiring to ensure that the bean created by Spring is the one that is used by your controller.
#RestController
#RequestMapping("/api/gift-card")
public class GiftCardController{
private GiftCardServiceImpl giftCardService;
#Autowired
public GiftCardController(GiftCardServiceImpl giftCardService) {
this.giftCardService = giftCardService;
}
#Value annotation uses for injecting values into fields in Spring-managed beans. In your example, you create GiftCardServiceImpl on your own and Spring cannot control the creation and inject the webserviceurl value from application.properties. You can change GiftCardController to allow Spring to do this.
package com.vincent.springoauth.controller;
import com.vincent.springoauth.model.GiftCardRequest;
import com.vincent.springoauth.model.GiftCardResponse;
import com.vincent.springoauth.service.InCommGiftCardServiceImpl;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/api/gift-card")
public class GiftCardController{
private final GiftCardServiceImpl giftCardService;
public GiftCardController(GiftCardServiceImpl giftCardService) {
this.giftCardService = giftCardService;
}
#PostMapping("/activate")
public #ResponseBody
GiftCardResponse activate(GiftCardRequest request) {
return giftCardService.activate(request);
}
}
Try to inject GiftCardServiceImpl via Spring DI like in the example below
package com.vincent.springoauth.controller;
import com.vincent.springoauth.model.GiftCardRequest;
import com.vincent.springoauth.model.GiftCardResponse;
import com.vincent.springoauth.service.InCommGiftCardServiceImpl;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/api/gift-card")
public class GiftCardController{
#Autowired
private GiftCardServiceImpl giftCardService;
#PostMapping("/activate")
public #ResponseBody
GiftCardResponse activate(GiftCardRequest request) {
return giftCardService.activate(request);
}
}
The #Value annotation only works for Spring Beans, when you create the class via simple new keyword. Spring doesn't catch that should inject property in the constructor.

How assign a a value from application.properties to a #Qualifier

I would like to assign a value with the name of the #Service from application.properties tu |#Qualifier . I tried but it doesn't work .
In fact i have two services which implement the same interface and i would like to change the service from application.properties
Someone has any idea how to do this?
this is my code
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Controler {
#Qualifier("${service.name}")
#Autowired
private InterfaceTest interfaceTest;
#GetMapping("/test")
public String test(){
return interfaceTest.test();
}
}
Thank you very much for your help
I think you can do that in the constructor of this controller by using Environment and ApplicationContext beans.
Just remove Autowired annotation from fields and accept other parameters through constructor as well.
public Controller(Environment environment, ApplicationContext applicationContext) {
String serviceName = environment.getProperty("service.name");
this.interfaceTest = applicationContext.getBean(serviceName, InterfaceTest.class);
}
Those bean qualifiers and other annotations of spring just except literals as far as I know.
Hope this helps.

Bean cannot be autowired in a class

I'm using JWT (Json Web Token) to secure my Spring boot application.
There's a class which doesn't accept any bean.
First, I thought maybe the bean I want to inject is not defined. So I decided to print the list of beans names using spring ApplicationContext. But I found out that even ApplicationContext can not be injected into this class:
Any idea why this happens ?
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private TokenAuthenticationService tokenAuthenticationService;
#Autowired
private ApplicationContext applicationContext;
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException,
IOException, ServletException {
CustomUserDetails creds = new ObjectMapper().readValue(
req.getInputStream(), CustomUserDetails.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(creds.getUsername(),
creds.getPassword()));
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res, FilterChain chain, Authentication auth) {
java.util.List s = Arrays.asList(applicationContext.getBeanDefinitionNames());
System.out.println(s);
tokenAuthenticationService.addAuthentication(res, auth.getName());
}
}
The JWTLoginFilter has to be a bean as well to allow Spring to inject other beans into. Currently, Spring doesn't have any control over this. Marking the class with the #Component / #Service / #Repository annotations (depends on what role your filter plays, I assume the #Component is a good choice) is going to resolve the issue.
EDIT 1:
JWTLoginFilter required a bean of type java.lang.String that could not be found. Can a component have a constructor?
The problem: Spring tried to use a two-argument constructor to create a bean and was expected that these two arguments are its beans. But it's not true because there is no bean with the String class.
The solution: You should define a non-argument constructor to allow Spring to make an untuned instance without issues. Then create setters to provide methods which Spring will use to inject needed dependencies.
EDIT 2:
The workaround is to define a String bean (in a #Configuration class) which will be injected into the JWTLoginFilter constructor, but I'm not sure that your filter needs to have some external dependencies.
#Bean
public String getStringPatternBean() {
return "pattern";
}
Well, I finally decided to change how these classes are designed. I made methods inside TokenAuthenticationService static.

Spring bean properties persisting

I have a plain POJO being autowired in Spring whose properties appear to persist.
I find that the happy path is OK - Set bean properties and return then, however when I'm not on the happy path and I no longer wish to set a property (in this case responseCode), I find it is still set to the previous value (when a call was successful).
I would like this value to not be set and be equal to what I have currently specified in the model.
I have the following POJO Prototype bean
package com.uk.jacob.model;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("prototype")
public class Website {
public String url;
public boolean response;
public int responseCode = 0;
}
I am setting it's information within a service class
package com.uk.jacob.service;
import java.net.HttpURLConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.uk.jacob.adapter.HttpAdapter;
import com.uk.jacob.model.Website;
#Component
public class PingerService {
#Autowired
HttpAdapter httpAdapter;
#Autowired
Website website;
public Website ping(String urlToPing) {
website.url = urlToPing;
try {
HttpURLConnection connection = httpAdapter.createHttpURLConnection(urlToPing);
website.response = true;
website.responseCode = connection.getResponseCode();
} catch (Exception e) {
website.response = false;
}
return website;
}
}
Which is called from a RestController
package com.uk.jacob.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.uk.jacob.model.Website;
import com.uk.jacob.service.PingerService;
#RestController
#RequestMapping("api/v1/")
public class PingController {
#Autowired
PingerService pingerService;
#RequestMapping(value = "ping", method = RequestMethod.GET)
public Website getPing(#RequestParam String url){
return pingerService.ping(url);
}
}
The fact that your Website bean is #Scope("prototype") means that every time that it gets injected as a dependency in another bean upon this bean's creation, a new instance gets created and injected. In this case PingerService gets a new instance of Website. If say Website is also injected on another bean called Website2 then a different (new) instance gets injected.
If your anticipation is Website to be new upon each invocation within Website then this cannot be done simply with the prototype annotation. You'll need to expose the context and invoke ApplicationContext#getBean("website").
For your use case, I understand that you need a fresh instance of Website bean for every request.
Use #Scope("request").
Prototype scope on the other hand means it will be creating a separate instance for every Autowiring of Website it sees everywhere. For example, PingerService will have its own Website bean and won't be shared on other classes with the same Autowiring but its values will persist across http requests on PingerService.

Javaconfig bean overriding failing with List injected

I consider injection of beans as a List of automatically detected beans: I introduce several beans implementing the same interface and inject all of them as a List in a later bean.
I've not been able to find official documentation related to this feature. My single source is http://www.coderanch.com/t/605509/Spring/Java-config-autowired-List
Considering this feature, I have an issue with Bean overring: I would like to override a bean defined throught a no-arg method with a bean defined with the List of detected beans. However, spring behave like the second bean definition does not exist.
It can be reproduced with the following test:
import java.util.Date;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class SpringTest {
#Test
public void shouldtestSpringDifferentMethodNames() {
AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(AConfig.class, CConfig.class);
Assert.assertEquals("overriden", ctx2.getBean("bean"));
}
#Configuration
public static class AConfig {
#Bean
public Object bean() {
return "not overriden";
}
}
#Configuration
public static class CConfig extends AConfig {
#Bean
public Date anotherBean() {
return new Date();
}
#Bean
public Object bean(List<? extends Date> someDate) {
return "overriden";
}
}
}
If this is an expected behavior, how can I achieve such an overriding?
documentation for list autowire can be found at spring documentation
overriding of beans by id or name is not official spring feature - look for that question for more details
This has been considered as a bug by the Spring team: https://jira.springsource.org/browse/SPR-10988
A recent piece of documentation can be found at: http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-autowired-annotation (Thanks Alexander Kudrevatykh for the 2.5 source)

Categories