Inject request scoped bean into another bean - java

I want to create a UUID that is unique in a request life cycle.
To do this, I create a UUID bean with the #Scope("request") annotation.
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
public UUID requestUUID() {
return UUID.randomUUID();
}
I want to access this bean in my controller. So I inject it with #Autowired.
This works fine.
#Controller
public class DashboardController {
#Autowired
UUID uuid;
#Autowired
WelcomeMessageService welcomeMessageService;
#Autowired
IssueNotificationService issueNotificationService;
#RequestMapping("/")
public String index(Model model) throws InterruptedException, ExecutionException {
System.out.println(uuid);
PortalUserDetails userLog = getPortalUserDetails();
BusinessObjectCollection<WelcomeMessage> welcomeMessages = welcomeMessageService.findWelcomeMessages(
20,
0,
userLog.getZenithUser(),
userLog.getConnectionGroup().getConnectionGroupCode(),
"FR");
if(welcomeMessages!=null) {
model.addAttribute("welcomeMessages", welcomeMessages.getItems());
}
BusinessObjectCollection<IssueNotification> issueNotifications =
issueNotificationService.findIssueNotifications(userLog.getZenithUser());
if(welcomeMessages!=null) {
model.addAttribute("welcomeMessages", welcomeMessages.getItems());
}
model.addAttribute("issueNotifications", issueNotifications);
return "index";
}
}
The controller call multiple services. Every service use a RestTemplate bean. In this RestTemplate bean, I want to get the UUID.
#Component
public class ZenithRestTemplate extends RestTemplate {
#Autowired
private UUID uuid;
public void buildRestTemplate() {
List restTemplateInterceptors = new ArrayList();
restTemplateInterceptors.add(new HeaderHttpRequestInterceptor("UUID", uuid.toString()));
this.setInterceptors(restTemplateInterceptors);
}
}
When I try to inject the UUID here, I have an error :
Error creating bean with name 'zenithRestTemplate': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.UUID com.geodis.rt.zenith.framework.webui.service.ZenithRestTemplate.uuid; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestUUID': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
What can I do to access my UUID bean inside the RestTemplate bean ?
My project use Spring-MVC, Spring-boot with java configuration.
I already tried to add a RequestContextListener but it doesn't solve the problem.
#Bean public RequestContextListener requestContextListener(){
return new RequestContextListener();
}

I think you need to mark your UUID request scoped bean like:
#Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
Where controller is singleton scoped bean you are injecting request scoped bean in it. As singleton beans are injected only once per their lifetime you need to provide scoped beans as proxies which takes care of that.
Another option is to instead use the org.springframework.web.context.annotation.RequestScope annotation:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Scope(WebApplicationContext.SCOPE_REQUEST)
public #interface RequestScope {
#AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
#RequestScope is a meta-annotation on #Scope that 1) sets the scope to "request" and 2) sets the proxyMode to ScopedProxyMode.TARGET_CLASS so you don't have to do it every time you want to define a request-scoped bean.
Edit:
Note that you may need to add #EnableAspectJAutoProxy on your main configuration class.

Related

How to create #Controller and #Service instance conditionally on Spring

