In SpringBoot configuring, how do the classes of #Import get turned into a globally accessible List of classes? - spring

I have inherited some code that involves SpringBoot, #Import, and #Autowired. The code works, but I don't understand why. Can someone tell me what is going on?
In Spring Boot configuration code I have code much like this:
#Configuration
#Import({ FirstCombo.class, SecondCombo.class, ThirdCombo.class })
public class MyProgramConfiguration {
...
}
The (FirstCombo, SecondCombo, ThirdCombo) all extend the Combo class.
Then there is a business logic class that has this:
public class MyBusinessLogic {
#Autowired private List<Combo> combos;
public void doSomething() {
for(Combo c: combos) {
...
}
}
}
The method MyBusinessLogic has access to the List containing the imported Combo classes. It works in the real world. My best guess is that when #Import does its work it creates a globally available List<Combo> instance. But I'm unable to find documentation that this is what is really happening.
Can someone tell me what is really going on with this sort of code?
Thanks,
Jerome.

It's here
https://docs.spring.io/spring-framework/docs/2.5.x/reference/beans.html#beans-autowired-annotation
It is also possible to provide all beans of a particular type from the
ApplicationContext by adding the annotation to a field or method that
expects an array of that type:
public class MovieRecommender {
#Autowired
private MovieCatalog[] movieCatalogs;
// ... } The same applies for typed collections:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
#Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ... }
You can try also with
#Autowired
private List<Object> allObjects;
To fetch all available beans in your class.

Related

Is there a way for #SpyBean to create spies for all types based on the interface type in a Spring Boot test?

I have a Spring Boot application where I would like to ensure that a list of decorators are verified to be executed. These decorators all extend from the same Abstract class, which in turn extend from the same interface, and they are autowired into a service class as a list of decorators. I would have thought that providing the #SpyBean(MyDecorator.class) at the class level of the test would have done the trick, but I got the error specifying that the decorator is not a spy. It looks like the MockitoPostProcessor class expects that we provide the individual concrete classes in the annotation as so #SpyBean(classes = {decorator1.class,decorator2.class}). I tried the latter, and it worked.
However, the issue that I have with this is that we have to add to this list every time we create a new decorator, which is not ideal. This is why I thought it makes sense to have the interface type be checked as well. Please let me know if there is a better way of doing this, or if I missed something. A thought that crossed my mind was to define my own post processor to wrap any bean from a defined type in a mockito spy, but I would like to check here first. Here is a skeleton definition of the classes to help you understand my dilemma.
MyDecorator.java
public interface MyDecorator{
public void decorate(SomeObject obj);
}
AbstractDecorator.java
public class AbstractDecorator implements MyDecorator{
//common decorator logic
}
Decorator1.java
#Component
public class Decorator1 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
Decorator2.java
#Component
public class Decorator2 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
DecorationService.java
#Service
public class DecorationService implements Service{
#Autowired
private List<MyDecorator> decoratorList;
public void processDecorators(){
//go through list of decorators and process some object
}
}
DecoratorServiceTest.java
#Runwith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
//#SpyBean(MyDecorator.class) //<-- This doesn't wrap the classes in a spy and errors out
#SpyBean(classes = {Decorator1.class, Decorator2.class}) //<-- This works
public class DecoratorServiceTest{
#Autowired
private List<MyDecorator> decoratorList;
#Test
public void testProcessDecorator(){
//verify that each decorator was processed
}
}
I posted a spring boot github issue here. Hopefully we would either see an improvement on it or we get an explanation as to why it is designed in this way.
I have a workaround in place that I'm using which is I've created a class that implements Spring's BeanPostProcessor interface, and I override the postProcessAfterInitialization method, and I check if the class is what I'm expecting, then I would wrap it in a mockito spy. Also, you would need to define the spring bean.
Here is a snippet of the class that I created.
public class SpyBeanPostProcessor<T> implements BeanPostProcessor{
/**
* The class type to spy on.
*/
private Class<T> typeToSpy;
/**
* Construct a SpyBeanPostProcessor with a class type to wrap
* as a {#link org.mockito.Spy}
* #param typeToSpy The class type to spy on.
*/
public SpyBeanPostProcessor(Class<T> typeToSpy) {
this.typeToSpy = typeToSpy;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (typeToSpy.isAssignableFrom(bean.getClass())){
return Mockito.spy(bean);
}else{
return bean;
}
}
}
I also needed to create a new spring bean that loads the BeanPostProcessor as shown below.
#Bean
public static SpyBeanPostProcessor decoratorSpyBeanPostProcessor(){
return new SpyBeanPostProcessor(MyDecorator.class);
}

how to conditionally not create beans in spring boot?

In my application, I have a component that reads data from other system when the application is started.
However, during testing, I don't want this component to be created
#Component
#Slf4j
public class DeviceStatisticsSyncHandler {
#EventListener
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
}
I can use condition to solve this, but other code readers need to understand, so I don't think this is a very good method:
#EventListener(condition = "#deviceStatisticsSyncHandler .isServiceEnabled()")
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
public boolean isServiceEnabled() {
return !serviceEnabled;
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
My application doesn't use Profiles, is there any other method to solve this problem.
Spring Boot version:2.1.3
One possible option is not to load the DeviceStaticsticsSyncHandler at all if you're in a test mode.
The "test.mode" is not a good name here, because the production code contains something tightly bound to the tests.
How about the following approach:
#Component
#ConditionalOnProperty(name ="device.stats.handler.enabled", havingValue = "true", matchIfMissing=true)
public class DeviceStatisticsSyncHandler {
// do whatever you need here, but there is no need for "test.mode" enabled related code here
}
Now in Tests you can define a test property "device.stats.handler.enabled=false" on the test itself or even place that definition in src/test/reources/application.properties so it will be false for all tests in the module.
An obvious advantage is that this definition is pretty much self explanatory and can be easy understood by other project maintainers.
for me, it's not the case of the condition rather environment-related. I will solve this problem using spring profile.
Step 1: Create an Interface first
public interface DeviceStatisticsSyncHandler {
public void handle(ApplicationReadyEvent event);
}
Step 2: Create an Implementation for production
#Component
#Profile("!test")
public class DeviceStatisticsSyncHandlerImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
}
step 3: create an implementation of test
#Component
#Profile("test")
public class DeviceStatisticsSyncHandlerTestImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
//do Nothing
}
}
final step
All you need to do is set/toggle the property
-Dspring.profiles.active=test
or
-Dspring.profiles.active=prod
I found a way to achieve this without any further external configuration required.
The idea is to create a general configuration that applies to all integration tests and use #MockBean there to replace the real bean. So one should create a class like this under the test classpath (i.e. that is not scanned during normal application launch):
#Configuration
public class IntegrationTestConfiguration
{
#MockBean
public DeviceStatisticsSyncHandler deviceStatisticsSyncHandler;
}
I was actually surprised that #MockBean can be used here, but the Javadoc explicitly points that out: Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner..

