Automatically configure beans in root and dispatcher application context with single annotation - java

Let's say I've create a annotation called #EnableFeauture which imports a bean configuration class EnableFeatureConfiguration. This annotation is typically placed on top of the dispatcher configuration. Must beans like view resolvers etc. belong to that dispatcher config but a few beans really belong to the root context.
How can I define those beans without the need for another annotation? My first thought was to autowire the WebApplicationContext and call context.getParentBeanFactory() to register beans but I'm not sure if this is the best way to achieve my goal. How is this typically done?
UPDATE
To clarify the problem a bit. I'm working on a project to integrate a template engine with Spring MVC. The project consists of the following categories / parts:
Configuration
Annotation e.g. EnableFeature (imports configuration)
View
ViewResolver
Template factory
Logically all class categories could exist in the web application context. However, the template factory could be used by other services as well (e-mail, etc.). Services that mostly exist in the root context. So what I'm basically asking is, how can I make the factory available to the root context in a clean way. I would like the configuration required to be as low as possible. As of now the setup only requires one annotation placed on top of the dispatcher configuration class.

It took me some time to clearly understand what you want to do and the implications beyond. Finding the root application context from the servlet one would be the easy part, context.getParentBeanFactory(), or directly context.getParent() gives it immediately in any ApplicationContextAware class, or through direct injection of the ApplicationContext.
The hard part, is that at the time of initialization of the servlet application context, the root application context has already been refreshed. If I look at what happens in Tomcat :
at deploy time the root application context is fully initialized and refreshed
next, at first request for the DispatcherServlet the child context is initialized with root context as parent.
That mean that when servlet context is initialized, it is way too late to inject beans in root context : all singleton beans have already been created.
There may be workaround, all with their own flaws :
register a new Configuration class in parent context and do a new refresh().IMHO, this would be the least bad solution as normally WebApplicationContextes support multiple refresh. The potentials problems are :
all other beans must be initialized once without the new beans : the configuration must be tolerant to non existing beans
other components (filters, security, DAO, etc.) may already be running, and they have to be thoroughly tested against a hot context refresh
create an intermediate ApplicationContext with current root as parent containing beans that should not go into servlet application context, and then create the servlet application context with this intermediate context as parent.
no problem for the root application context that is not even aware of the whole operation
but no bean of root context can be injected with any new bean
register all new beans directly in root context. Ok, all is fine for root initialization, and beans of servlet context will have access to all beans. But if one new bean need to be injected with a bean from servlet context, you will have to to it manually at servlet context initialization, with careful tests (or prayers) that it cannot be used before that ... and you will have some pollution of root context with beans only relevant for the servlet
use only only root context and an empty servlet context.
ok, each bean has access to any other one
but it breaks the separation between root and servlet context and adds some pollution to the root context
My conclusion is that having a single piece of configuration for 2 different application contextes is a little against Spring philosophy and I would advice you to keep with 2 separate configuration classes one for the root context and one for the servlet context. But if you wish, I can elaborate on the refreshing of root context from servlet context (1st solution).
If you want to inject beans into root context from a feature that would be declared in servlet context to have a single configuration point, you can use something like:
#Configuration
public class FeatureConfig implements ApplicationContextAware {
static boolean needInit = true;
#Override
// Register the configuration class into parent context and refreshes all
public void setApplicationContext(ApplicationContext ac) throws BeansException {
AnnotationConfigWebApplicationContext parent =
(AnnotationConfigWebApplicationContext) ((AnnotationConfigWebApplicationContext) ac).getParent();
if (needInit) { // ensure only one refresh
needInit = false;
parent.register(RootConfig.class);
parent.refresh();
((AnnotationConfigWebApplicationContext) ac).refresh();
}
}
#Configuration
#Conditional(NoParentContext.class)
// Can only be registered in root context
public static class RootConfig {
// configuration to be injected in root context ...
}
// special condition that the context is root
public static class NoParentContext implements Condition {
#Override
public boolean matches(ConditionContext cc, AnnotatedTypeMetadata atm) {
logger.debug(" {} parent {}", cc.getBeanFactory(), cc.getBeanFactory().getParentBeanFactory());
return (cc.getBeanFactory().getParentBeanFactory() == null);
}
}
// other beans or configuration that normally goes in servlet context
}
With such a #Configuration class it is enough to have a #import(FeatureConfig.class) annotation in a configuration class for the application context of the DispatcherServlet.
But I could not find any way to allow the configuration to happen before the normal servlet application context initialisation. An outcome is that any bean from the special configuration can only be injected in root context with a #Autowired(required=false), because the root context will be refreshed twice, first time without the special configuration class, second time with it.

As I understand all you have to do is to provide custom configuration that will be imported by annotating #Configuration class by #EnableFeature. Then you just have to include custom beans in your EnableFeatureConfiguration class.
#Configuration
public class EnableFeatureConfiguration {
#Bean
public MyBean myBean() {
return MyBean();
}
}
Then your EnableFeature looks like:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(EnableFeatureConfiguration.class)
public #interface EnableFeature {
}
And that's all. In project you have to use:
#Configuration
#EnableFeature
public class MySpringConfig {
}

