I am pretty new in Spring and I have some doubts about how is injected some classes into a controller class.
Into my project I have this HomeController class:
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
#Autowired
private MessageSource messageSource;
#Autowired
private Environment env;
.....................................................
.....................................................
.....................................................
}
My doubt is related to the 2 objects MessageSource messageSource and Environment env classes.
As you can see these classes are injected by the #Autowired annotation.
The problem is that I have not bean definition into my XML configuration for these classes. So why are correctly injected? Where are the definition of these bean?
Tnx
Automatic discovery of beans is based on the following rules:
1) Use context:annotation-config tag in spring-config.xml to let
Spring use Annotations
2) Use context:component-scan tag in
spring-config.xml and tell Spring the package in which to look for
auto-discovering beans
3) Use #Component annotation to mark a class
as a Spring auto-discoverable bean
If #Component annotation is used, then the bean declarations need not be declared in spring-config.xml
Spring mappings can be done with XML or with annotations.
In your case, if no XML defined, your MessageSource and Environment classes should be mapped by Spring annotations like #Component #Service or #Resource:
#Component
Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
#Autowired
#Autowired annotation will try to find a bean of type Foo in the spring context and will then inject the same.
#Resource
Similar to this is #Resource annotation that will try to find the bean with the name "foo". To summarize, #Autowired wires by type and #Resource wires by name.
Both the Environment and the MessageSource are closely tied to the inner workings of Spring Framework.
The environment is part of the application context and will be available for autowiring.
The ApplicationContext interface extends the MessageSource interface and will be available for autowiring as a message source, even if you have not defined your own message source bean. (If you define your own message source, the application context will delegate to that)
Related
Have seen below code. Why we need to use this Configuration and Service annotation together.
#Configuration
#Service
public class SomeClass{
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public MongoClient somemethod(#Value){
.....
return mongoClient;
}
Also #Bean default scope is singleton then why again mention
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
The #Service annotation makes SomeClass a bean, whereas the #Configuration and #Bean are intended to create bean definitions for classes that you did not write(i.e. library classes).
And indeed the default scope is a singleton so the annotation can be ommited.
#Configuraton and #Service annotations has different purposes.
#Configuration annotation indicates that a class declares one or more bean methods and may be processed by spring container to generate bean definitions and service requests for those beans at runtime.
#Service annotation indicates than an annotated class is a "Business Service Facade" or similar thing.
Singleton is the default scope for a Spring Bean. So it is not necessary to define its scope. However, for better clarity and understanding, you can highlight this fact by using the #Scope annotation.
I have a class called EmployeeService and which is annotated with #service annotation from spring framework
package com.sample.EmployeeService
#Service
public class EmployeeService {
}
and I also have entry in context.xml
<bean id="empSer" class ="com.sample.EmployeeService"
May I know how many bean has been created in psring container.
In here
Spring will scan all the classes with #Service annotation, register it as a bean, then it will inject the dependencies that have #Autowired annotation.
#Service
public class EmployeeService {
}
In here
The context.xml is Spring's advanced container. Similar to BeanFactory, it can load bean definitions, wire beans together
<bean id="empSer" class ="com.sample.EmployeeService"
Actually you get one single bean - whether context.xml it is loaded first or classes with #Service annotation
And
If first loaded context.xml and second loaded #Service annotation class, Spring Application overwrite by default as the beans are being loaded up into the factory
I want to add #Component classes to spring container after ApplicationContext load. But I cannot use BeanFactory. Because I'm using BeanFactory I have to define beans for these classes. But I cannot defined it(If I am not use reflection). Because these classes will load at runtime by ClassLoader.
For example
#Component
public class Service {
private final CustomerService customerService;
private final OrderService orderService;
private final PaymentService paymentService;
#Autowired
public Service(CustomerService customerService, OrderService orderService, PaymentService paymentService) {
this.customerService = customerService;
this.orderService = orderService;
this.paymentService = paymentService;
}
}
In this example Spring create bean for this class while application invoking. There is no need to define bean with #Bean. But what I want is compile spring projects and load those clasess from another project and add to Spring ApplicationContext. So i can autowire these. Otherwise I have to create bean with reflection at runtime. And if I use reflection, I have invoke all dependent class recursively.
Is there any way do it without create beans using reflection at runtime.
If I understand things correctly: you have some classes that are marked with #Component and you want Spring to manage their lifecycle?
Does this help: https://springframework.guru/spring-component-scan/ ? #ComponentScan specifically or in XML config something like:
<context:component-scan base-package="org.example"/>
I found a solution.
ConfigurableApplicationContext context = SpringApplication.run(EventServiceApplication.class, args);
// Load class ...
context.start();
If we run context.start() method after load class, spring create #Component like class beans and put to spring container.
Another solution (This is exact solution):
ConfigurableApplicationContext context = SpringApplication.run(EventServiceApplication.class, args);
List<Class<?>> classes = // load classes
classes
.stream()
.filter(clazz -> clazz.isAnnotationPresent(Component.class) || Arrays.stream(clazz.getAnnotations()).anyMatch(annotation -> annotation.annotationType().isAnnotationPresent(Component.class)))
.forEach(applicationContext::register);
After register the classes, maybe one of your loaded class annoteded with #Configuration and it contains #Bean annotated methods. For register those #Bean methods. You should use
ConfigurationClassPostProcessor configurationClassPostProcessor; // This class is autowireable. Spring has bean for this class at spring bean container.
configurationClassPostProcessor.processConfigBeanDefinitions(applicationContext.getDefaultListableBeanFactory())
I found this solution at Spring Framework source code
I'm learning Spring boot framework (version 2.0.7.RELEASE) for developing web application. When I try to autowire MessageSource class it works for one classes, but doesn't for another:
Here is my WebConfig class:
package net.local.mis.phog.config;
#Configuration
public class WebConfig implements WebMvcConfigurer {
[...]
#Bean
public MessageSource messageSource() {
// This is the only place around application where messageSource is created
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
[...]
}
Everything works fine in controller classes, for example:
package net.local.mis.phog.controller;
#Controller
#Transactional
public class AlbumController {
[...]
#Autowired
private MessageSource messageSource; (works fine)
[...]
}
But when I try to inject messagSource in model classes it failes:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
[...]
#Autowired
private MessageSource messageSource; (null)
[...]
}
In spring - component autowired in one class but not in another Balaji Krishnan says: "You are not using spring to obtain Account instance - so spring did not get a chance to autowire it". Perhaps I should do something of the kind, but I cannot understand how.
Can anybody please help me.
Thank you, Mikhail.
For spring to leverage dependency injection, the beans must all be managed by spring. The AlbumModel object creation should be managed by spring, so that the MessageSource can be autowired. If AlbumModel should not be managed by spring and you want to create the object yourself (which I doubt because you annotated it with #Component) then you could also use constructor injection.
Whereas you can have something like this:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
[...]
private MessageSource messageSource;
[...]
#Autowired
public AlbumModel(MessageSource messageSource) {
this.messageSource = messageSource;
}
}
With the solution above, when you manually create an AlbumModel, you could pass in the MessageSource object, which is already autowired by the calling class (for example the controller or any service layer class). But also when AlbumModel creation is manage by spring it is advisable to use constructor injection. Read more about it on an article by a Spring contributor
Thank you guys for your answers.
Amit Bera, I create AlbumModel object just by calling its constructor:
package net.local.mis.phog.controller;
#Controller
#Transactional
public class AlbumController {
[...]
#Autowired
private JenreDao jenreDao;
#Autowired
private MessageSource messageSource;
[...]
#RequestMapping(value={"/album"},method=RequestMethod.GET)
public String albumShowHandler(Model model,
HttpServletRequest request,
#RequestParam(value="jenreIdSelected") Long jenreIdSelected,
#CookieValue(value="thumbnailOrder",defaultValue="ordDefault") String thumbnailOrder,
[...]) {
[...]
ThumbnailOrderAux thumbnailOrderAux = new ThumbnailOrderAux(thumbnailOrder);
JenreAux jenreAux = new JenreAux(jenreDao.getLstJenreModel(null,jenreIdSelected),jenreIdSelected);
AlbumModel albumModel = new AlbumModel(jenreAux,thumbnailOrderAux);
[...]
model.addAttribute("albumModel",albumModel);
return "album";
}
[...]
}
And here is code of AdminModel constructors:
package net.local.mis.phog.model;
#Component
public class AlbumModel {
private JenreAux jenreAux;
private ThumbnailAux thumbnailAux;
[...]
public AlbumModel() {
super();
}
public AlbumModel(JenreAux jenreAux,ThumbnailOrderAux thumbnailOrderAux) {
super();
this.jenreAux = jenreAux;
this.thumbnailOrderAux = thumbnailOrderAux;
}
[...]
}
Nazeem, I added #Component annotation to AlbumModel class in hope that MessageSource bean would be injected. Unfortunately it was not.
I could pass MessageSource as argument and it works even if I don't annotate constructor #Autowired. If I understand it right MessageSource bean is injected to AlbumController because it managed by Spring, and it isn't injected to AlbumModel because it doesn't managed by Spring. Right? If so, is there any way I make Spring manage AlbumModel object?
I had the same problem on a spring 3.1.1-RELEASE old application. In my case during debug I've got two different MessageResource objects as if the wold have been instanziated twice.
I've solved moving the bean definition:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
from dispatcher-servlet.xml xml-based configuration file to the applicationContext.xml ones.
In other words, there exists two contexts: the application context and the dispatcher servlet contexts. The application context is the parent of the dispatcher servlet context (I guess that many servlet contexts may exists under the same application context if your application consists of many servlet).
Thus, beans defined in the application context are visible to the servlet context, but not vice versa. While the servlet context contains those beans that are specifically related to MVC, the application context is used to define broader beans like services.
Since #Controller annotation is a #Component annotated ones:
#Component
public #interface Controller
i guess Spring autowires beans in #Controller and #Component differently, looking for beans in the two different contexts mentioned above.
I know springs AnnotationConfigApplicationContext is capable of accepting not only #Configuration classes as input but also plain #Component classes and classes annotated with JSR-330 metadata.
I have created AppConfig.java below without #Configuration annotation.
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
Passed this class as my java config class to AnnotationConfigApplicationContext, it accepted and registered my service beans.
I did some modification on above same AppConfig like below.
#Component
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
passed AppConfig to AnnotationConfigApplicationContext, it accepted and registered my service beans.
Question:
AnnotationConfigApplicationContext class is accepting the java config class with #Configuration, without #Configuration and with #Component annotations, what is the difference between #Component and #Configuration?
Why is it Accepting even without #Configuration annotation?
When to use #Configuration, and when to use #Component as java config class?
#Component
Indicates that an annotated class is a "component".
That is, in a context where component scanning is enabled, Spring generates bean definitions for #Component annotated types. These bean definitions end up being turned into beans.
#Configuration, which is itself annotated with
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, [...]
So any #Configuration type, for which Spring generates a bean, acts as a factory for beans.
The javadoc of #Bean states
#Bean methods may also be declared within classes that are not
annotated with #Configuration. For example, bean methods may be
declared in a #Component class or even in a plain old class. In such
cases, a #Bean method will get processed in a so-called 'lite' mode.
Bean methods in lite mode will be treated as plain factory methods by
the container (similar to factory-method declarations in XML), with
scoping and lifecycle callbacks properly applied. The containing class
remains unmodified in this case, and there are no unusual constraints
for the containing class or the factory methods.
In contrast to the semantics for bean methods in #Configuration
classes, 'inter-bean references' are not supported in lite mode.
Instead, when one #Bean-method invokes another #Bean-method in lite
mode, the invocation is a standard Java method invocation; Spring does
not intercept the invocation via a CGLIB proxy. This is analogous to
inter-#Transactional method calls where in proxy mode, Spring does not
intercept the invocation — Spring does so only in AspectJ mode.
So #Bean methods have full functionality in #Configuration annotated classes and limited functionality in #Component annotated classes.
why it is Accepting even without #Configuration annotation?
That's how the class is designed. An ApplicationContext is a BeanFactory. AnnotationConfigApplicationContext simply offers an extra way to register a bean definition.
When to use #Configuration, and when to use #Component as java config class?
These really completely separate goals. Follow the javadoc. When you need to setup an ApplicationContext, you can use an AnnotationConfigApplicationContext with a #Configuration annotated class. If you simply need a bean, annotate its type with #Component.
#Component annotation marks the Java class as a bean, but #Configuration annotation marks the Java class containing beans (methods that have #Bean annotation).
The #Bean annotation must use with #Configuration exactly to create Spring
beans.
In following class
#Component
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
#Bean annotation is not any effect, and getSampleService() method will be plain old java method and will not be singleton, because as i mentioned, #Bean annotation must use with #Configuration, so it must be repaired as following:
#Configuration
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
so replacing #Configuration annotation with any other annotation, or removing it, just make #Bean annotation ineffective and getSampleService() method will not be singleton anymore.