Spring boot get #value within a class extending a dependency - spring-boot

Folks, am trying to access config properties from within a class that I've extended from a dependency. Apparently the config class is returning a null pointer in my implementation class (CustomUtil).
pom.xml
...
<dependency>
<groupId>com.utilapp</groupId> //3rd party library that I need to use
<artifactId>util-lib</artifactId>
</dependency>
...
The BaseUtil is from the dependency. CustomUtil is a bean in my SpringBoot App & am overriding the method as below to check the key from the config property ignoreKeyList .
package com.myapp
...
#Component
public class CustomUtil extends BaseUtil {
#Autowired
private ClientConfig clientConfig; // This returns null!
#Override
protected boolean shouldExcludeFromList(String key) {
return this.clientConfig.getIgnoreKeyList.contains(key); // their library/util excludes certain keywords from the content being formatted
}
}
Config class in my SpringBoot App.
package com.myapp
...
#Configuration
#Getter
#Setter
public class ClientConfig {
#Value("${myapp.ignorekeys}")
private List<String> ignoreKeyList;
...
...
}
Main class in my app.
package com.myapp
...
#SpringBootApplication
public class MyClientApplication {
public static void main(String[] args) {
SpringApplication.run(MyClientApplication.class, args);
}
}
Service implementation class in my SpringBoot App.
package com.myapp
import com.utilapp.formatters.FormatBuilder;
import com.utilapp.formatters.TextFormatter;
...
#Service
public class ServiceImpl implements MyService {
#Autowired
private ClientConfig clientConfig; // works here!
public String formatContent(String content) {
TextFormatter formatter = this.getTextFormatter();
return formatter.parseAndFormat(content);
}
private TextFormatter getTextFormatter() {
return FormatBuilder.custom() // FormatBuilder is from the dependency
.withApplication(this.clientConfig.getAppName()) // In their library, the BaseUtil gets instantiated which am extending in my CustomUtil bean
...
...
.withIndentSupport(Boolean.TRUE)
.build();
}
}
Controller in my app
package com.myapp
...
#RestController
#RequestMapping("/app/v1")
public class StyleController {
#Autowired
private MyService myService;
#GetMapping("/format")
public String formatContent(#RequestParam String content) {
return this.myService.formatContent(content);
}
}
When I debug ClientConfig, it loads the properties from the config file; application.properties in src\main\resources.
But for some reason the ClientConfig is returning null from within the CustomUtil bean. Not sure why!
I originally thought it was due to ordering of bean instantiation. I tried playing around with #Order, #DependsOn, #ComponentScan, #ScanBasePackages etc, but none yields.
Any pointers/guidance please.
Thanks.

Related

Spring #Bean - creation of Spring Bean depends on service class name

