I'm new to Java and Spring, coming from C# and the .NET world, so bear with me - what I am attempting to do may be off the mark...
I am attempting to configure Spring DI using Java configuration and annotations, not XML configuration, however I am having a few issues. This is for a standalone application, not a web app. I have worked through the springsource documentationand as far as I can tell my very basic configuration should be correct...but isn't. Please take a look at the code below:
Java Configuration Annotated Class:
package birdalerter.common;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import birdalerter.process.ISightingsProcessor;
import birdalerter.process.SightingsProcessor;
#Configuration
#ComponentScan({"birdalerter.process", "birdalerter.common"})
public class AppConfig {
#Bean
#Scope("prototype")
public ISightingsProcessor sightingsProcessor(){
return new SightingsProcessor();
}
}
Configure Component implementing the ISightingsProcessor interface:
package birdalerter.process;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import org.springframework.stereotype.Component;
import birdalerter.domainobjects.IBirdSighting;
#Component
public class SightingsProcessor implements ISightingsProcessor{
private LinkedBlockingQueue<IBirdSighting> queue;
private List<ISightingVisitor> sightingVisitors = new ArrayList<ISightingVisitor>();
public SightingsProcessor(){
}
...
}
Configure Factory Component:
package birdalerter.process;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
public class ProcessorFactory {
private ISightingsProcessor sightingsProcessor;
#Autowired
#Required
private void setSightingsProcessor(ISightingsProcessor sightingsProcessor){
this.sightingsProcessor = sightingsProcessor;
}
public ISightingsProcessor getSightingsProcessor(){
return this.sightingsProcessor;
}
}
Wire up the AnnotationConfigApplicationContext and test:
#Test
public void testProcessingDI(){
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
System.out.println(processor);
Assert.assertTrue(processor != null);
}
The SightingsProcessor is not being setter injected and the assert is failing as the returned object is null. Hopefully I have missed something very obvious.
Thanks in advance.
Edited in Response to Meriton:
Thanks for the answer Meriton.
Why would Spring not know about the newly created object? Does Spring not maintain dependencies throughout the application lifecycle and inject as appropriate when new objects are created that are configured as beans?
I don't want to directly use context.getBean(ISightingsProcessor.class) if I can help it to be honest, I would like the dependency injected in the setter method without having manual intervention - it just seems cleaner.
I am using the ProcessorFactory as the ISightingsProcessor interface extends Runnable - the implementing object is to be started as a thread. The application will be configurable to have n* threads, with each being started within a loop iteration. I don't think it is possible (I may be wrong, please advise if so) to have #Autowired annotations within method declarations, hence I use the factory to supply a new instance of the injected ISightingsProcessor concrete class.
Yes I've just had a look regarding the #Scope annotation - you are right, that needs moving to the AppConfig #Bean declaration (which I've done in this edit), thanks for that.
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
This calls the constructor of ProcessorFactory, and then the getter of the instance the constructor created. Spring can not know about that newly created object, and therefore not inject its dependencies. You should ask Spring for the ProcessorFactory instead, for instance with
ProcessorFactory pf = context.getBean(ProcessorFactory.class);
ISightingsProcessor processor = pf.getSightingsProcessor();
That said, I don't know why you need class ProcessorFactory at all. You might just as well get the ISightingsProcessor directly:
ISightingsProcessor processor = context.getBean(ISightingsProcessor.class);
Additionally, "Java Based Configuration" and component scanning are independent ways to declare beans. Currently, you are therefore declaring the ISightingsProcessor twice: Once with the #Bean-annotated factory method, and once with the component scan and the #Component annotation on the class. Doing either of that will do. In fact, doing both might cause one bean definition to override the other.
Oh, and the #Scope annotation is for bean definitions (those you annotate with #Bean or #Component). It will likely be ignored on injection points (#Autowired).
Related
In a real project, I found out that #Component may be omitted in the following code:
// no #Component !!!!!
public class MovieRecommender {
private final CustomerPreference customerPreference;
#Autowired
public MovieRecommender(CustomerPreference customerPreference) {
this.customerPreference = customerPreference;
}
// ...
}
#Component
public class CustomerPreference {...}
(The example is taken from the official Spring docs https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation , and the docs show no #Component at all, which may mean either that it is not needed, or that it is just not shown.)
The project where I work does not use any XML bean declarations, but it uses frameworks other than just Spring, so it is possible that something declares the class as a bean. Or it may be a feature of the version of Spring that we use, and if that feature is not documented, it may be dropped later.
Question:
Must the class that uses #Autowired be annotated with #Component (well, be a bean)? Is there any official documentation about that?
UPD Folks, there is no #Configuration and no XML configs in the project, I know that such declarations make a bean from a class, but the question is not about them. I even wrote "(well, be a bean)" in the question above to cover that. Does #Autowired work in a class that is not a bean? Or maybe it declares the class that uses it as a bean?
there are several ways to instantiate a bean in Spring.
One of them indeed is with the #Component annotations, with that, Spring will scan all the packages defined for component-scan and initialize all annotated classes (either with #Component or one of the annotations that uses it - Controller, Service, etc.).
Other way to initialise beans is using a configuration class (annotated with #Configuration) that includes methods annotated with #Bean. each of these methods will create a bean.
There's also an option to create the beans using xml configurations, but this is becoming less and less common, as the annotation-based approach is more convinient
According to https://stackoverflow.com/a/3813725/755804 , with autowireBean() it is possible to autowire a bean from a class not declared as a bean.
#Autowired
private AutowireCapableBeanFactory beanFactory;
public void sayHello(){
System.out.println("Hello World");
Bar bar = new Bar();
beanFactory.autowireBean(bar);
bar.sayHello();
}
and
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
public class Bar {
#Autowired
private Foo foo;
public void sayHello(){
System.out.println("Bar: Hello World! foo="+foo);
}
}
On the other hand, by default the latest Spring does not assume that classes that use #Autowire are #Component-s.
UPD
As to the mentioned real project, the stack trace shows that the constructor is called from createBean(). That is, the framework creates beans from classes declared in the framework's configs.
I have been struggling to understand fully how the dependency injection works via #Configuration, #Bean and #Component annotations.
My Code is as follows.
1) Route.kt
/* Route.kt */
package com.example.service
import com.example.service.ports.AutoComplete
import com.example.service.ports.Validation
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.web.reactive.function.server.router
#Configuration
#Suppress("unused")
class Routes(private val autoComplete: AutoComplete,
private val validation: Validation) {
#Bean
fun route() = router {
("/service-lookup" and accept(APPLICATION_JSON)).nest {
GET("/auto-complete/{service}", autoComplete::autoComplete)
GET("/validation/{service}", validation::validation)
}
}
}
2) ServiceImpl.kt
package com.example.service
import com.example.service.ports.AutoComplete
import com.example.service.ports.Validation
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
#Component
#Suppress("unused")
class ServiceImpl: AutoComplete, Validation {
override fun autoComplete(request: ServerRequest): Mono<ServerResponse> {
TODO("not implemented autoComplete") //To change body of created functions use File | Settings | File Templates.
}
override fun validation(request: ServerRequest): Mono<ServerResponse> {
TODO("not implemented validation") //To change body of created functions use File | Settings | File Templates.
}
}
3) ports/AutoComplete.kt
package com.example.service.ports
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
interface AutoComplete {
fun autoComplete(request: ServerRequest): Mono<ServerResponse>
}
4) ports/Validation.kt
package com.example.service.ports
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
interface Validation {
fun validation(request: ServerRequest): Mono<ServerResponse>
}
My question is, how does the route bean created from Route.kt know that the autoComplete and validation should use the ServiceImpl class from ServiceImpl.kt?
The following description of the Spring mechanism is simplified for you example:
At startup Spring Boot scans the classpath for all classes annotated with #Configuration, #Component, etc and builds a bean definition list. In your example it finds the Routes and ServiceImpl classes.
After this Spring scans all methods of every class in the bean definition list for further #Bean annotations and adds the method (especially the return type) to the bean definition list. In your example it finds the route method.
After the first scan Spring knows which bean types are present and which bean class implements which interfaces. And Spring knows which constructor parameters, Inject targets or method parameters are required to create an instance of the bean. With this information Spring instantiates the beans in the correct order.
In your example Spring knows that Router requires AutoComplete and Validation and that ServiceImpl implements these interfaces. Therefore it has to instantiate ServiceImpl first and Router later.
If more then one bean implements the same interface Spring throws an exception and you have to further qualify the bean.
I will try to clarify:
#Component - It is tighty bound to the spring auto configuration and component scanning. Everything marked with Component will be picked up as long as you have it in the path of the component scanning defined by #ComponentScanned annotation. The #Components come into three flavours:
A) Repositories - used for persistence
B) Controllers for example RestController
C) Service - a service without state . F.ex a Facade.
This annotation is used to automate the construction of the Application context,as well as to define a stereotype for the bean bound to the context.
#Bean - #Bean and #Component have the same goal, but #Bean is not a #Component. They both build the application context, but they do it in a very different way. While #Component defines a stereotype of the class and tells spring to pick it up. The Bean has full responsibility of manually configuring the instance of what you are creating. The implementation and the configuration are fully separated and you receive a higher degree of control on how exactly the Bean is generated.
#Configuration is used in conjunction with #Bean. #Bean in contrast to #Component is a method level annotation therefore the usual case is that a class is marked with #Configuration and then one ro more methods annotated with #Bean are following.
In your particular example
You have create a #Bean router. The router was created based on the autoComplete and validation which were injected in the Routes. Spring was able to figure out what to inject based on the best matching candidate. Since you have a single implementing bean instance of the two interfaces AutoComplete, Validation it injects it. In your case autoComplete and validation will point to the same instance.
I am configuring a spring boot application. So I created a property bean class like this:
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#EnableConfigurationProperties
#PropertySource("classpath:/application.properties")
public class APPStaticProperties {
#NotNull
#Value("${dblog.system.name}")
String db_logsystem_name;
/**
* #return the db_logsystem_name
*/
public String getDb_logsystem_name() {
return db_logsystem_name;
}
/**
* #param db_logsystem_name the db_logsystem_name to set
*/
public void setDb_logsystem_name(String db_logsystem_name) {
this.db_logsystem_name = db_logsystem_name;
}
}
And I am creating a object by in the controller class:
#Autowired
APPStaticProperties appStaticProperties;
But I wanted to know how do I pass the this object for use in other projects? Because my code flow goes from the controller to a JSP and then to a class in another project. I need the object available for use in that class. Well to be honest, in many other projects as well! I am not able to figure it out how to do it. Neither are there too many examples out there for reference.
You wouldn't normally inject an #Configuration bean into other Spring managed beans, because it sets up configuration to be used by other Spring managed beans.
For example, because you have declared an #PropertySource, to access your properties in other Spring managed means you can just inject properties into your code elsewhere using #Value. You don't need to inject your APPStaticProperties bean to do this.
i want to execute some code during (or rather at the end of) application startup. I found a couple of resources doing this using #PostConstruct annotation, #EventListener(ContextRefreshedEvent.class), implementing InitializingBean, implementing ApplicationListener... All of them execute my code at startup, but the placeholder of the application properties are not replaced at that moment. So if my class has a member with an #Value("${my.property}") annotation, it returns "${my.property}" instead of the actual value defined in the yaml (or wherever).
How do i accomplish to execute my code after the replacement took place?
You can implement InitializingBean which has a method named afterPropertiesSet(). This method will be called after all properties placeholders are replaced.
#PostConstruct is called when bean is created. Ypu have to check if spring found file with properties.
If you have a config class, #Configuration, then you can try explicitly importing your properties file by adding the following annotation:
#PropertySource("classpath:your-properties-file.properties")
Any other non-config resources should load after your config classes and your #Value annotations should work fine.
You should implement ApplicationListener<ContextRefreshedEvent> like this:
#Component
public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Value("${my.property}")
private String someVal;
/**
* // This logic will be executed after the application has loded
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
// Some logic here
}
}
You can get it after spring boot start.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
#Component
#Order(0)
class ApplicationReadyInitializer implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
ResourceLoader resourceLoader;
#Value("${my.property}")
private String someVal;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// App was started. Do something
}
}
I was wondering is there an annotations based method for starting a spring application?
i.e. replacing this below:
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
User user = (User)ctx.getBean("user");
user.createUser();
}
With an annotations based method for getting the context and then Autowiring in the bean?
I don't think you can do that, after all someone would have to understand and process that annotation. If Spring has not initialized yet, who would?
There is an anotation in spring: called #ContextConfiguration
Very helpfull for testing.
You need to extend one of the spring abstract classes created for test support(testNG or JUnit).
(e.g. AbstractTransactionalTestNGSpringContextTests tor testNG or AbstractTransactionalJUnit4SpringContextTests for JUnit)
Then you just use the #ContextConfiguration annotation(for Class, interface (including annotation type), or enum declaration)
some example code for junit test:
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/applicationContext.xml" and "/applicationContext-test.xml"
// in the root of the classpath
#ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-test.xml"})
public class MyTest {
// class body...
}
please read:
http://static.springsource.org/spring/docs/2.5.x/reference/testing.html
You cannot avoid that ClassPathApplicationContextXml code but you can avoid that ctx.getBean("user") by doing as below. Ideally what it does is it asks the xml to scan the packages where spring want to inject things. The only thing you should not here is that i have declared my main class as spring Component, since springs annotations work on spring recognized classes and hence i am making my main as spring recognized class. The loading of xml using Classpathapplicationcontext cannot be avoided.
package com.spring.sample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import com.spring.sample.component.Sample;
#Component
public class SampleMain {
#Autowired
Sample testSample;
static ApplicationContext appCtx = new ClassPathXmlApplicationContext("META-INF/webmvc-application.xml");
public static void main(String[] args){
SampleMain sampleMain = appCtx.getBean(SampleMain.class);
sampleMain.invokeSample();
}
private void invokeSample(){
testSample.invokeSample();
}
}