Is there a way to prevent bean overriding with Spring Boot? - java

With Spring's AbstractRefreshableApplicationContext, I am able to force Spring to fail if there is a conflict in Bean IDs or circular references by setting a couple of flags and refreshing the context like so:
AbstractRefreshableApplicationContext refreshableContext;
...
refreshableContext.setAllowBeanDefinitionOverriding(false);
refreshableContext.setAllowCircularReferences(false);
refreshableContext.refresh();
However, Spring Boot returns a ConfigurableApplicationContext which is not an instance of AbstractRefreshableApplicationContext and does not appear to have any means to prevent bean definition overriding or circular references.
Does anyone know of a way and have an example of how to prevent these types of conflicts?
For context, this is for a large project that has a mix of annotated and xml defined beans. The version of Spring Boot used is 1.3.1.RELEASE. There have been some cases where folks added duplicate bean definitions in the xml, but the application started up fine and wasn't immediately apparent the original bean was overridden until run-time issues started occurring.
The goal here is to prevent the application from event starting up if such a conflict occurs. From various forums I know Spring IDE can detect these, but the desire is to enforce this in the CI build which is a stronger safety net.
After some searching, I can't find any support for this in the context that Sprint Boot returns. If this can't be done through the context, is there a different solution available?
Thanks in advance.

You may use an initializer when building your Spring Boot app:
#SpringBootApplication
public class SpringBootApp {
public static void main(String... args) {
new SpringApplicationBuilder(SpringBootApp.class)
.initializers(new ApplicationContextInitializer<GenericApplicationContext>() {
#Override
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.setAllowBeanDefinitionOverriding(false);
}
})
.run(args);
}
}
Or with java 8:
new SpringApplicationBuilder(SpringBootApp.class)
.initializers((GenericApplicationContext c) -> c.setAllowBeanDefinitionOverriding(false) )
.run(args);

add below into your application.yml
spring.main.allow-bean-definition-overriding: false

Related

How to inject extra property sources in SPRING before other beans are created?

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.

Spring Boot Camel - Autowiring issues in Camel components

I am using Spring Boot 1.5.7 and Apache Camel 2.19.3, using Spring Boot AutoConfiguration provided by spring-boot-starter-camel
It is pretty basic Spring Boot and Camel initialized as in their tutorial, so we have a RouteBuilder component that does exactly that.
#Component
public class CIFRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
// build routes
}
}
We have a Configuration that defines some beans we need in our application
#Configuration
public class MyConfiguration {
#Bean
public void Map<String, Object> map() {
return new HashMap<>()
}
}
Finally, we have a custom InflightRepository implementation that should be scanned by auto-configuration and added to the CamelContext which basically works, but for some reason, the component doesn't get initialized properly. Means, its dependencies are not initialized but the bean is instantiated and injected in my Application.
#Component
public class MyCustomInflightRepository extends DefaultInflightRepository {
#Autowired
private Map<String, Object> map;
#Override
public void add(Exchange exchange) {
super.addExchange(exchange);
// ....
}
}
The problem now is that map remains (null), I also tried adding a #PostConstruct initializer method but it doesn't get called.
As far as I was able to reconstruct, it seems to be connected to premature in CamelAutoConfiguration where the CamelContext bean gets instantiated (done in private method afterPropertiesSet.
InflightRepository inflightRepository = getSingleBeanOfType(applicationContext, InflightRepository.class);
if (inflightRepository != null) {
LOG.info("Using custom InflightRepository: {}", inflightRepository);
camelContext.setInflightRepository(inflightRepository);
}
If MyCustomInflightRepository doesn't implement InflightRepository, the bean is initialized correctly, but indeed not recognized by Camel. When disabling auto-configuration, the bean's dependencies are injected.
So, either I'm doing the impossible by Spring standards or there's something fishy with the Camel component for Spring.
I'm a bit quick on resolving this (I wanted to post this two days ago already^^), but a colleague figured out what could be the problem.
When using CamelAutoConfiguration the InflightRepository bean (or practicially everything for which Camel tries to get a matching bean here), the context is accessed before property resolvers are fully initialized which leads to the bean being initialized (and cached in context) before any auto-wired properties can be resolved.
I'm not a Spring expert but this behavior is a bit problematic in my opinion because uninitialized beans are pulled into the CamelContext when you rely on Spring DI for your custom components.
To be sure, I'll raise this with the maintainers...
By the way, my simple solution was to manually setting the in-flight repository in context configuration (as suggested)
#Bean
public CamelContextConfiguration camelConfig() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
context.setInflightRepository(new MyCustomInflightRepository(/* Dependencies go here */ ));
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
}
};
}
Also it seems to be an issue when use camel-http-starter in your project which isn't recommended, they claim it is deprecated.
So, either don't do DI (regardless if via property or constructor injection) for your camel-managed beans or skip that starter.
The problem is that a Map<String,Object> is too vague for Spring to be able to understand what you want; I think the default behavior is that it'll give you all beans keyed by name.
Instead, be more specific, or possibly provide the necessary parameters as constructor arguments and configure them explicitly in an #Bean method (it's a good idea to always use constructor injection anyway).

