below is the class which has constructor args which gets autoWired, but what i don't understand is that how the constructor gets autowired here without specifying any annotation or without mentioning it in any xml file.
I am kind of baffled with the code.
#Loggable
#Slf4j
public class DefaultDirectPlusService extends AbstractDnBDirectPlusService implements DirectPlusService {
public DefaultDnBDirectPlusService(String baseURL, RestTemplate restTemplate, DnBMetricsRepository dnbMetricsRepository, Environment env) {
super(restTemplate, dnbMetricsRepository, env);
this.baseURL = baseURL;
}
Here the question is how does baseURL gets populated without any annotations or the xml configuration?
As specified in the documentation (https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/core.html#beans-autowired-annotation), starting with Spring 4.3, if a class has only one constructor, Spring will automatically use that constructor for autowiring.
If you would have more than one constructor (you can try it out), you need to add the #Autowired annotation to the constructor which you want Spring to use.
Related
I've started to learn spring and I'm a bit confused about this:
If I create a spring #Controller or #RestController and then in the method I ask for different parameters, I've noticed that spring automatically populate those parameters even without having them declared as bean or component.
So on my project I've a Movie class (a simple POJO) and I need to have a singleton instance of RestTemplate too. The first approach was to declare the RestTemplate bean and then ask for it using the #Autowired and that's working fine. After that I've commented out that part of code and put the RestTemplate and Movie as parameters of my method and spring is automatically providing me those instances even though I don't have any bean declaration of them.
I thought spring leverages on bean declaration to do dependency injection so my question is why do I've to declare the bean and use #Autowired if spring already injects whatever I want inside the method? Does spring injects a singleton instance of these parameters? And is this okay to use or better to decalre the bean and go through #Autowired?
Below the classes:
This is my movie model:
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class Movie {
private String movieId;
private String name;
}
This is my rest controller:
#RestController
#RequestMapping("/catalog")
public class MovieCatalogController {
//#Autowired
//private RestTemplate restTemplate;
#RequestMapping("/{userId}")
public List<Catalog> getCatalog(#PathVariable("userId") String userId, RestTemplate restTemplate, Movie movie){
// todo
return new ArrayList<>();
}
}
And the main spring boot application class:
#SpringBootApplication
public class MovieCatalogServiceApplication {
//#Bean
//public RestTemplate getRestTemplate() {
//return new RestTemplate();
//}
public static void main(String[] args) {
SpringApplication.run(MovieCatalogServiceApplication.class, args);
}
}
Thank you
What you are referring to is called method injection. Any component can be injected as long as there is a single implementation (you can specify what implementation to use when this is not the case with profiles or with #Qualifier annotation)
The number of instances of the class is dictated by the #scope. Singleton is the default scope so you should be ok without needing to use your bean definition.
#autowired is a bit old by now but still has its uses. Although for basic injection it is preferred to use constructor or method injection.
I'm fairly new to Spring myself, so I might be wrong
I have a service-client project which is in normal spring application , not spring boot .its used for mainly logging related things.which contains Interceptor , loggingservice impl class and some model classes for logging. I have added this module as a dependency to main application in pom.xml.and i was able to inject and use the loggingService beans within the service layers of the main application.
Am getting NullPointerException while auto-wiring loggingService within the interceptor .The bean is not available within the interceptor.but like i said it can be injected and used within the main application.
Also am not able to read properties using #Value within the interceptor.
This is my Interceptor class .
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoggingService loggingService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
loggingService.info("Am in prehandle");
return true;
}
}
This is my configuration class where i register the interceptor with the main application
#Component
public class LoggingConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginInterceptor());
}
#Bean
public LoggingInterceptor getLoginInterceptor() {
return new LoggingInterceptor();
}
}
My question is almost similar to this post Cannot Autowire Service in HandlerInterceptorAdapter , but its different like am referring the interceptor from another module , and like they suggested i tried to create the bean from the application.
But the issues am facing right now is
getting NullPointerException while injecting loggingService within interceptor, but its working in main application
#Value annotation also return null, not able to read from properties
You have 2 possible solutions.
Mark your LoggingConfig as #Configuration instead of #Copmponent
Inject the LoggingInterceptor instead of referencing the #Bean method
Option 1: LoggingConfig as #Configuration
Your LoggingConfig is marked as an #Component whereas it should be marked as an #Configuration. The difference is that whilst it is allowed to have an #Bean method on an #Component it operates in a so-called lite mode. Meaning you cannot use method references to get the instance of a bean (this is due to no special proxy being created). This will lead to just a new instance of the LoggingInterceptor being created but it isn't a bean.
So in short what you are doing is equivalent to registry.addInterceptor(new LoggingInterceptor()); which just creates an instance without Spring knowing about it.
When marking the LoggingConfig as an #Configuration a special proxy will be created which will make the LoggingInterceptor a proper singleton bean, due to the method call being intercepted. This will register the bean in Spring and you will be able call the method.
NOTE: You actually endup with 2 instances of the LoggingInterceptor one due to the #Component on it the other through the #Bean. Remove the #Component.
Option 2: Inject the LoggingInterceptor.
As your LoggingInterceptor is marked as an #Component Spring will already create an instance (you actually have 2 instances of it created in your current setup). This instance you can inject into your LoggingConfig.
#Component
public class LoggingConfig implements WebMvcConfigurer {
private LoggingInterceptor loggingInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
With this you can remove the #Bean method as you will get the proper one injected into your LoggingConfig class. The class can also remain an #Component in this case. Although I would recommend using #Configuration as to also properly stereotype it.
NOTE: If you are on a recent Spring version you can use #Configuration(proxyBeanMethods=false). This will make a lite-configuration (just like an #Component) but it is still marked properly as a configuration class.
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 4 years ago.
In my Spring boot application, I have a dependency Spring project. From my SpringBoot class, I am trying to call a dependency class but the class has no value set.
#CrossOrigin(origins = "*")
#RestController
#Configuration
#ComponentScan(basePackages = "com.dependency.package")
public class BootClass {
private DependencyClass dependencyClass;
public BootClass() {
this.dependencyClass = new DependencyClass();
}
}
My DependencyClass object just gives me an empty object {}. Any ideas?
My Dependency class looks like this:
#Component
public class DependencyClass {
#Value("${jdbc.driver.class.name}")
private String driver;
#Value("${jdbc.url}")
private String url;
}
Thank you,
Julian
This is the classic Spring beginner mistake: calling new to instantiate an object.
You cannot call new if you want Spring to manage beans and provide dependencies. You have to give it to the Spring bean factory to manage. It's all or none.
Yours looks like a perfect case for constructor injection.
#Configuration
#ComponentScan(basePackages = "com.dependency.package")
public class BootClass {
private final DependencyClass dependencyClass;
#Autowired
public BootClass(#Qualifier(name = "dependencyClass") DependencyClass dependencyClass) {
this.dependencyClass = dependencyClass;
}
}
Surely you can think of a better name for DependencyClass. I'd suggest something like DatabaseConfiguration.
That is not to say that every object should be under the control of the bean factory, or that new should never be called. Objects with short scope that aren't shared can certainly be instantiated.
It's objects that require dependency injection that need to be under the control of the bean factory.
Just Mark that with #Autowired Annotation as shown below. That would do.
#Autowired
private DependencyClass dependencyClass;
Extending the answer of #duffymo:
Once you are done what he suggested, also make sure that "jdbc.driver.class.name" and "jdbc.url" are present in your main project properties file. If you are expecting that these values will be populated just because you have it in
properties file of "Dependent" project, you might get disappointed. Check this for more details:
https://stackoverflow.com/a/49564724/3458292
My application context XML is simple:
<context:component-scan base-package="com.depressio.spring" />
In that package, I have my configuration:
package com.depressio.spring
#Configuration
#ComponentScan(basePackages = "com.depressio")
public class DepressioConfiguration
{
#Inject private ApplicationContext context;
}
Within com.depressio, there's a repository (DAO):
package com.depressio.dao;
#Repository
public class ParameterDAO
{
public Parameter getParameter(long ID) { ... }
}
... and a service where injection is working just fine (no NPE when parameterDAO is used):
package com.depressio.resource;
#Service
#Path("/depressio/parameters")
public class ParameterResource
{
#Inject private ParameterDAO parameterDAO;
#Path("{id}")
public Response getParameter(long parameterID)
{
return Response.ok(parameterDAO.getParameter(parameterID).legacyFormat()).build();
}
}
However, the legacyFormat() method call there constructs another object. Within that object, I have to inject a different DAO (also annotated with #Repository, though). That injection isn't working.
So, we have the original Parameter object:
package com.depressio.domain;
public class Parameter
{
...
public LegacyParameter legacyFormat()
{
return new LegacyParameter(this);
}
}
... and the LegacyParameter where the injection isn't working:
package com.depressio.domain.legacy;
public class LegacyParameter
{
#Inject private LegacyDAO legacyDAO;
....
public LegacyParameter(Parameter newParameter)
{
// NullPointerException when using the injected legacyDAO.
}
}
I've tried a few things, including:
Using an no-args constructor for LegacyParameter, then calling a populate method so I'm not using the injected DAO until after the object is constructed. This didn't work.
Injecting the LegacyDAO into the ParameterResource and passing it in. This worked, but isn't ideal since I have to pass it around a whole lot (which injection should help avoid, no?). It did prove that LegacyDAO is injectible... just not into LegacyParameter apparently.
Adding a #Service, #Component, or #Named annotation on LegacyParameter. All end up with the NullPointerException on the line I try to reference the injected legacyDAO.
What am I missing?
As Sotirios has pointed out, it can't work since you create a regular Java object and do not give Spring a chance to enhance it.
Either let Spring create objects for which you want to enjoy the Spring 'magic' (like setting #Inject dependencies etc).
Or create your own objects and set the dependencies yourself (yourObject.setDao(dao)).
That said, there are exceptional cases in which you still want to create your objects 'on the fly' by yourself but rely on Spring to inject dependencies to these objects. In this case you should call Spring explicitly:
LegacyParameter p = new LegacyParameter(...);
applicationContext.getAutowireCapableBeanFactory().autowireBean(p);
I don't think you really need it in your case.
(see this link inject bean reference into a Quartz job in Spring? for an example when this is really required).
In addition, I would advice to simplify your configuration.
Why do you use both xml-based and java-based configuration that do actually the same? In your example you could keep only one of them and have the same effect.
Let's say we have a class:
public class MyClass {
#Autowired private AnotherBean anotherBean;
}
Then we created an object of this class (or some other framework have created the instance of this class).
MyClass obj = new MyClass();
Is it possible to still inject the dependencies? Something like:
applicationContext.injectDependencies(obj);
(I think Google Guice has something like this)
You can do this using the autowireBean() method of AutowireCapableBeanFactory. You pass it an arbitrary object, and Spring will treat it like something it created itself, and will apply the various autowiring bits and pieces.
To get hold of the AutowireCapableBeanFactory, just autowire that:
private #Autowired AutowireCapableBeanFactory beanFactory;
public void doStuff() {
MyBean obj = new MyBean();
beanFactory.autowireBean(obj);
// obj will now have its dependencies autowired.
}
You can also mark your MyClass with #Configurable annotation:
#Configurable
public class MyClass {
#Autowired private AnotherClass instance
}
Then at creation time it will automatically inject its dependencies. You also should have <context:spring-configured/> in your application context xml.
Just got the same need and in my case it was already the logic inside non Spring manageable java class which had access to ApplicationContext. Inspired by scaffman.
Solved by:
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
I used a different approach. I had spring loaded beans that I wanted to call from my extended classes of a third-party library that created its own threads.
I used approach I found here https://confluence.jaytaala.com/display/TKB/Super+simple+approach+to+accessing+Spring+beans+from+non-Spring+managed+classes+and+POJOs
In the non-managed class:
{
[...]
SomeBean bc = (SomeBean) SpringContext.getBean(SomeBean.class);
[...]
bc.someMethod(...)
}
And then as a helper class in the main application:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware
{
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException
{
SpringContext.context = context;
}
}
I wanted to share my solution that follows the #Configurable approach as briefly mentioned in #glaz666 answer because
The answer by #skaffman is nearly 10 years old, and that does not mean not good enough or does not work
The answer by #glaz666 is brief and didn't really help me solve my problem but, did point me in the right direction
My setup
Spring Boot 2.0.3 with Spring Neo4j & Aop starts (which is irrelevant anyway)
Instantiate a bean when Spring Boot is ready using #Configurable approach (using ApplicationRunner)
Gradle & Eclipse
Steps
I needed to follow the steps below in order to get it working
The #Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false) to be placed on top of your Bean that is to be manually instantiated. In my case the Bean that is to be manually instantiated have #Autowired services hence, the props to above annotation.
Annotate the Spring Boot's main XXXApplicaiton.java (or the file that is annotated with #SpringBootApplication) with the #EnableSpringConfigured and #EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
Add the dependencies in your build file (i.e. build.gradle or pom.xml depending on which one you use) compile('org.springframework.boot:spring-boot-starter-aop') and compile('org.springframework:spring-aspects:5.0.7.RELEASE')
New+up your Bean that is annotated with #Configurable anywhere and its dependencies should be autowired.
*In regards to point #3 above, I am aware that the org.springframework.boot:spring-boot-starter-aop transitively pulls the spring-aop (as shown here mavencentral) but, in my case the Eclipse failed to resolve the #EnableSpringConfigured annotations hence, why I explicitly added the spring-aop dependency in addition to the starter. Should you face the same issue, just declare the dependency or go on adventure of figuring out
Is there a version conflict
Why the org.springframework.context.annotation.aspect.* is not available
Is your IDE setup properly
Etc etc.
This worked for me:
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
See more information: https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
Found the following way useful for my use case. Sharing here for reference, credit goes to the blogger entirely. This creates a static field and populates that from Spring and then provides a public static method which returns the field populated above.
https://sultanov.dev/blog/access-spring-beans-from-unmanaged-objects/