Spring root and servlet context with Java config - spring

I'm running a Spring application in a Servlet 3.0+ environment to programmatically configure the servlet context using all Java configuration. My question (with details below): how is a project structured to support component scanning for both root and web application contexts without duplicating component initialization?
As I understand it, there are two contexts in which to register Spring beans. First, the root context is where non-servlet-related components go. For example batch jobs, DAOs, etc. Second, the servlet context is where servlet-related components go such as controllers, filters, etc.
I've implemented a WebApplicationInitializer to register these two contexts just as the JavaDoc in WebApplicationInitializer specifies with a AppConfig.class and DispatcherConfig.class.
I want both to automatically find their respective components so I've added #ComponentScan to both (which is resulting in my Hibernate entities being initiated twice). Spring finds these components by scanning some specified base package. Does that mean I need to put all my DAO-related objects in a separate package from the controllers? If so, that'd be quite inconvenient as I generally like to package by functionality (as opposed to type).
Code snippets...
WebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebAppConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
AppConfig:
#Configuration
#ComponentScan
public class AppConfig {
}
WebAppConfig:
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(basePackageClasses = AppConfig.class)
public class WebAppConfig extends WebMvcConfigurerAdapter {
}

Just define what you want to scan for in each configuration. In general your root configuration should scan for everything but #Controllers and your web configuration should only detect #Controllers.
You can accomplish this by using the includeFilters and excludeFilters attributes of the #ComponentScan annotation. When using include filters, in this case, you also need to disable using the default filters by setting useDefaultFilters to false.
#Configuration
#ComponentScan(excludeFilters={#Filter(org.springframework.stereotype.Controller.class)})
public class AppConfig {}
And for your WebConfig
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(basePackageClasses = AppConfig.class, useDefaultFilters=false, includeFilters={#Filter(org.springframework.stereotype.Controller.class)})
public class WebAppConfig extends WebMvcConfigurerAdapter {}
In addition, you need to import the #Filter annotation:
import static org.springframework.context.annotation.ComponentScan.Filter;

The short answer is:
Yes, you should define seperate component scans per context, thus modeling your project differently and extracting the DAO into a seperate name-space (package).
The longer answer is split into two parts, one regarding the servlet contexts and the other regarding modeling a project.
Regarding root and servlet contexts: You have defined correctly the different responsibilities of root context and servlet context. This is an understanding most developers tend to miss and it is very important.
Just to clarify a bit more on this subject, you can create one root context and several (0+) servlet contexts. All of the beans defined in your root context will be available for all servlet contexts but beans defined in each servlet context will be not be shared with other servlet contexts (different spring containers).
Now since you have multiple spring containers and you use component scan on the same packages then beans are created twice, once per container.
To avoid this you can do a few things:
Use one container and not two, meaning you can define only the root container or only the servlet container (I have seen many applications like this).
Seperate your beans into different places and provide each container its unique component scan. remember that all of the beans defined in the root container are available to use in the servlet container (you do not need to define them twice).
Lastly, a few words regarding project modeling. I personally like to write my project in layers, thus separating my code into controllers (application layer), business logic (bl layer) and DAO (database layer).
Modeling like this helps in many ways, including this root/servlet context issue you have encountered.
There are tons of information regarding layered architecture, here is a quick peed: wiki-Multilayered_architecture

Related

Spring (Security) dependency injection

I asked a question more specific to my case about 2 hours ago, but I realised I'm not really addressing my problem at the root cause.
I have a Spring application that uses Spring Security. Throughout my application, (Controllers, service classes etc) I'm using dependency injection and it all works fine. However, I recently started configuring Spring Security, and I can't inject any dependencies inside the classes in my "security" package. Online I read somewhere: "Also when you use #Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail" and I was wondering if this maybe had something to do with my issue. My spring configuration basically has one "starting-point", that is the following class:
#Component
#Service
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.register(SecSecurityConfig.class);
sc.addListener(new ContextLoaderListener(root));
sc.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
}
}
This code is run on startup. As you can see, it is registering the SecSecurityConfig.class which is where I configure Spring Security. Inside of that class and onwards (all classes it uses and all classes that those classes use) I can't inject any dependencies. I was wondering if anyone could tell me what the problem could be. Sorry if I'm unclear or incorrect - please tell me so, I find the concept of DI somewhat hard to grasp. My component-scan in XML is: <context:component-scan base-package="com.qars"/> which is the package that my security package is also in.
Also all my classes are annotated with #Component or #Service

Is ApplicationContext automatically instantiated in Spring?