#SpringBean support for bean in child ApplicationContext

I'm working with the developer of PF4J(Plugin Framework for Java) to provide better plugin functionality for Wicket. There is already a pf4j-spring and a pf4j-wicket project to provide some basic integration. In order to allow the #SpringBean or #Inject annotations to have access to plugin beans in a child context we need to be able to lookup the ApplicationContext associated with a specific class.
So for example, say I have a MyService bean in a child(plugin) ApplicationContext and that plugin also provides a panel that needs that via a #SpringBean annotation. Spring doesn't allow the parent ApplicationContext to see beans in a child context and for good reason. So we would get an exception saying that bean could not be found since #SpringBean only looks up beans in the parent context. We have code that we have developed that look up the child context like so:
SpringPlugin plugin = (SpringPlugin)PluginManager.whichPlugin(MyService.class);
ApplicationContext pluginContext = plugin.getApplicationContext();
How could I modify or provide this functionality in a custom version of SpringComponentInjector? It uses a ISpringContextLocator but that context locator does not specify the class for which it needs the ApplicationContext.
Any ideas on how this could be achieved?
Thanks for your help!
I'm afraid current SpringComponentInjector is not prepared for such usage. You will have to create your own version.
The problem that I see is that you will have to have either as many IComponentInstantiationListeners as plugins there are. Or create a composite ICIL that delegates to SpringBeanLocators for each plugin. I think the composite would be better. Then you'll have to make sure that a Panel in pluginA cannot use a bean located by SpringBeanLocatorB.
If you manage to do it and you find something in wicket-spring that could be made more generic to help make your version simpler then please let us know and we will consider your suggestion(s)!
Take a look at sbp. It is built on top of pf4j to support Spring Boot, and also provides mechanism of sharing beans between main application and plugins. It looks like:
#Override
protected SpringBootstrap createSpringBootstrap() {
return new SharedDataSourceSpringBootstrap(this, MdSimplePluginStarter.class)
.addSharedBeanName("objectMapper")
.addSharedBeanName("cacheService");
}

What are the ways to get ApplicationContext object in Spring?