Passing parameters to #Configuration in Spring

I have a requirement of creating a prototype bean that's stateful, i.e. take parameters in constructor.
I tried to use #Configuration to create that bean, but found it doesn't work if I use a parameterized constructor...
Note that the parameters I want to pass are NOT spring beans...they are simple POJOs...so I can't autowire them.
So this is what I want to do -
#Configuration
public class MyClassFactory {
#Bean
public MyClass getMyClass(Pojo1 pojo1, Pojo2 pojo2) {
return new MyClass (pojo1, pojo2);
}
}
#Scope("PROTOTYPE")
public class MyClass {
public MyClass(Pojo1 pojo1, Pojo2 pojo2) {
...
}
#Autowired SomeService1 service1;
#Autowired SomeService1 service2;
...
}
Of course I can make MyClass applicationContextAware, and pick up services from it, rather than making it a prototype bean...but was wondering why above pattern is not allowed...

#Autowired in static classes

This is an Spring MVC project with Hibernate.
I'm, trying to make a Logger class that, is responsible for inputting logs into database.
Other classes just call proper methods with some attributes and this class should do all magic.
By nature it should be a class with static methods, but that causes problems with autowiring dao object.
public class StatisticLogger {
#Autowired
static Dao dao;
public static void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public static void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public static void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){ //BUT DAO IS NULL
dao.saveOrUpdateEntity(elb);
}
}
How to make it right? What should I do not to make dao object null?
I know that I could pass it as a method parameter, but that isn't very good.
I'm guessing that autowired can't work on static objects, because they are created to early to autowiring mechanism isn't created yet.
You can't #Autowired a static field. But there is a tricky skill to deal with this:
#Component
public class StatisticLogger {
private static Dao dao;
#Autowired
private Dao dao0;
#PostConstruct
private void initStaticDao () {
dao = this.dao0;
}
}
In one word, #Autowired a instance field, and assign the value to the static filed when your object is constructed. BTW, the StatisticLogger object must be managed by Spring as well.
Classical autowiring probably won't work, because a static class is not a Bean and hence can't be managed by Spring. There are ways around this, for example by using the factory-method aproach in XML, or by loading the beans from a Spring context in a static initializer block, but what I'd suggest is to change your design:
Don't use static methods, use services that you inject where you need them. If you use Spring, you might as well use it correctly. Dependency Injection is an Object Oriented technique, and it only makes sense if you actually embrace OOP.
I know this is an old question but just wanted to share what I did,
the solution by #Weibo Li is ok but the problem it raises Sonar Critical alert about assigning non static variable to a static variable
the way i resolved it with no sonar alerts is the following
I change the StatisticLogger to singlton class (no longer static)
like this
public class StatisticLogger {
private static StatisticLogger instance = null;
private Dao dao;
public static StatisticLogger getInstance() {
if (instance == null) {
instance = new StatisticLogger();
}
return instance;
}
protected StatisticLogger() {
}
public void setDao(Dao dao) {
this.dao = dao;
}
public void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){
dao.saveOrUpdateEntity(elb);
}
}
I created a service(or Component) that autowire the service that i want and set it in the singlton class
This is safe since in spring it will initialize all the managed beans before doing anything else and that mean the PostConstruct method below is always called before anything can access the StatisticLogger
something like this
#Component
public class DaoSetterService {
#Autowired
private Dao dao0;
#PostConstruct
private void setDaoValue () {
StatisticLogger.getInstance().setDao(dao0);
}
}
Instead of using StatisticLogger as static class I just use it as StatisticLogger.getInstance() and i can access all the methods inside it
You can pass the DAO to StatisticLogger from where you call it.
public static void AddLoginEvent(LogStatisticBean user, DAO dao){
dao.callMethod();
}
It might be too late to put an answer to this question, especially when a question is already having an accepted answer. But it might help others in case they face the same issue.
inside the StatisticLogger class create an instance of the Dao service.
public static Dao daoService = new Dao();
then, auto-wire the service instance through the constructor of the StatisticLogger class.
#Autowired
public functionName(Dao daoService0) {
this.daoService = daoService0;
}
//use this service as usual in static class
daoService.fun();
I think this is the simplest solution for the problem.