Is ApplicationContext automatically instantiated in Spring?
If I have my bean defined like this
#Component
public class Car{
...
}
and then I have my config class which tells Spring container where to look for beans through the annotation #ComponentScan
#Configuration
#ComponentScan
public class AppConfig {
...
}
Is Spring automatically creating a context loading all my beans? Or do I have to create it programmatically? If so how do I do it, with something like this?
#Configuration
#ComponentScan
public class AppConfig {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean(Car.class);
...
}
Even doing this, there may be a problem, because every time I need the context I have to call new AnnotationConfigApplicationContext...
what is the recommended way to instantiate the context and making him available inside the whole project, maybe as a bean like inside Spring boot app where i can just autowire it.
How Spring Boot can initialize it, load all the beans and let the context available as a bean, ready to be autowired?
No, Application Context isn't automatically instantiated, if you're having a simple and basic Spring Core application. Moreover, your #Configuration class won't scan anything and won't create any beans, if you don't create your Spring Container/Context explicitly with that #Configuration class.
There are several ways of creating Application Context, but the most popular and traditional ones are:
ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml) - implying, that you have your container configuration in the applicationContext.xml file;
ApplicationContext context = new AnnotationConfigApplicationContext(ConfigClass.class); - implying, that your ConfigClass is the #Configuration class.
However, if you have the Spring Boot application annotated with #SpringBootApplication, then the Application Context will be automatically instantiated for you, because:
#SpringBootApplication annotation consists of:
#EnableAutoConfiguration - which enables Spring Boot’s auto-configuration mechanism;
#ComponentScan - which enable #Component scan on the package where the application is located;
#Configuration - allows to register extra beans in the context or import additional configuration classes.
and this will spin up the context for you.
You can obtain the reference to the Spring Context created by Spring Boot, by the factory method you have in your main method: SpringApplication.run(MainClass.class, args);
This returns the reference to the Application Context and you can assign it to variable like this:
ApplicationContext context = SpringApplication.run(MainClass.class, args)

Spring Framework 5.0.0.Final parent context not getting loaded