I use Spring 4.2.8 and I do have the service class below. If this class has the name ScheduleEmailCreateAndSendServiceImpl than everything works fine (method generalEmailMessage is invoked at start time in order to create the Spring Bean)
If I rename this class to EmailCreateAndSendServiceImpl than method generalEmailMessage will not be invoked at start time - does anyone know why?
#Service("emailCreateAndSendService")
public class ScheduleEmailCreateAndSendServiceImpl extends AbstractService
implements EmailService {
protected EmailMessage generalMessage;
#Override
public void createAndSendMessage(String receiver, boolean emailActive, Object[] values) throws BusinessException {
// create and send email
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
[EDIT]
with this code it is the same
#Configuration
public #Data class ScheduleGeneralEmailConfiguration {
protected EmailMessage generalMessage;
public ScheduleGeneralEmailConfiguration() {
System.out.println("asdf");
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
#Bean annotated methods should be in #Configuration annotated class.
You can also put the #Bean annotated methods in the main class of the Spring Boot application annotated with #SpringBootApplication which encapsulates #Configuration, #EnableAutoConfiguration, and #ComponentScan annotations.
Make sure your #Configuration annotated class is placed in the same package or sub package of the Spring Boot Application class

SpringBoot: how to inject two classes having same name

In my application, I have two classes having the same name, but of course in different packages.
Both classes need to be injected in the application; Unfortunately, I get the following error message:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'myFeature' for bean class [org.pmesmeur.springboot.training.service.feature2.MyFeature] conflicts with existing, non-compatible bean definition of same name and class [org.pmesmeur.springboot.training.service.feature1.MyFeature]
My issue can be reproduced by the following sample:
#Component
#EnableConfigurationProperties(ServiceProperties.class)
public class MyService implements IService {
private final ServiceProperties serviceProperties;
private final IProvider provider;
private final org.pmesmeur.springboot.training.service.feature1.IMyFeature f1;
private final org.pmesmeur.springboot.training.service.feature2.IMyFeature f2;
#Autowired
public MyService(ServiceProperties serviceProperties,
IProvider provider,
org.pmesmeur.springboot.training.service.feature1.IMyFeature f1,
org.pmesmeur.springboot.training.service.feature2.IMyFeature f2) {
this.serviceProperties = serviceProperties;
this.provider = provider;
this.f1 = f1;
this.f2 = f2;
}
...
package org.pmesmeur.springboot.training.service.feature1;
public interface IMyFeature {
void print();
}
package org.pmesmeur.springboot.training.service.feature1;
import org.springframework.stereotype.Component;
#Component
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("HelloWorld");
}
}
package org.pmesmeur.springboot.training.service.feature2;
public interface IMyFeature {
void print();
}
package org.pmesmeur.springboot.training.service.feature2;
import org.springframework.stereotype.Component;
#Component
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("FooBar");
}
}
If I use different names for my classes MyFeature, my problem disappears!!!
I am used to work with Guice and this framework does not have this kind of problem/limitation
It seems that the spring dependencies injection framework uses only
the class-name instead of package-name + class-name in order to
select its classes.
In "real-life" I have this problem with a far-bigger project and I would strongly prefer not to have to rename my classes: can anyone help me?
One last point, I would prefer to avoid "tricks" such as using
#Qualifier(value = "ABC") when injecting my classes: in my sample,
there should be no ambiguity for finding the correct instance of
MyFeature as they do not implement the same interface
Simply re-implementing BeanNameGenerator adds a new problem for beans declared/instantiated by names
#Component("HelloWorld")
class MyComponent implements IComponent {
...
}
#Qualifier(value = "HelloWorld") IComponent component
I solved this issue by extending AnnotationBeanNameGenerator and redefining method buildDefaultBeanName()
static class BeanNameGeneratorIncludingPackageName extends AnnotationBeanNameGenerator {
public BeanNameGeneratorIncludingPackageName() {
}
#Override
public String buildDefaultBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
return beanDefinition.getBeanClassName();
}
}
You can assigna a value for each component e.g. #Component(value="someBean") and then inject it with #Qualifier e.g.
#Autowired
public SomeService(#Qualifier("someBean") Some s){
//....
}
Spring provides autowire by type and name. Your classname are same. By default spring considers only className not package. But you can override this behaviour by defining custom implementation of BeanNameGenerator interface in which you can generate name using both package and name. I am not providing code solution because i think you should explore more on this.
You can do something like this;
in package a
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("FooBar");
}
}
in package b
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("HelloWorld");
}
}
and in some config class;
#Configuration
public class Configuration {
#Bean
public a.MyFeature f1() {
return new a.MyFeature();
}
#Bean
public b.MyFeature f2() {
return new b.MyFeature();
}
}
Then you can autowire them with names f1 and f2, that are the names of their respective bean constructor methods.
You can do the similar thing with #Component("f1") &
#Component("f2")
Even though different interfaces are implemented and are in different packages, identical bean name causes this trouble, and you have to utilize some sort of custom naming to distinguish. Utilizing some custom Spring logic would be way too ugly compared to what you'd do with above solutions.

Spring Boot/Batch with multiple #Configuration classes not instantiating bean (or loading properties)

