spring: where does `#autowired` look for beans? - spring

I'm having a spring mvc application with two contexts (as declared in my AbstractAnnotationConfigDispatcherServletInitializer subclass)
the root context contains models, repositories, etc
the mvc context contains the controllers
Now, I can inject a repository into a controller, but why?
Does the web context also include the beans from the root context (something like #Import??). The documentation implies they have a parent-child relationship, but by inspecting the web context I don't the repository beans inside it.
Or, does #Autowired work across multiple contexts? And, if so, how??

Both contexts are stored in the same servlet context.
If you notice, AbstractDispatcherServletInitializer,the parent class of AbstractAnnotationConfigDispatcherServletInitializer, does the registration on the onStartup method
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
To do that, first calls its parent onStartup method where it first adds the rootApplicationContext which is created by the createRootApplicationContext method of the AbstractAnnotationConfigDispatcherServletInitializer class and finally adds it to the ServletContext received on the onStartup method:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
/**
* Register a {#link ContextLoaderListener} against the given servlet context. The
* {#code ContextLoaderListener} is initialized with the application context returned
* from the {#link #createRootApplicationContext()} template method.
* #param servletContext the servlet context to register the listener against
*/
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
And after that, AbstractDispatcherServletInitializer calls its registerDispatcherServlet method where it calls the abstract method createServletApplicationContext that is in the AbstractAnnotationConfigDispatcherServletInitializerclass and creates the dispatcherServlet that is then added to the same ServletContext:
/**
* Register a {#link DispatcherServlet} against the given servlet context.
* <p>This method will create a {#code DispatcherServlet} with the name returned by
* {#link #getServletName()}, initializing it with the application context returned
* from {#link #createServletApplicationContext()}, and mapping it to the patterns
* returned from {#link #getServletMappings()}.
* <p>Further customization can be achieved by overriding {#link
* #customizeRegistration(ServletRegistration.Dynamic)} or
* {#link #createDispatcherServlet(WebApplicationContext)}.
* #param servletContext the context to register the servlet against
*/
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return empty or null");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
That's why you can inject a repository in a controller, because the 2 contexts are in the same ServletContext.

I typically return a single context from AbstractAnnotationConfigDispatcherServletInitializer.getRootConfigClasses() this class then has ComponentScan and/or Imports which finds #Configurations and #Components etc.
Unless you explicitly create parent-child contexts, all your beans, components, services ends up in a single ApplicationContext. By explicit I mean that you have to call setParent() on ApplicationContext before refresh() is called, so you typically know when you have done it.
This means that you can #AutoWire into an other spring bean from the same application context (the autowired bean may be from a parent context if you have nested contexts)
Edit
If you use AbstractAnnotationConfigDispatcherServletInitializer.getServletConfigClasses() and return two contexts from you initializer, the servletConfig context will be a child and the root context will be the parent. This means that you can autowire beans from the RootConfig context into the servletConfig context beans, but not the other way around. - This is why I typically only return a context from getRootConfigClasses() and null from getServletConfigClasses().

Related

How to get the DispatcherServlet from spring WebApplicationContext?

How to get the DispatcherServlet instance from a spring WebApplicationContext object?
I want to perform a direct request to the the front controller servlet (avoiding the network interface) and check the response from a spring application, e.g. WebApplicationContext instance.
Notice, we are not able to use MockMvc to check the HTML rendered by JSP because: "if you use JSPs, you can verify the JSP page to which the request was forwarded, but no HTML is rendered"(according to MockMvc vs End-to-End Tests)
So maybe if we get the DispatcherServlet instance we could perform the request through its doDispatch method and check the content from response.
When you look at
DispatcherServlet.java, there is a constructor with WebApplicationContext as parameter, but the super constructor from FrameworkServlet.java does only hold the field itself, without any callbacks - so the given web application context will not know the dispatcher servlet.
So I don't think you can resolve the dispatcher servlet by the WebApplicationContext. Of course you could try to inject/autowire the DispatcherServlet - maybe this is also possible inside tests, but I am not sure.
I think the most easiest way to check for JSP outputs would be to test those parts with a real server as described at Testing with a running server . This should give you the complete output and web application behaviour inside your test without any workarounds.
Dispatcher Servlet : It acts as front controller for Spring based web applications. It provides a mechanism for request processing where actual work is performed by configurable, delegate components. It is inherited from HttpServlet, HttpServletBean and FrameworkServlet. It also implements ApplicationContextAware hence it is able to set ApplicationContext.
WebApplicationContext : It is an extension of a plain ApplicationContext. It is web aware ApplicationContext i.e it has Servlet Context information. When DispatcherServlet is loaded, it looks for the bean configuration file of WebApplicationContext and initializes it.
So you can set and get the instance of any ApplicationContext from DispatcherServlet but not vice-versa.
dispatcherServlet.getWebApplicationContext();
Although, you can get an instance of DispatcherServlet but I doubt it will serve your purpose as it's scope is protected.
#Autowired
DispatcherServlet dispatcherServlet;
Code inside DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
It is possible to create your own DispatcherServlet and perform actions. You can override the default one created by Spring boot app.
Sample:
public class SampleDispatcherServlet extends DispatcherServlet {
private final Log logger = LogFactory.getLog(getClass());
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// You can use other implementations of HttpServletRequest and
// HttpServletResponse as per your requirement
// e.g. FormContentRequestWrapper , ResourceUrlEncodingResponseWrapper many more
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
HandlerExecutionChain handler = getHandler(request);
try {
// Any task to be done
super.doDispatch(request, response);
} finally {
// Some task to be done
}
}
}
HandlerExecutionChain- Handler mapper is used to match current request to appropriated controller. Interceptors are the objects invoked before and after some of dispatching actions
Then we have to register the DispatcherServlet:
#Bean
public ServletRegistrationBean dispatcherRegistration() {
return new ServletRegistrationBean(dispatcherServlet());
}
//The bean name for a DispatcherServlet that will be mapped to the root URL "/"
#Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new SampleDispatcherServlet();
}