Related

How to manually configure/create the ContextLoaderListener in Spring boot Web app?

Hope I am making sense... my understanding is that there are DispatcherServlet and ContextLoaderListener, and DispatcherServlet creates servlet application context. And ContextLoaderListener creates root application context, which belongs to the whole application (kind of a global thing). The idea is to put "web beans" in servlet context and "non-web beans" in root application context.
Now, my questions are:
is this root application context created no matter what? In other words, do I always get this root application context and servlet context, even I am not creating the root context explicitly? For example, if I use the simplest way to start the app,
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
does Spring boot framework still create the root context?
If I want to manually control/configure ContextLoaderListener, for example,
public class MyContextLoaderListener extends ContextLoaderListener { ... }
how can I accomplish that without using any web.xml configuration (use Java config only)?
Having been search on the Web, but still could not put a whole picture together.
Many thanks.

What setting causes ContextLoaderListener's (root context) to be the 'parent' context over DispatcherServlet's context?

First came into this problem by wondering how I was supposed to autowire/inject a service layer level bean from the ContextLoaderListener's application context INTO a bean from DispatcherServlet's context.
Let's say for a random simple situation, a PuppyService needs to be autowired/injected into PuppyResource on the actual resource/controller level. The Puppy Service along with the Puppy Repository and any Puppy Entities would be beans auto-loaded into the root/ContextLoaderListener's context from a #Configuration class that does a component-scan in some other package to grab the beans and load them...
At the same time, the Puppy RESOURCE would be more on the webMvc level and loaded into DispatcherServlet's context.
From what I just read, and now hopefully understand, the root context is actually the 'parent' context of the context created by DispatcherServlet. This means that beans that live inside the root context can actually be autowired/injected into any bean that lives inside the context created by DispatcherServlet. I literally just learned about this concept of 'nested' contexts. Is this accurate?
If this is accurate, then where does the configuration get set to make the root context the 'parent' context? Currently, when I configure the servlet/listener, I do it via a custom implementation of WebApplicationInitializer, wherein I simply create two contexts, load them each respectively into a DispatcherServlet instance, and a ContextLoaderListener instance, then register each of those respectively into the servlet. I'm guessing somewhere in there, the ContextLoaderListener's application context automatically gets set to the 'parent'.
Could someone briefly explain this? Thank you.
The behavior is built into the DispatcherServlet. The javadoc defines the root application context.
Only the root application context as loaded by ContextLoaderListener,
if any, will be shared.
The javadoc of ContextLoaderListener also states
Bootstrap listener to start up and shut down Spring's root WebApplicationContext.
And, assuming you use the DispatcherServlet constructor that receives a WebApplicationContext,
If the given context does not already have a parent, the root
application context will be set as the parent.
you'll get this behavior automatically.
Again from the javadoc,
This constructor is useful in Servlet 3.0+ environments where
instance-based registration of servlets is possible through the
ServletContext.addServlet(java.lang.String, java.lang.String) API.
which is what the common AbstractDispatcherServletInitializer uses to set up your Spring MVC application.

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 container implementations