I am using Spring Boot 1.5.9.
Is there a way to turn on/off #Controller and #Services?
Something such as #ConditionalOnProperty, #Conditional for beans.
#ConditionalController // <--- something like this
#RestController
public class PingController {
#Value("${version}")
private String version;
#RequestMapping(value = CoreHttpPathStore.PING, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Map<String, Object>> ping() throws Exception {
HashMap<String, Object> map = new HashMap<>();
map.put("message", "Welcome to our API");
map.put("date", new Date());
map.put("version", version);
map.put("status", HttpStatus.OK);
return new ResponseEntity<>(map, HttpStatus.OK);
}
}
Then use some configuration bean to load it up.
#ConditionalOnProperty should work for Controller (or Service) as well, since it is also a Spring bean.
Add to your PingController
#ConditionalOnProperty(prefix="ping.controller",
name="enabled",
havingValue="true")
#RestController
public class PingController {...}
and to the application.properties to turn it on/off
ping.controller.enabled=false
do you want to try and load the bean programmatically instead?
you could access the application context using one of the two mechanisms
#Autowired private ApplicationContext appContext;
or creating something like a bean factory by extending ApplicationAware
public class ApplicationContextProvider implements
ApplicationContextAware{
once you have a handle to the application context you can add a bean to the context programmatically.
By default in Spring, all the defined beans, and their dependencies, are created when the application context is created.
We can turn it off by configuring a bean with lazy initialization, the bean will only be created, and its dependencies injected, once they're needed.
You can enable lazy initialization by configuring application.properties.
spring.main.lazy-initialization=true
Setting the property value to true means that all the beans in the application will use lazy initialization.
All the defined beans will use lazy initialization, except for those that we explicitly configure with #Lazy(false).
Or you can do it through the #Lazy approach. When we put #Lazy annotation over the #Configuration class, it indicates that all the methods with #Bean annotation should be loaded lazily.
#Lazy
#Configuration
#ComponentScan(basePackages = "com.app.lazy")
public class AppConfig {
#Bean
public Region getRegion(){
return new Region();
}
#Bean
public Country getCountry(){
return new Country();
}
}

Spring Autowiring with Inheritance

I'm relatively new to Spring, and I'm trying to understand an issue I'm experiencing in which a dependency is not being autowired into a bean that is the parent of another bean that is being constructed.
The parent class is specified as follows. Note that I've abbreviated some of the logic for clarity. The Log class is implemented as an entity.
#Component
#Scope("prototype")
public class AppTransaction {
private String componentName;
#Autowired
private LogRepository logRepository;
public AppTransaction(String componentName) {
this.componentName = componentName;
}
public void writeLog(String logText) {
Log log = new Log(this.componentName, logText);
this.logRepository.save(log);
}
}
The child class is implemented as follows.
#Component
#Scope("prototype")
public class EmployeeJobTransaction extends AppTransaction {
#Autowired
private EmployeeRepository employeeRepository;
private String componentName;
private String jobName;
public EmployeeJobTransaction(String componentName, String jobName) {
super(componentName);
this.jobName = jobName;
}
public List<String> doJob() {
List<String> statuses = this.employeeRepository.getEmployeeJobStatuses();
writeLog("Job complete.");
}
}
In my service class, I'm using the application context to get an instance of the EmployeeJobTransaction bean. I'm doing this because the input parameters to the EmployeeJobTransaction bean are based on user input.
#Service
public class JobServiceImpl {
#Autowired
private ApplicationContext applicationContext;
public void initJob() {
EmployeeJobTransaction employeeJobTransaction = this.applicationContext.getBean(EmployeeJobTransaction.class, "EmployeeComponent", "CheckEmployeeJobStatus");
jobTransaction.doJob();
}
}
When I get the EmployeeJobTransaction bean, the EmployeeRepository bean is autowired as expected. However, the AppTransaction instance created does not appear to be a Spring-managed bean because the LogRepository object that has been autowired is null. I'm assuming this is because the call to super() in the EmployeeJobTransaction class does not create an instance of AppTransaction as a Spring-managed bean.
What are my options? Must I autowire the LogRepository bean into the EmployeeJobTransaction class and then pass it to the writeLog method as a parameter? I'm not understanding why the parent class is not created as a Spring bean when the child class is created as such.
For those that may come across this question in the future, I understand now that I was interpreting what Spring was doing incorrectly.
I turned on debugging and had set a breakpoint in the AppTransaction constructor.
public AppTransaction(String componentName) {
this.componentName = componentName; //Breakpoint here
}
When I executed my logic, I was confused because the autowired LogRepository bean was null.
According to the Spring documentation:
Fields are injected right after construction of a bean, before any config methods are invoked.
Therefore, it makes sense that the LogRepository bean is null in the constructor. When the writeLog method is called, the LogRepository bean is not null because it was successfully autowired AFTER the construction of the AppTransaction bean.
Thanks to those who responded.
TLDR: Autowired fields are null inside the constructor of a bean. The beans are not autowired until the construction of the bean in which they are referenced has completed.
I think you are a littel confused. I will try to explain me:
Your code shows 3 Spring beans:
AppTransaction
With an autowired dependency of the logRepository
EmployeeJobTransaction
With an autowired dependency of the employeeRepository
With an autowired dependency of the logRepository (inhereted of the parent class AppTransaction)
JobServiceImpl
When you call to he super method on EmployeeJobTransaction you are not calling to AppTransaction bean. You are calling to the parent class constructor method.
Is unnecessary to have a Spring bean of the AppTransaction class (remove the Component and Scope annotations of this class)
When you call to getBean method, Spring create a new bean each time. I think this is unnecessary. You could declare EmployeeJobTransaction bean as singleton (remove de Scope annotation) and to add the componentName and jobName attributes as parameters of doJob method.
I think woult be the best way.

#ConditionalOnBean not working with JdbcTemplate.class

I have some spring component:
#Component
#ConditionalOnBean(value={JdbcTemplate.class})
public class DictionaryHandler implements Handler {
private final JdbcTemplate jdbcTemplate;
#Autowired
public DictionaryHandler(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//some methods
}
In debug, I see, that JdbcTemplate bean was created:
But, my bean not created.It doesn't go into the constructor. But why? Bean JdbcTemplate is exist, but my bean not created.
Does the condition work wrong? I don't want create DictionaryHandler if JdbcTemplate is missing. Without this I get an error.
You should use #ConditionalOnBean on auto configuration classes only (otherwise the order is unspecified)
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
Since your JdbcTemplate bean is defined inside JdbcTemplateAutoConfiguration class so I assume that JdbcTemplateAutoConfiguration is marked with #Configuration.
In that case, you can ensure the instantiate of your bean by config:
#Configuration
#AutoConfigureAfter(JdbcTemplateAutoConfiguration.class)
public class DicationaryHandlerConfiguration {
#Bean
#ConditionalOnBean(JdbcTemplate.class)
public DictionaryHandler handler(JdbcTemplate jdbcTemplate) {
return new DictionaryHandler(jdbcTemplate)
}
}
public class DictionaryHandler implements Handler {
private final JdbcTemplate jdbcTemplate;
public DictionaryHandler(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//some methods
}
Check the documentation for ConditionalOnBean. It runs once and if the bean it requires not yet created - it will not trigger. The order of bean creation matters. You can try to lower priority of your component with #Order annotation or increase priority of you configuration/component class which holds JdbcTemplate bean.
From Annotation Type ConditionalOnBean:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
In your case, the problem is DictionaryHandler bean is attempted to be created before processing the configuration class and given that JdbcTemplate bean is not yet in DI container, your DictionaryHandler is not getting instantiated.
One workaround could be to initialize DictionaryHandler in the same configuration class as JdbcTemplate.
Also, you can use different configuration class, but you'll have to use #DependsOn annotation.
An example:
#Component
#ConditionalOnBean(name = "bean1")
class AnotherBean {
}
#Configuration
class Config {
#Bean
public Object bean1() {
return new Object();
}
}
In the previous example AnotherBean will not be created, but it will be in this way:
#Configuration
class Config {
#Bean
public Object bean1() {
return new Object();
}
#Bean
#ConditionalOnBean(name = "bean1")
public AnotherBean anotherBean() {
return new AnotherBean();
}
}

Using AOP scoped proxy to autowire prototype bean in a singleton bean

I was able to test that autowiring a prototype bean, within a singleton bean results in only a single prototype bean being created.
As a solution to that, I read that I could define AOP scoped proxy for the prototype bean or use Spring's lookup method injection.
Here is what I have tried -
PrototypeBean.java
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES)
public class PrototypeBean implements Prototype {
private String welcomeMessage;
public String getWelcomeMessage() {
return welcomeMessage;
}
public void setWelcomeMessage(final String welcomeMessage) {
this.welcomeMessage = welcomeMessage;
}
}
SingletonBean.java
#Component
public class SingletonBean implements Singleton{
#Autowired private Prototype prototype;
public Prototype getPrototype() {
return prototype;
}
public void greet() {
System.out.println(prototype.getWelcomeMessage());
}
}
Test Class
public class AutowiredDependenciesDemo {
#Autowired private Singleton autowiredSingleton;
#Autowired ConfigurableApplicationContext context;
#Test
public void testPrototypeBeanWithAopScopedProxy(){
Assert.assertNotNull(autowiredSingleton);
Prototype prototypeBean = (Prototype) ((SingletonBean) autowiredSingleton).getPrototype();
prototypeBean.setWelcomeMessage("hello world");
autowiredSingleton.greet();
Singleton contextSingleton = (Singleton) context.getBean("singletonBean");
Assert.assertSame(autowiredSingleton, contextSingleton);
Prototype anotherPrototypeBean = (Prototype) ((SingletonBean)contextSingleton).getPrototype();
anotherPrototypeBean.setWelcomeMessage("hello india");
contextSingleton.greet();
autowiredSingleton.greet();
// i expected both the prototype instances to be different. in the debugger, it does show two different 'proxied' instances. however the test fails.
Assert.assertNotSame(prototypeBean, anotherPrototypeBean);
}
Am I missing something here ? Also, the calls to greet() method return null.
There are things you mix in your thinking about Proxies and prototype beans.
When Spring Framework injects a Prototype Scoped bean into a Singleton Scoped bean then it creates a Proxy object (which implements all required interfaces) and injects it instead of instance of the Prototype bean. Then, whenever method is called on this Prototype Proxy, Spring creates a new instance and the method is called on this new instance.
In your case - in test - you only compare injected Proxies and they are the same because there exists only 1 proxy for the Prototype Bean and this Proxy is responsible for creation of new instance of the Prototype Bean whenever needed.
Here is my example:
I have an interface Prototype and its implementation PrototypeImpl. In my test I obtain bean of type Prototype directly from ApplicationContext and I also inject it using #Autowired. Then in debugger I see this:
Notice that there is only one and the same Proxy (look at its address) but calling 'toString()' on this proxy shows two different addresses of the PrototypeImpl objects. Which shows exactly what I have written above.
EDIT: Info about un-proxying
To extend the comment by M. Deinum you can extract the underlying object from the Proxy in the following way:
Prototype extracted = null;
if(AopUtils.isAopProxy(a) && a instanceof Advised) {
Object target = ((Advised)a).getTargetSource().getTarget();
extracted = (Prototype) target;
}

Does Spring #Autowired inject beans by name or by type?

I am reading beginning spring (wiley press) book. In chapter 2 there is an example
about Java configuration and #Autowired. It provides this #Configuration class
#Configuration
public class Ch2BeanConfiguration {
#Bean
public AccountService accountService() {
AccountServiceImpl bean = new AccountServiceImpl();
return bean;
}
#Bean
public AccountDao accountDao() {
AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
//depedencies of accountDao bean will be injected here...
return bean;
}
#Bean
public AccountDao accountDaoJdbc() {
AccountDaoJdbcImpl bean = new AccountDaoJdbcImpl();
return bean;
}
}
and this regular bean class
public class AccountServiceImpl implements AccountService {
#Autowired
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
...
}
When I run the code, it works. But I expected an exception because I have defined 2 beans with the same type in the configuration.
I realized it works like this:
if Spring encounters multiple beans with same type it checks field name.
if it finds a bean with the name of the target field, it injects that bean into the field.
Isn't this wrong? Is there a bug in Spring's handling of Java configuration?
The documentation explains this
For a fallback match, the bean name is considered a default qualifier
value. Thus you can define the bean with an id "main" instead of the
nested qualifier element, leading to the same matching result.
However, although you can use this convention to refer to specific
beans by name, #Autowired is fundamentally about type-driven injection
with optional semantic qualifiers. This means that qualifier values,
even with the bean name fallback, always have narrowing semantics
within the set of type matches; they do not semantically express a
reference to a unique bean id
So, no, it's not a bug, that is the intended behavior. The bean id (name) will be used as a fallback if a by-type autowiring doesn't find a single matching bean.

Categories