Hi i want to know what are the different ways to get ApplicationContext Object in Spring?
I know only one way that is,
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
is there any other ways? if it is please let me know.
Thanks.
You can also use annotation based configuration
#Configuration
public class Config {
#Bean
public Bean1 bean1() {
return new Bean1();
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
}
}
You can implement the interface ApplicationContextAware, like this :
public class MyClass implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
If you are using annotation, you could also autowire it
#Autowired
private ApplicationContext applicationContext;
Also, the code you wrote does not get an existing application context, it creates one.
Well, there are a lot of ways out there, I wonder whom would know them all...
But first, we need to make a difference between instanting a new context, or getting a running and existing application-context.
By new ***ApplicationContext a new context will be created. Therefore all Subclasses of org.springframework.context.ApplicationContext can be used to create a new ApplicationContext. You can find all implementing classes here. The new way to instantiate a spring-context is through AnnotationConfigApplicationContext.
Also, you can add a displatcher-servlet or an servlet-listener in your web.xml. Or use a framework like gemini-blueprint in an osgi-environment which starts all xml-files in meta-inf/spring. (e.g. eclipse virgo)
On the other hand, you can get an existing context (which means not a new one) through different ways:
ApplicationContextAware
Implement the ApplicationContextAware interface and you will get the context via setApplicationContext(ApplicationContext applicationContext) method.
Just add #Autowired private ApplicationContext applicationContext; to your spring bean. But make sure it is a spring bean.
In your web-application, you can get the context of your listener-context via ApplicationContextUtils.getWebApplicationContext( servletcontext)
There would a lot of more ways, but these are those which popped up in my mind quickly.
If you are referring to the possible way you can create an ApplicationContext and not to the ways such an instance can be passed through your code then I suggest taking a look at the Spring javadoc for ApplicationContext. So based on this the concrete implementations of this interface are:
org.springframework.context.annotation.AnnotationConfigApplicationContext
Standalone application context, accepting annotated classes as input - in particular #Configuration-annotated classes, but also plain #Component types and JSR-330 compliant classes using javax.inject annotations. Allows for registering classes one by one using register(Class...) as well as for classpath scanning using scan(String...).
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
This is essentially the equivalent of AnnotationConfigApplicationContext for a web environment.
org.springframework.context.support.ClassPathXmlApplicationContext
Standalone XML application context, taking the context definition files from the class path, interpreting plain paths as class path resource names that include the package path (e.g. "mypackage/myresource.txt"). Useful for test harnesses as well as for application contexts embedded within JARs.
org.springframework.context.support.FileSystemXmlApplicationContext
Standalone XML application context, taking the context definition files from the file system or from URLs, interpreting plain paths as relative file system locations (e.g. "mydir/myfile.txt"). Useful for test harnesses as well as for standalone environments.
org.springframework.context.support.GenericApplicationContext
Generic ApplicationContext implementation that [...] does not assume a specific bean definition format
org.springframework.context.support.GenericXmlApplicationContext
Convenient application context with built-in XML support. This is a flexible alternative to ClassPathXmlApplicationContext and FileSystemXmlApplicationContext, to be configured via setters, with an eventual AbstractApplicationContext.refresh() call activating the context.
org.springframework.context.support.GenericGroovyApplicationContext
An ApplicationContext implementation that extends GenericApplicationContext. [...] Consider this as the equivalent of GenericXmlApplicationContext for Groovy bean definitions, or even an upgrade thereof since it seamlessly understands XML bean definition files as well.
org.springframework.web.context.support.GenericWebApplicationContext
Subclass of GenericApplicationContext, suitable for web environments.
org.springframework.web.context.support.GroovyWebApplicationContext
WebApplicationContext implementation which takes its configuration from Groovy bean definition scripts and/or XML files, as understood by an GroovyBeanDefinitionReader. This is essentially the equivalent of GenericGroovyApplicationContext for a web environment.
org.springframework.jca.context.ResourceAdapterApplicationContext
ApplicationContext implementation for a JCA ResourceAdapter. Needs to be initialized with the JCA BootstrapContext, passing it on to Spring-managed beans that implement BootstrapContextAware.
org.springframework.context.support.StaticApplicationContext
ApplicationContext implementation which supports programmatic registration of beans and messages, rather than reading bean definitions from external configuration sources. Mainly useful for testing.
org.springframework.web.portlet.context.StaticPortletApplicationContext
Static Portlet-based ApplicationContext implementation for testing. Not intended for use in production applications.
org.springframework.web.context.support.StaticWebApplicationContext
Static WebApplicationContext implementation for testing. Not intended for use in production applications.
org.springframework.web.portlet.context.XmlPortletApplicationContext
Portlet-based WebApplicationContext implementation which takes its configuration from XML documents, understood by an XmlBeanDefinitionReader.
org.springframework.web.context.support.XmlWebApplicationContext
WebApplicationContext implementation which takes its configuration from XML documents, understood by an XmlBeanDefinitionReader. This is essentially the equivalent of GenericXmlApplicationContext for a web environment.