How to use #Autowired to dynamically inject implementation like a factory pattern

I am fairly new to Sprint and am using Spring 3.x and roo1.1.1 for my application.
I have multiple implementation of an interface which would be #Autowired into other different classes. I would only be able to decide which implementation to go with at the runtime. This should be achieved with like a factory pattern.
public interface SomeInterface {
public void doSomething();
}
Implementation 1.
public class SomeOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Implementation 2.
public class SomeOtherOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Now in my service i needed this Autowired like
#Service
public class MyService {
#Autowired
SomeInterface ob;
//Rest of the code here
}
1) The logic to choose which implementation to be Autowired is only know runtime, so i cannot use the #Qualifier annotation to qualify this.
2) I tried to create a FactoryBean like
public class SomeFactoryBean implements FactoryBean<SomeInterface> {
#Override
public SomeInterface getObject() throws Exception {
if(/*Somecondition*/) {
return new SomeOb();
} else
return new SomeOtherOb();
}
#Override
public Class<? extends SomeInterface> getObjectType() {
if(/*Somecondition*/) {
return SomeOb.class;
} else
return SomeOtherOb.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
In the applicationContext.xml i have the tag mentioned.
When i run the webserver i run into an error like
No unique bean of type [com.xxxx.xxxx.SomeInterface] is defined: expected single matching bean but found 3: [xxxx, xxxxxxx, xxxxFactory]
Can anyone please help me to resolve this issue. If i am not doing this right please direct me to do this the right way.
Thanks and appreciate any help,
jjk
Thanks for the suggestion. I was able to solve the problem with help from a colleague. What i was doing wrong
I had the implementation of the SomeInterface with #Service. So this was picked up by the spring scanner and added to the bean.
During trial and error i removed the #Component annotation from by FactoryBean implementation.
After making these changes it worked like a charm.
return true from isSingleton() if you only need one implementation of the bean for a given instance of your application
But I question your design.
I would always use properties files to switch out implementations like this. I once had to implement CAPTCHA integration for a site. We were prototyping with the JCaptcah and ReCAPTCHA APIs. I created a new interface that contained just the functionality we needed and then created implementations for both APIs. Using a placeholders in the Spring configuration file and Maven profiles, we could switch out the implementation class at compile time or deployment time, for example, mvn jetty:run -DcaptchaImpl=recaptcha or -DcaptchaImpl=jcaptcha.
Without knowing the task that you want to accomplish, it's hard to provide more advice.

Resources