I was trying with latest spring 5.0.0.Final with my EAR project which has a parent context defined in web.xml using context-param
with param names locatorFactorySelector and parentContextKey but spring could not able to load the parent context. When i checked the ContextLoaderListener source code it seems like there is no logic applied to pick parent context. Here my question is does spring 5 provide any default implementation of ContextLoader which caters loading of parent context or spring 5 dropped, if not what is the approach to support this, do i have to implement our own ?
The loading of the parent context based on locatorFactorySelector were handled at ContextLoader#loadParentContext(). But they changed it to return null in this commit.
As said by the javadoc , I think you can create a new ContextLoaderListener and override this method to return the parent context:
public class FooContextLoaderListener extends ContextLoaderListener{
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
//load and return the parent context ......
}
}
Then use this ContextLoaderListener to start up Spring :
<listener>
<listener-class>org.foo.bar.FooContextLoaderListener</listener-class>
</listener>
For me this below piece of code worked fine.
public class BeanFactoryContextLoaderListener extends ContextLoaderListener {
private static Logger log = Logger.getLogger(BeanFactoryContextLoaderListener.class);
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beanRefFactory.xml");
return ctx;
}
}
Obviously I added a listener too in web.xml.
My team recently bumped into the same problem. We wanted to start using Webflux and it requires Spring 5.
Here is what I did:
Manually reintroduce BeanFactoryLocator mechanism. Take following classes from Spring 4, put it into your code and fix packages:
AbstractUrlMethodNameResolver
AnnotationMethodHandlerAdapter
BeanFactoryLocator
BeanFactoryReference
BootstrapException
ContextSingletonBeanFactoryLocator
DefaultAnnotationHandlerMapping
HandlerMethodInvocationException
HandlerMethodInvoker
HandlerMethodResolver
InternalPathMethodNameResolver
MethodNameResolver
NoSuchRequestHandlingMethodException
ServletAnnotationMappingUtils
SingletonBeanFactoryLocator
SourceHttpMessageConverter
WebUtils
XmlAwareFormHttpMessageConverter
Following Subhranil's advice from this thread, use custom ContextLoaderListener which loads parent context same as in Spring 4. Then use it in web.xml.
In each WAR's spring-servlet.xml add DefaultAnnotationHandlerMapping so it scans for controllers. Accompanying beans like AnnotationMethodHandlerAdapter are also needed.
It worked for us.
If all you need is your context-param in any of your spring managed class you are looking for ServletContextAware.
Just implement that class and override its method to get the ServletContext object. Later you can also get the context-params using the ServletContext object.
Check out a very similar question.
Apparently, the mechanism for locating the parent context was removed with SPR-15154 (see also the corresponding Github issue spring-framework#19720).
One workaround is to extend org.springframework.web.context.ContextLoaderListener and re-implement the loadParentContext method is described in this stackoverflow answer.
There is probably a better way to solve parent context loading in Spring 5.x, which I still have to figure out.

When use AbstractAnnotationConfigDispatcherServletInitializer and WebApplicationInitializer?

I am working with Spring 4.0.7
I did a research about configure Spring MVC through JavaConfig.
Practically until yesterday I have seen two configurations using these two options
extends AbstractAnnotationConfigDispatcherServletInitializer
extends WebMvcConfigurerAdapter and implements WebApplicationInitializer
Note: (2) are two classes, one for extension and the other for implementation
I am using (2) because I have found many examples where I am able to configure converters, formatters, resources handlers etc…
But in the latest days I have tried to help a question on StackOverflow and I did realize (1) exists.. I did some overview on Google about (1) and exists some examples working with (1)
My question is how the title of this post describe.
Thank You
With the release of the Servlet 3.0 spec it became possible to configure your Servlet Container with (almost) no xml. For this there is the ServletContainerInitializer in the Servlet specification. In this class you can register filters, listeners, servlets etc. as you would traditionally do in a web.xml.
Spring provides a an implementation the SpringServletContainerInitializer which knows how to handle WebApplicationInitializer classes. Spring also provides a couple of base classes to extend to make your life easier and the AbstractAnnotationConfigDispatcherServletInitializer is one of those. It registers
a ContextLoaderlistener (optionally) and a DispatcherServlet and allows you to easily add configuration classes to load for both classes and to apply filters to the DispatcherServlet and to provide the servlet mapping.
The WebMvcConfigurerAdapter is for configuring Spring MVC, the replacement of the xml file loaded by the DispatcherServlet for configuring Spring MVC. The WebMvcConfigurerAdapter should be used for a #Configuration class.
#Configuration
#EnableWebMvc
public class WebConfiguration
extends WebMvcConfigurerAdapter implements WebApplicationInitializer
{ ... }
I wouldn't recommend mixing those as they are basically 2 different concerns. The first is for configuring the servlet container, the latter for configuring Spring MVC.
You would want to split those into 2 classes.
For the configuration.
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter { ... }
For bootstrapping the application.
public class MyWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer
{
protected Class<?>[] getRootConfigClasses() {
return new Class[] {RootConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebConfiguration .class};
}
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
An added advantage is that you now can use the convenience classes provided by Spring instead of manually configuring the DispatcherServlet and/or ContextLoaderListener.
To start from the beginning it is worth looking into how servlet container starts.
SpringServletContainerInitializer is bootstrapped automatically by any Servlet 3.0 container.
SpringServletContainerInitializer looks for classes implementing WebApplicationInitializer (link to spring.io; also well described in "Spring In Action" 4th edition by Craig Walls, p.135).
So to start - SpringServletContainerInitializer has to find the right class implementing WebApplicationInitializer. There are two ways of making it happen:
One is by implementing WebApplicationInitializer on its own; the interface was introduced in Spring 3.1
The second is by extending AbstractAnnotationConfigDispatcherServletInitializer class which also implements WebApplicationInitializer. The class was introduced in Spring 3.2 for convenience and it is "the preferred approach for applications that use Java-based Spring configuration." - see the link. It enables you to start servlet application context as well as root application context.
I would also like to higlight that WebMvcConfigurerAdapter you mention should not be confused with WebApplicationInitializer. As it name suggests - it has to do with configuring "Mvc". It is an adapter class that implements empty methods from WebMvcConfigurer. You use it when you configure your Mvc controller with #EnableWebMvc annotation.
Hope this helps.

Spring: how AnnotationConfigWebApplicationContext could not overwrite later beans?

I have a web application that use Sring IoC framework.
I use the Java configuration for Spring, and I only use #Configuration annoted module definition (no DI related tags elsewhere in the code).
The Spring registry is built on web application start-up thanks to (a bit modified version of) Spring context loader listener, and the contextConfigLocation param in web.xml configured to point to the #Configuration annotated class.
All that is good and I get a AnnotationConfigWebApplicationContext.
Now, I want to have plugins in my application, that will have their own #Configuration annotated configuration classes, and will use some of the main application services. BUT I don't want to have main application to be modified to load these new modules.
So, I thought that I could simply use the package searching of annotated class for that, but now, it seems that I can use two beans with the same type, even if they have different ids, and clearly AnnotationConfigWebApplicationContext doc states that:
Note: In case of multiple #Configuration classes, later #Bean definitions will override ones defined in earlier loaded files. This can be leveraged to deliberately override certain bean definitions via an extra Configuration class.
I don't want that, because modules should be able to contribute alternative version of services, not (alwways) override existing one - especcially if I want to have a "moduleDef" bean.
I tried to use differents approach on that, but the hierachy of Context and related services is just to big for me.
So, does anybody know how I could reach my goal ?
Thanks
You can have multiple beans of the same type, but You cannot have 2 or more beans with the same ID in a single Spring ApplicationContext - no matter if You use XML or JavaConfig.
The overriding mechanism matches the bean ID's, so all You need to do is to ensure unique ID, i.e.: coreModuleDef, someOtherModuleDef, anotherModuleDef. I don't think You need the ID of each module definition to be identical? What should be sufficient is the type to be the same, but not ID.
You can also turn off the overriding mechanism by setting allowBeanDefinitionOverriding to false on Your AnnotationConfigWebApplicationContext to get an exception if You accidentally override a bean:
public class MyDispatcherServlet extends DispatcherServlet {
#Override
protected void postProcessWebApplicationContext(
ConfigurableWebApplicationContext wac) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}
or:
public class MyContextLoaderListener extends ContextLoaderListener {
#Override
protected void customizeContext(
ServletContext servletContext,
ConfigurableWebApplicationContext applicationContext) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}

Resources