Team, I am working on an Spring boot application which uses lot of other third party libraries.
All those libraries are built on top of spring core.
The common issue that usually comes is DuplicateBeanException.
Let's say if the bean-name is same from different libraries, spring throws error while trying to start the server. As I do not have control over third party jars. I have to rename the beans in my application: below is the code snippet I wrote to handle this scenario. But this is not an efficient solution as in future there can be again some duplicate beans. How can I solve it in a generic way so that when ever any duplicate beans come, my application can handle and initialize those beans.
My code to handle duplicate bean :
#Configuration
public class ExternalBeanConfiguration {
#Bean(employeeLib1)
public Employee getEmployee() {
return new Employee();
}
#Bean(employeeLib2)
public com.another.library.Employee getEmployee() {
return new com.another.library.Employee();
}
}
When you configure where to scan the beans from the external libraries by #ComponentScan , you can specify a BeanNameGenerator for how the name of the detected beans are defined.
#Configuration
#ComponentScan(basePackages = { "com.foo.lib1" , "com.foo.lib2"}, nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class ExternalBeanConfiguration {
}
The FullyQualifiedAnnotationBeanNameGenerator is exactly for solving your problem which is mentioned in the javadoc as :
Favor this bean naming strategy over {#code
AnnotationBeanNameGenerator} if you run into naming conflicts due to
multiple autodetected components having the same non-qualified class
name (i.e., classes with identical names but residing in different
packages)
It will name the bean as the fully qualified class name as the default bean name such that even if different packages has a same class name , it will still have different bean name.
Related
I have multiple modules (application + library) in a project and they contain service classes of same names (but different packages) that are to be injected thru Lombok generated constructors within their respective modules.
When I start the app module, the Spring's ClassPathBeanDefinitionScanner however picks both and raises BeanDefinitionStoreException because it detects both as a candidates, due to the same bean name and doesn't care about the fully qualified type name.
Is there an option to enable more precise resolution of the beans that also requires a type match?
Isn't this a design flaw? What am I missing?
Edit: Names, I believe, are taken from constructors generated by Lombok's #RequiredArgsConstructor, and service classes are also generated code (MapStruct mappers).
You can use #Component("beanName"), #Bean("beanName"), #Named("beanName") to assign a custom name to a bean. By default, spring uses the class name as the bean name. For Example, you can define bean name of your CustomAccountsApiController class as below:
#RestController("customAccountsApiControllerPrimary")
public class CustomAccountsApiController {
}
You can read more on bean name from here
I have below working groovy code from Spring framework project:
import org.springframework.oxm.Unmarshaller
public class ItemSearchService {
Unmarshaller unmarshaller;
public ItemSearchResponse getObject(InputStream xml) {
ItemSearchResponse its = null;
try {
its = (ItemSearchResponse) unmarshaller.unmarshal(new StreamSource(xml));
} finally {
}
return its;
}
}
Unmarshaller.unmarshall is actually interface method:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/oxm/Unmarshaller.html
Unmarshaller interface is implemented by several classes.
Who decides which implementing class to inject during runtime and how it decides which class to use?
Does Spring IoC mechanism does this? If so does it pick up one specific implementing class during build time when jar is generated or it does it during run time?
Also how to know which implementing class it actually used?
Will above code work outside of Spring in ordinary Java file assuming dependent jars in classpath?
As soon as it goes about a Grails project (not a pure Spring one), some things should be clarified.
Who decides which implementing class to inject during runtime and how it decides which class to use? Does Spring IoC mechanism does this? If so does it pick up one specific implementing class during build time when jar is generated or it does it during run time?
What Spring does in a plain Spring(Boot) is nicely described by #svr s answer and other documentation on the Internet, but has not much to do with your case.
In Grails the convention-over-configuration doctrine is used, meaning that
by default Bean Autowiring is active
only beans which are declared as Grails artefacts, like Service are auto-injected.
Usually, if you want to autowire a bean of other stereotype, you have to declare it somewhere, otherwise Spring IoC container inside Grails simply won't find it.
You shall check your grails-app/conf/spring folder for resources.groovy (or maybe yaml or xml files depending on version ) and see if your unmarshaller bean is defined there. It should look like:
beans = {
unmarshaller Unmarshaller ....
}
So, basically it doesn't matter how many implementations of Unmarshaller interface are present in all project's jar files. The only thing what matters is what defined in your resources.groovy.
If you want to check the class of the bean in runtime, just log it's class in your service:
class ItemSearchService {
Unmarshaller unmarshaller
ItemSearchResponse getObject(InputStream xml) {
log.info "unmarshaller's class is $unmarshaller.class"
// or
// println "unmarshaller's class is $unmarshaller.class"
}
}
Who decides which implementing class to inject during runtime and how
it decides which class to use?
Does Spring IoC mechanism does this? If so does it pick up one
specific implementing class during build time when jar is generated or
it does it during run time?
Yes Spring IOC container. It happens at run time when the application is run.
Also how to know which implementing class it actually used?
For most part you will have to define bean with implementation to pick. For other cases you can look at the spring auto configuration classes.
Will above code work outside of Spring in ordinary Java file assuming
dependent jars in classpath?
Not sure what you mean outside of Spring but as long as application is packaged correctly it should all work.
Quick Overview
Spring IOC will only inject a bean when found in the application context. An application context loosely is repository of beans.
Classpath Scanning and Registration
Spring scans application to pick up all the stereotyped classes and registers bean definition with application context. Stereotyped annotations include #Component, #Repository, #Service, #Controller, #Configuration. More
Dependency Injection
Spring DI creates the bean and injects them as dependencies into application. You can create dependencies between different application components using the registered beans and spring will autowire these for you. More. There are two types of DI - Constructor based and setter based - Constructor for required dependencies and setter based for optional dependencies. More
There are sensible defaults provided when you use any spring classes. You just have to define the bean where there is no default or you would like to pick different implementation. Application context can be further be enriched by using Spring Boot Auto Configuration where all related classes are wired together to form a coherent entry classes. You can then easily inject these into application for getting started. More
Example
You can define bean in the #Configuration class for a web service module.
#Configuration
public class WSConfig {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marsharller = new Jaxb2Marshaller();
marsharller.setContextPath("some package");
}
#Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marsharller) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marsharller);
}
}
#RequiredArgsConstructur
public class ItemSearchService {
private final Unmarshaller unmarshaller;
public ItemSearchResponse getObject(InputStream xml) {
ItemSearchResponse its = null;
try {
its = (ItemSearchResponse) unmarshaller.unmarshal(new StreamSource(xml));
} finally {}
return its;
}
}
This similar configuration is automatically taken care by WebServicesAutoConfiguration with ways to customize when using Spring Boot.
I am writing a library which will be used by spring-boot projects. I'd like to inject into the boot projects' SpringEnvironment a property source that I take from the Internet.
I tried the following
#Configuration
public class MyCustomConfiguration {
#Bean
BeanDefinedAbove above() { /* do some work */ }
#Bean
#ConditionalOnBean(BeanDefinedAbove.class)
SmartInitializingSingleton propertySourceSetting(ConfigurableEnvironment env, BeanDefinedAbove bean) {
return () -> {
PropertySource source = bean.getPropertySourceDownloadedFromTheInternet();
env.getPropertySources().addFirst(source);
}
}
}
In my clients' projects when I debug this code what happens is either one of the two:
above() is called
user's #Service or #Controller are called
propertySourceSetting(...) is called
OR
user's #Service or #Controller are called
above() is called
propertySourceSetting(...) is called
Depending whether or not my client's depend on my BeanDefinedAbove bean, which is normal as the #Service is depdent on the bean created in above().
I have also added the FQDN of my class to the EnableAutoConfiguration in the META-INF/spring.factories.
So how to ensure that the logic in propertySourceSetting(..) is called before users' #Service and #Controller
I'll provide you with three options.
Option 1: (THIS IS A BAD APPROACH, BUT A QUICK WORKAROUND)
Add #Lazy(false) annotation to both Beans. Spring will eagerly create those two beans, which they will probably be created before the other ones.
Why this is bad?
This does not ensure order. Spring decides the creation order based on dependencies and some other conditions. This is why it will "probably" work :)
Option 2: Create a main class to bootstrap Spring Boot Initialization (the old way of starting spring boot).
public static void main(String[] args) throws Exception {
SpringApplication application = new SpringApplication(MyApplication.class);
// ... add property source here before start
application.run(args)
}
You also need to specify main class in the manifest for Spring Boot like this: https://www.baeldung.com/spring-boot-main-class
In that main-class you would add your propertysource, kinda like this:
SomeClassThatRetrievesProperties propRetriever = new SomeClassThatRetrievesProperties ();
Map<String,String> properties = propRetriever.getAllPropertiesAsMap();
application.setDefaultProperties(properties);
Option 3: Create a CustomApplicationContext by extending WebApplicationContext and overriding getSpecificConfigurations() method.
This way you will have full control but we aware that you could break some important stuff.
I have an application which has a very simple plugin architecture by the means of registering additional behaviour using spring classpath scanning ("Installing" the plugin happens by putting the plugin.jar on the classpath).
This works great for registering additional beans and it also works great for registering hooks like this:
// core.jar:
#Component
class CoreClass {
public void addListener(Listener listener) { /* ... */ }
}
// plugin.jar
#Component
class Plugin {
public Plugin(CoreClass coreClass) {
coreClass.addListener(new PluginListener());
}
}
However sometimes it is more appropriate to to replace an entire bean. This might look like this:
// core.jar:
#Component("someBean")
class CoreClass implements CoreInterface {
#Override
public void doStuff();
}
// plugin.jar
#Component("someBean")
#Order(HIGHEST_PRECEDENCE)
class Plugin implements CoreInterface {
#Override
public void doStuff();
}
In this case I want that both Plugin and CoreClass are discovered by classpath scan during startup, but then the CoreClass should be ignored, because the bean someBean has another definition with higher precedence.
I am aware that I could do this using XML (<import resource="classpath*:plugin-spring.xml" />), because XML allows overriding definitions. But is this also possible using annotations?
Edit: Note that I sometimes have multiple bean instances of the same type (with different properties) which I inject name-based. Thus I actually need to override the bean with a certain name.
Possible solution to use:
#Primary
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
This annotation is semantically equivalent to the element's primary attribute in Spring XML.
May be used on any class directly or indirectly annotated with #Component or on methods annotated with #Bean.
// core.jar:
#Component
class CoreClass implements CoreInterface {
#Override
public void doStuff();
}
// plugin.jar
#Component
#Primary
class Plugin implements CoreInterface {
#Override
public void doStuff();
}
http://memorynotfound.com/handling-multiple-autowire-candidates-with-spring-primary/
Or you could use :
#Configuration
#ImportResource( { "classpath*:core-spring.xml", "classpath*:plugin-spring.xml" } )
public class ConfigClass { }
I suspect that problem with annotations is not clear which order to override, if you define over notations same name you will end-up with ConflictingBeanDefinitionException spring will not allow you to have #Component or #Bean with same name. In xml you can manage override order that’s why you can do this over xml. In annotations you can't do that.
Any given Spring context can only have one bean for any given id or name. In the case of the XML id attribute, this is enforced by the schema validation. In the case of the name attribute, this is enforced by Spring's logic.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behavior depends on the ordering of the files when they get loaded by the context.
So while it's possible, it's not recommended. It's error-prone and fragile, and you'll get no help from Spring if you change the ID of one but not the other.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behavior depends on the ordering of the files when they get loaded by the context.
I have a Dynamic Web Project created in Eclipse (Juno) using Spring/Maven/Weblogic. I also have an Abstract class into which I inject a property (on the setter) via a properties file. The annotation used is #Value(value="${some.property}").
For some strange reason, when I deploy this project via maven to weblogic, the property gets injected for the concrete class that etends this Abstract class. But when I deploy this project directly onto weblogic via the Server -> Add Deployment, it fails to inject the property. In fact it does inject properties for all other annotations on the concrete class but ignores any annotations for the abstract class.
So basically this has nothing to do with coding but seems like some kind of config problem. Has anyone encountered something similar. Thanks.
public abstract class MyAbstract {
#Value(value="${myproperty1}")
public void setMyValue(String myValue) {
log.debug("setMyValue({})", myValue);
this.myValue = myValue;
}
}
public class MyConcrete {
#Value(value="${myproperty2}")
public void setMyValue2(String myValue2) {
log.debug("setMyValue2({})", myValue2);
this.myValue2 = myValue2;
}
}
I found the issue. The thing is that this particular bean has #Scheduled annotation and hence does not qualify for Bean Post Processing. Since the properties via #Value are set during this phase, they are skipped. Instead I shifted the bean creation into my application context xml file and set the properties from there eg.
<bean id="myConcrete" class="some.package.MyConcrete"
p:value="${myproperty1}"
/>
Now the properties are getting injected during normal bean lifecycle i.e. after Instantiation but before Bean Post Processor.