Spring Batch not finding global singleton scoped beans in web application - what is wrong?

I have a spring web application which registers multiple spring batch jobs for lengthy background processing at launch time. As near as I can tell, the spring-batch contexts are not aware of the singletons declared in the root AnnotationConfigWebApplicationContext bean, so multiple copies of complex-to-initialize beans are being instantiated for every job both at initial application load time and again at execution time.
I have determined that in AbstractBeanFactory.doGetBean() the bean is being correctly identified as a singleton, but for some reason the bean factory for the job is unaware of the bean factory for the parent context.
I have refactored some of the beans as Application scoped, but the application scope bean is (apparently) not legal in the spring batch context.
Either I am grossly misunderstanding something about spring scopes, or there is something out of kilter with my initialization of the spring-batch elements (code below). I am leaning towards both, as the one would lead to the other.
As I understand Spring scopes, I should see something like this, with each child scope being able to see singletons defined in the parent scope:
AnnotationConfigWebApplicationContext (web application context)
|
v
ResourceXmlApplicationContext (1 per registered job)
|
v
ResourceXmlApplicationContext (1 per registered step)
Initialization code in question:
#Component("mySingletonScopedBean")
#Scope(value = "singleton", proxyMode = ScopedProxyMode.DEFAULT)
#Order(1)
public class MySingletonScopedBean {
// getters, setters, etcetera
}
// EDIT: Added in response to comment below
#Autowired
public ApplicationContext applicationContext;
#Bean
public ClasspathXmlApplicationContextsFactoryBean classpathXmlApplicationContextsFactoryBean () throws IOException
{
String resourcePath = somePath "*.xml";
logger.trace("classpathXmlApplicationContextsFactoryBean() :: {} ", resourcePath);
Resource[] resources = applicationContext.getResources(resourcePath);
ClasspathXmlApplicationContextsFactoryBean bean = new ClasspathXmlApplicationContextsFactoryBean ();
bean.setApplicationContext(applicationContext);
bean.setResources(resources);
return bean;
}
#Bean
public AutomaticJobRegistrar automaticJobRegistrar() throws IOException, Exception {
ClasspathXmlApplicationContextsFactoryBean c = classpathXmlApplicationContextsFactoryBean ();
AutomaticJobRegistrar automaticJobRegistrar = new AutomaticJobRegistrar();
automaticJobRegistrar.setApplicationContext(applicationContext);
automaticJobRegistrar.setApplicationContextFactories(c.getObject());
automaticJobRegistrar.setJobLoader(jobLoader());
return automaticJobRegistrar;
}
#Bean
public JobLoader jobLoader() {
DefaultJobLoader jobLoader = new DefaultJobLoader(jobRegistry(), stepRegistry());
return jobLoader;
}
#Bean
public StepRegistry stepRegistry() {
MapStepRegistry stepRegistry = new MapStepRegistry();
return stepRegistry;
}
#Bean
public JobRegistry jobRegistry() {
JobRegistry jobRegistry = new MapJobRegistry();
return jobRegistry;
}
Edit: Closing
The whole spring environment initialization was messed up.
I'm saving my rants about the "clean" injection model and what a pain it is to figure things out when they break for a massive blog post after this project.