Spring circular reference example

I have a circular reference in one of my projects at work using spring, which I am unable to fix, and fails with the following error at startup:
'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference?
I tried to recreate the same problem at a smaller level in a sample project (without all the details of my work project). I have however been unable to come up with a plausible scenario where spring fails with an error.
Here's what I have:
public class ClassA {
#Autowired
ClassB classB;
}
public class ClassB {
#Autowired
ClassC classC;
}
#Component
public class ClassC {
#Autowired
ClassA classA;
}
#Configuration
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
I have a similar scenario in my project, which fails, and I was expecting spring to complain in my sample project as well. But it works fine! Can someone give me a simple example of how to break spring with the circular reference error?
Edit: I fixed the issue using javax.inject.Provider. The only other difference in the 2 projects was the annotations used were javax.inject.Inject and javax.annotation.ManagedBean in place of #Autowired and #Component.
You could use #Lazy to indicate that the bean is lazily created, breaking the eager cycle of autowiring.
The idea is that some bean on the cycle could be instantiated as a proxy, and just at the moment it is really needed it will be initialized. This means, all beans are initialized except the one that is a proxy. Using it for the first time will trigger the configuration and as the other beans are already configured it will not be a problem.
From one Issue in Spring-Jira:
#Lazy annotation that can be used in conjunction with #Configuration
to indicate that all beans within that configuration class should be
lazily initialized. Of course, #Lazy may also be used in conjunction
with individual #Bean methods to indicate lazy initialization on a
one-by-one basis.
https://jira.springsource.org/browse/SJC-263
Meaning that annotating your bean as #Lazy would be enough. Or if you prefer just annotate the configuration class as #Lazy as follows:
#Configuration
#Lazy
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
If you implement an interface of your beans this will work quite well.
This is an old thread, so I guess you almost forgot about the issue, but I want to let you know about the mystery. I encountered the same problem, and mine didn't go away magically, so I had to resolve the problem. I'll solve your questions step by step.
1. Why you couldn't reproduce the circular reference exception?
Because Spring takes care of it. It creates beans and injects them as required.
2. Then why does your project produce the exception?
As #sperumal said, Spring may produce circular exception if you use constructor injection
According to the log, you use Spring Security in your project
In the Spring Security config, they do use constructor injection
Your beans which injects the authenticationManager had the circular reference
3. Then why has the exception gone away mystically?
The exception may or may not occur depends on the creation order of beans. I guess you made several *context.xml files or so, and load them with config something like below in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:*-context.xml</param-value>
</context-param>
The xml files will be loaded by XmlWebApplicationContext class and the loading order of files are not guaranteed. It just loads files from the file system. The problem is here. There's no problem if the class loads the application context file first, because your beans are already created when they are used for the construction injection of Spring Security. But, if it loads the Spring Security context file first, the circular reference problem occurs, because Spring tries to use your beans in the constructor injection before they had been created.
4. How to solve the problem?
Force the loading order of the xml files. In my case, I loaded the security context xml file at the end of the application context file by using <import resource="">. The loading order can be changed depends on environments even with the same code, so I recommend setting the order to remove potential problems.
According to Spring documentation, it is possible to get Circular dependency issue or BeanCurrentlyInCreationException by using constructor injection.
The solution to fix the issue is to use setters instead of Constructor injection.
Reference http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html.

Categories