So first, apologies if this seems to similar to other problems - I've looked, tried the proposed solutions, and none have solved the problems. First the sanitized code...
package com.mine.batchMain;
#SpringBootApplication
#Configuration
public class MyApplication implements CommandLineRunner {
//...
public static void main(String[] args) {
//....
}
}
package com.mine.batchMain;
//...
import com.min.batchMain.firstSteps.FirstStepConfigHolder;
//...
#Configuration
#EnableBatchProcessing
public class BatchConfigurer {
//....
#Autowired
private FirstStepConfigHolder firstStep;
//...
#Bean
public Step defineFirstStep() {
return stepBuilder.get("First Step")
.chunk<MyPOJO, MyPOJO>(batchSize)
.readerfirstStep.fetcher())
.writer(firstStep.extracter())
.listener(firstStep.listen())
.build();
}
//....
}
package com.mine.batchMain.firstSteps;
//...
import com.mine.batchMain.common.MyRepo;
import com.mine.batchMain.firstSteps.DocFetcher;
//...
#Configuration
#EnableJPARepositories
public class FirstStepConfigHolder {
//....
#Value("${myapp.dbUrl}")
String dbUrl;
#Value("${myapp.dbSchema}")
String dbSchema;
#Value("${myapp.dbUser}")
String dbUser;
#Value("${myapp.encDbPass}")
String encryptDbPass;
#Value("${myapp.dbDriver}")
String dbDriver;
#Value("${myapp.maxDocSize}")
String maxDocSize;
#Value("${myapp.maxNumDocs}")
String maxNumDocs;
#Bean
public DocFetcher fetcher() {
log.trace("Creating DocFetcher.")
return new DocFetcher(myDb());
}
#Bean
public MyRepo myDb() {
log.trace("Creating repo.");
MyRepo retDb = new MyRepo(myDataSource());
retDb.setMaxNumDocs(Integer.valueOf(maxNumDocs));
retDb.setMaxDocSize(Integer.valueOf(maxDocSize));
log.debug("Confirming db class state:"+retDb.toString());
return retDb;
}
private DataSource myDataSource() {
DriverManagerDataSource retDs = new DriverManagerDataSource(dbUrl, dbUser, decrypt(encryptDbPass));
retDs.setDriverClassName(dbDriver);
return retDs;
}
}
The problems/symptoms are these:
1) MyRepo is not picking up maxDocSize and maxNumDocs. (Logging shows the defaults)
2) Logging shows the trace call to "Creating DocFetcher", but not to "Creating repo.", nor is it showing the debug of the Repo state.
Which is frustrating, as according to what I know & understand, it should be picking them up. What am I missing and/or not understanding correctly?
Make sure that your BatchConfigurer.java file should be in the same package as your MainApplication.java file .
or,
Import BatchConfigurer.java in your MainApplication.java file.
Check this image for reference

Autowire working in unit test but not in main java class