Injecting dependencies using an Interceptor

Would it be technically a good and acceptable practice to inject required dependencies using an Interceptor type. For example:
public #interface Inject {
public Class thisType();
}
public class InjectionInterceptor implements HandlerInterceptor {
#Override
public bool preHandle(HttpServletRequest hsr, HttpServletResponse hsr1, Object o) {
HandlerMethod handlerMethod = (HandlerMethod) o;
Class type = handlerMethod.getBeanType();
Annotation[] annotations = type.getAnnotationsByType(Inject.class);
for(Annotation annotation: annotations){
Inject inject = (inject) annotation;
for(Field field :type.getDeclaredFields()){
if(field.getType().equals(inject.thisType())){
field.setAccessible(true);
field.set(handlerMethod.getBean(), Services.find(inject.thisType()));
}
}
....
return true;
}
...
}
A bean can have 4 scopes.
Singleton
Only one shared instance of the bean will be managed, and all requests for beans with an id or ids matching that bean definition will result in that one specific bean instance being returned by the Spring container.
It is default scope of a bean.
Prototype
New instance is returned by the container when bean with the specified id is requested.
Ex: If you have a bean with id as "employee" in your spring container, then everytime you do a
Employee emp = context.getBean("employee");
A new instance will be returned.
request, session, and global session are for use only in web-based applications
Request
A new instance is created for every single HTTP request.
Ex : Login needs different instance everytime.
Session
A new instance will be created of the bean using the bean definition for the lifetime of a single HTTP Session.
global
The global session scope is similar to the standard HTTP Session scope and really only makes sense in the context of portlet-based web applications
You can specify the scope of a bean in two ways
Using XML:
<bean id="employee" class="com.company.Employee" scope="singleton"/>
Using annotation.
mark the class with #Scope("prototype")
you can read more about scopes here
sample code for reference is available here

Configuring Spring to ignore dependencies annotated with #Inject

I have an EJB class in which I need to inject two beans - one should be injected by the EJB container and other is a Spring Container.
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#LocalBean
public class SomeClass {
#Inject
private EJBClass a;
#Autowired
private SpringComponent b;
}
Here, the Spring interceptor trying to intercept the injection of bean 'a' and it's getting failed. I want the EJB container to inject the bean 'a' and Spring container to inject bean 'b'.
Please show me a way out here.
By customizing the SpringBeanAutowiringInterceptor class, dependencies annotated with #Inject can be excluded from auto wiring.
To understand what happens behind the scene, have a look at source code of
SpringBeanAutowiringInterceptor.java -
/**
* Actually autowire the target bean after construction/passivation.
* #param target the target bean to autowire
*/
protected void doAutowireBean(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
configureBeanPostProcessor(bpp, target);
bpp.setBeanFactory(getBeanFactory(target));
bpp.processInjection(target);
}
At first line, of doAutowireBean, a new instance of AutowiredAnnotationBeanPostProcessor is created. Here set of annotations to be scanned for auto wiring dependencies are configured.
/**
* Create a new AutowiredAnnotationBeanPostProcessor
* for Spring's standard {#link Autowired} annotation.
* <p>Also supports JSR-330's {#link javax.inject.Inject} annotation, if available.
*/
#SuppressWarnings("unchecked")
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
Since, by default, the #Inject annotation is configured spring scans respective dependencies marked with #Inject and tries to auto wire them.
To exclude #Inject annotated dependencies write below custom class.
public class CustomSpringBeanAutowiringInterceptor extends SpringBeanAutowiringInterceptor {
/**
* Template method for configuring the
* {#link AutowiredAnnotationBeanPostProcessor} used for autowiring.
* #param processor the AutowiredAnnotationBeanPostProcessor to configure
* #param target the target bean to autowire with this processor
*/
protected void configureBeanPostProcessor(AutowiredAnnotationBeanPostProcessor processor, Object target) {
Set<Class> annotationsToScan = new HashSet<Class>();
annotationsToScan.add(Autowired.class);
annotationsToScan.add(Value.class);
processor.setAutowiredAnnotationTypes(annotationsToScan);
}
}
Here configureBeanPostProcessor hook is utilized to customize the bean post processor so as to include only those annotations which we require to be auto wired.
After applying this custom class as interceptor in code the desired behaviour can be achieved
#Stateless
#Interceptors(CustomSpringBeanAutowiringInterceptor.class)
#LocalBean
public class SomeClass {
#Inject
private EJBClass a;
#Autowired
private SpringComponent b;
}
Let know in comments if you face any issues. Also feel free to optimize the code as deemed fit and excuse any compilations / formatting issues.
Use #EJB annotation to inject EJB

Jersey with Spring AOP

I would like my AOP advice to have a handle to the currently executing Jersey resource's HttpContext. The spring-annotations sample mentions that the user could get hold of the request and authenticate etc.,, but how does one get hold of any of the values in the Context in the advice?
Currently my resource definition looks like this:
#Singleton
#Path("/persist")
public class ContentResource {
#PUT
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Auth
public Content save(Content content){
//Do some thing with the data
}
}
And the aspect is defined so:
#Aspect
public class AuthorizeProcessor {
#Around(value="#annotation(package.Auth) and #annotation(auth)", argNames = "auth")
public Object authorize(ProceedingJoinPoint pjp, Auth auth) throws Throwable{
//How do I get the HttpContext here?
return pjp.proceed();
}
}
Granted this is most likely too late, but I did what you are doing by implementing a Servlet Filter in front of the service that does the authorization. This avoids the need for AOP entirely, and it gives you the actual ServletRequest directly without working around the system to get it.
Ironically, the question that you helped me to answer would likely help you here, if you really wanted AOP.
You can supply the Spring RequestContextFilter to the request, and then access the HttpServletRequest (as opposed to the HttpContext):
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/path/to/services/*</url-pattern>
</filter-mapping>
Access code down the filter chain:
/**
* Get the current {#link HttpServletRequest} [hopefully] being made
* containing the {#link HttpServletRequest#getAttribute(String) attribute}.
* #return Never {#code null}.
* #throws NullPointerException if the Servlet Filter for the {#link
* RequestContextHolder} is not setup
* appropriately.
* #see org.springframework.web.filter.RequestContextFilter
*/
protected HttpServletRequest getRequest()
{
// get the request from the Spring Context Holder (this is done for
// every request by a filter)
ServletRequestAttributes attributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
return attributes.getRequest();
}

Resources