I keep on learning Spring and it is very difficult to figure out which implementation of ApplicationContext is intended for. I've standalone J2EE application and I don't interested in Web* or Portlet* implementations.
Can you provide me the brief list of possibilities (if isn't clear, see P.S. section of my question) and purposes of each implementation below:
ResourceAdapterApplicationContext
StaticApplicationContext
ClassPathXmlApplicationContext
FileSystemApplicationContext
P.S.
A don't ask you to provide me reference to the docs. For example:
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
But from that definition its not clear that ClassPathXmlApplicationContext also implements AbstractRefreshableApplicationContext and can be used to change beans definition without stopping server.
I'm sorry you don't want references to the docs, but that's where all the information is.
StaticApplicationContext states
org.springframework.context.ApplicationContext implementation which
supports programmatic registration of beans and messages, rather than
reading bean definitions from external onfiguration sources. Mainly
useful for testing.
So you use it to register bean definitions directly
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition(beanName, beanDefinition);
This can be used in cases where your ApplicationContext needs to be dynamically changed. Note that you can pass a parent ApplicationContext to the StaticApplicationContext if you need both behaviors, ie. reading from XML/Java config and dynamically registering.
ClassPathXmlApplicationContext is one of the more common ApplicationContext implementations in my opinion. You simply point it to an XML (bean definition) resource on the classpath and it loads it up. The javadoc states
Useful for test harnesses as well as for application contexts embedded
within JARs.
You could therefore simply point to a resource on the classpath coming from a JAR and load that. It's simply enough to setup tests environments this way.
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("some-context.xml");
// boom you're ready to go
Note that Spring's JUnit support classes offer other (better) ways to setup testing environment.
But from that definition its not clear that
ClassPathXmlApplicationContext also implements
AbstractRefreshableApplicationContext and can be used to change beans
definition without stopping server.
That's what the javadoc is for.
FileSystemXmlApplicationContext is similar to the ClasspathXmlApplicationContext above, but it takes the configuration files from the file system instead of reading resources from the classpath.
ResourceAdapterApplicationContext states
org.springframework.context.ApplicationContext implementation for a
JCA ResourceAdapter. Needs to be initialized with the JCA
javax.resource.spi.BootstrapContext, passing it on to Spring-managed
beans that implement BootstrapContextAware.
I haven't worked with this one at all and I don't know where Resource Adapters are useful, but here are some more docs.
Just to add couple things to #Solitirios answer:
You forgot to mention several more context:
GenericApplicationContext
GenericXmlApplicationContext
AnnotationConfigApplicationContext
GenericWebApplicationContext
StaticWebApplicationContext
And many others.
In general, GenericApplicationContext is almost the same as StaticApplicationContext, the only difference between them in MessageSource support in StaticApplicationContext. Purpose for both of these classes is for small tests with tiny application context with couple beans.
GenericWebApplicationContext and StaticWebApplicationContext are also quite similar to each other, and typically they are used for emulation of Servlet container, e.g. tests or non-Servlet environment.
F.e. you can use something like this in your code (f.e. tests):
//create parent context
ApplicationContext xmlContext = new GenericXmlApplicationContext("classpath:/spring-*.xml");
//create mock servlet context
MockServletContext mockServletContext = new MockServletContext();
//create web context
GenericWebApplicationContext webContext = new GenericWebApplicationContext(mockServletContext);
//set attribute
mockServletContext.setAttribute(GenericWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext);
//set parent context
webContext.setParent(xmlContext);
//refresh context
webContext.refresh();
But there are couple contexts classes, which are worthy of attention. And considering your pre-requisites, I would choose one of them.
GenericXmlApplicationContext is very good alternative of ClassPathXmlApplicationContext and FileSystemXmlApplicationContext. Consider this example:
ApplicationContext context = new GenericXmlApplicationContext("classpath:some-context.xml");
is equivalent to
ApplicationContext context = new ClassPathXmlApplicationContext("some-context.xml");
or
ApplicationContext context = new GenericXmlApplicationContext("some-context.xml");
is equivalent to
ApplicationContext context = new FileSystemXmlApplicationContext("some-context.xml");
So GenericXmlApplicationContext looks more flexible.
AnnotationConfigApplicationContext is a context holder, if you don't want to keep your beans in XML-file.
//context creation
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
//context class
#Configuration
#ComponentScan("com.examples.services")
public class AppConfig {
#Bean
public DataSources dataSource() {
DataSource ds = new BasicDataSource();
//... init ds
return ds;
}
}
More information you can find here.

Ordering Listeners in Spring's WebApplicationInitializer

I have a custom ServletContextListener that I am using to initialize and start up a Cron4J scheduler.
public class MainListener implements ServletContextListener {
#Value("${cron.pattern}")
private String dealHandlerPattern;
#Autowired
private DealMoqHandler dealMoqHandler;
}
I am autowiring some objects in the Listener as shown, and would like for Spring to manage the listener's instantiation. I am using programmatic web.xml configuration through WebApplicationInitializer, but so far the Listener isn't being autowired (NullPointerExceptions whenever I try to access the supposedly autowired objects).
I've already tried to add my customer listener after adding the ContextLoaderListener, shown below:
public class CouponsWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SpringAppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
container.addListener(new MainListener()); //TODO Not working
}
I checked these past questions Spring - Injecting a dependency into a ServletContextListener and dependency inject servlet listener and tried to implement the following code inside the contextInitialized method of my listener:
WebApplicationContextUtils
.getRequiredWebApplicationContext(sce.getServletContext())
.getAutowireCapableBeanFactory()
.autowireBean(this);
However, I just get the following exception:
Exception sending context initialized event to listener instance of class com.enovax.coupons.spring.CouponsMainListener: java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:90) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
How can I make sure that Spring has already finished instantiation before adding my listener?
My mistake. It turns out the code in the original post is correct, and the listeners are being added in the correct order.
The issue was that I had annotated my custom listener with a #WebListener annotation (Servlet 3.0). This was causing the web app to disregard my addListener() code in WebApplicationInitializer, and instantiate the custom listener AHEAD of Spring's ContextLoaderListener.
The following code block illustrates the ERRONEOUS code:
#WebListener /* should not have been added */
public class CouponsMainListener implements ServletContextListener {
#Autowired
private Prop someProp;
}
You cannot use new with Spring beans - Java doesn't care about Spring and Spring has no way to modify the behavior of the new operator. If you create your objects yourself, you need to wire them yourself.
You also need to be careful what you do during initialization. When using Spring, use this (simplified) model: First, Spring creates all the beans (calls new for all of them). Then it starts to wire them.
So you must be extra careful when you start to use the autowired fields. You can't always use them right away, you need to make sure that Spring is finished initializing everything.
In some cases, you can't even use autowired fields in #PostProcess methods because Spring couldn't create the bean because of cyclic dependencies.
So my guess is that "whenever I try to access" is too early.
For the same reason, you can't use WebApplicationContextUtils in the WebApplicationInitializer: It hasn't finished setting up Spring, yet.

Categories