I've a domain class that I want to auto-populate from external config. Here is my domain class:
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class StudioVo {
#Value("${studio.code}")
private code;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my context xml:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
<property name="studioVo" ref="StudioVo" />
</bean>
<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />
</bean>
Here is the class where I want to use the vo:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
private StudioVo studioVo;
public List<Studio> read() throws Exception {
System.out.println("getCode: " + studioVo.getCode()); //code is null here
return null;
}
}
However when I run it via unit test by autowiring, it runs fine. Like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class StudioTest {
#Autowired
private StudioVo studioVo;
#Test
public void testAutoPopulationOfStudio(){
System.out.println("getCode: "+ studioVo.getCode()); // works!
// Assert.assertTrue(studioVo.getCode().equals("102"));
}
}
Not sure what's going on here - I'm working with an old Spring Batch application wrapped in Spring Boot (so there is a mix of XML based and Java based config - and may be that is the cause of this issue). What am I missing?
In your StudioTest, you are autowiring StudioReader where as you missed the #Autowired in your StudioReader code, so add it as shown below:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
#Autowired //add this so that studioVo can be injected
private StudioVo studioVo;
//add other code
}
Please be certain to note that using #Autowire requires a chain of Spring-managed beans below it from wherever you are using it including the class in which you are using #Autowire. That is because Spring needs the precedent references to match up the object-reference hierarchy. I.e., in business logic layer ClassA, you want to #Autowire a field. ClassA itself needs to be a managed bean. Further, if the field you want to #Autowire holds an object that has referential dependencies to other objects (and most do), these also must be Spring-managed.
For example, the following will work:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MessageRunner {
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
setterMessage.setMessage("Finally it works.");
p(setterMessage.getMessage());
}
private static void p(String s) {
System.out.println(s);
}
}
DemoConfiguration.java looks like this:
package com.example.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.example.demo")
public class DemoConfiguration {
}
SetterMessage.java, this:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage {
private String message = null;
#Autowired
private SetterMessage2 setterMessage2;
public String getMessage(){
return message+setterMessage2.getSubMessage();
}
public void setMessage(String message) {
this.message = message;
setterMessage2.setSubMessage("("+message+")");
}
}
SetterMessage2.java:
package com.example.demo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage2 {
private String subMsg = "";
public void setSubMessage(String msg) {
subMsg = msg;
}
public String getSubMessage() {
return subMsg;
}
}
Note that SetterMessage2.java is annotated as a Component (#Service) but no field in it is autowired. That is because it's the end of the object reference chain. But because it is a Component, it can be autowired into SetterMessage.java. However look at MessageRunner.java's main() method and field declarations. Note that the class field SetterMessage is NOT autowired. If it were annotated as #Autowired, main() would fail at runtime, throwing an NPE with the reference to setterMessage in main(). This is because MessageRunner.java is not marked as some kind of component. So we need to grab a valid instance of MessageSetter from the application context and use it.
To emphasize, the following version of MessageRunner.java's main() method WILL FAIL, throwing an NPE, if MessageRunner.java looked like this:
...
public class MessageRunner {
#Autowired // <-- This will not do the job for us
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
p(setterMessage.getMessage());
}
...
This is a real gotchya for people new to Spring. In fact, I'd place it among the Top Five Spring Newbie Discouragers and a really evil, pernicious detail that has caused new Spring programmers countless hours in aggravation and Google searches. So I do hope that noting this phenom here will save at least some newbies time and high blood pressure spikes.
Note: If you go to create the above classes in your IDE, bear in mind these were developed with Spring Boot enabled.

Spring annotations - #Configuration to invoke spring bean auto-building

If I declare a class using #Bean and then component scan for the class, spring will instantiate the class by invoking it's constructor and injecting constructor args and injecting any fields marked with #Inject. For simplicity's sake, lets call this spring auto-building.
I dislike component scan and wish to avoid it completely (I don't wish to discuss my reasons for not liking it). I would like to use a #Configuration object instead but would still like to have the auto-building functionality available to me. Is it possible to invoke spring to auto-build my objects instead of explicitly having to pass all the constructor arguments in my #Configuration object?
Lets assume that I have a bean:
public class MyServiceImpl implements MyService {
public MyServiceImpl(Dependency1 d1, Dependency d2) { ... }
....
}
I could define a configuration object like this:
#Configuration
public class MyConfiguration {
// lets assume d1 and d2 are defined in another #Configuration
#Inject
Dependency1 d1;
#Inject
Dependency2 d2;
#Bean
public MyService myService() {
// I dislike how I have to explicitly call the constructor here
return new MyServiceImpl(d1, d2);
}
}
But now, I have explicitly had to call the MyServiceImpl constructor myself so I will have to keep updating this as my constructor changes over time.
I was hoping that I could declare an abstract method so that spring auto-building could take place:
#Configuration
public abstract class MyConfiguration {
#Bean
public abstract MyServiceImpl myService();
}
But this doesn't work. Is there a way that I can invoke spring auto-building without using a component scan?
In Google Guice, this can be done via the Binder:
https://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/Binder.html
In Tapestry IOC, this can be done via the ServiceBinder:
http://tapestry.apache.org/ioc-cookbook-basic-services-and-injection.html#IoCCookbook-BasicServicesandInjection-SimpleServices
Update
Based on spod's answer, I was able to achieve what I was after (thanks!). Test case included for anyone that wants to do the same:
import java.util.Date;
import javax.inject.Inject;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class AutoBuildConfigurationTest {
#Configuration
public static class MyConfiguration {
#Inject
private AutowireCapableBeanFactory beanFactory;
#Bean
public Date date() {
return new Date(12345);
}
#Bean
public MyService myService() {
return autoBuild(MyService.class);
}
protected <T> T autoBuild(Class<T> type) {
return type.cast(beanFactory.createBean(type, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true));
}
}
public static class MyService {
private Date date;
public MyService(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
#Test
public void testAutoBuild() {
ApplicationContext appContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyService myService = appContext.getBean(MyService.class);
Assert.assertEquals(12345, myService.getDate().getTime());
}
}
The java based container configuration doesnt depend on doing a component scan in any way. Its merely a different approach for the XML based component configuration. With the XML configuration you'd just have to declare your bean with the MyServiceImpl class in case its already #inject annotated. Spring would recognize the annotations and take care of them. If you really want to instanciate MyServiceImpl from a #Configuration java class without calling the constructor yourself, then you'd have to make use of the bean factory (havent tested it, just give it a try):
#Configuration
public class MyConfiguration {
#Autowired AutowireCapableBeanFactory beanFactory;
#Bean public MyService myService() {
return beanFactory.createBean(MyServiceImpl.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
}
}

Resources