I have a project with a few old HTTPServlets, I would like a way to manage the creation/life-cycle of the Servlet in Spring so I can do a few things like DI and pass in database objects through Spring/Hibernate integration.
First I have setup the Spring application context in web.xml as follows;
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Next I have my servlet definition in web.xml;
<servlet>
<servlet-name>oldHttpServlet</servlet-name>
<display-name>oldHttpServlet</display-name>
<servlet-class>com.package.MyServlet</servlet-class>
</servlet>
In my Spring application-context.xml I would like to make some bean def as follows;
<bean id="oldHttpServlet" class="com.package.MyServlet"></bean>
I think the above I need to implement some interface within my servlet, keep the bean definition as above in the Spring app-context.xml and then make a change to the servlet definition in the web.xml... I'm not sure what is the simplest change to make as there is some inheritance to worry about on the MyServelet.java side, which looks like this;
class MyServlet extends MyAbstractServlet{
void doStuff(){
//YOu know...
}
}
And MyAbstractServlet;
class MyAbstractServlet extends HttpServlet{
doPost(){
//Some post implementation here...
}
}
What is the best way to wrap MyServlet in Spring and have it loaded from there rather than being instantiated via web.xml?
I'm assuming the best way would be to use Springs HttpRequestHandler and then use the HttpRequestHandlerServlet in the web.xml in place of the current servlet. However I'm not sure how I go about implementing the HttpRequestHandler interface to work with the existing doPost() stuff in the myAbstractServlet...
If you need to inject dependencies into a servlet than I would go with Spring's HttpRequestHandlerServlet. You create an implementation of HttpRequestHandler (it has a method:
public void handleRequest(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException;
that you need to implement - it is the equivalent of what you'd have in a doGet / doPost.
The instance of all your HttpRequestHandler implementatins should be handled by spring (set up an annotation driven config and annotate it with, say, #Component) or use an XML config to set up those beans.
In your web.xml create the mapping for HttpRequestHandlerServlet. If you name the servlet the same as you did your HttpRequestHandler then Spring will make HttpRequestHandlerServlet automatically handle the request with the matching HttpRequestHandler.
Since HttpRequestHandler is a Spring bean, you can make Spring inject all the services you need into it.
If you still do not want to use HttpRequestHandler in lieu of Servlets than all that is left is some "programming judo", like retrieving WebApplicationContext with Spring utils and getting beans from there by a "per name" basis. Servlets are not instantiated by Spring so it cannot manage them. You would have to create a base class for your servlets and manage the DI as an initialization step yourself.
The code would look like this (inside a servlet):
WebApplicationContext webContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
CertainBeanClass bean = (CertainBeanClass) webContext.getBean("certainBean");
[EDIT]
Indeed, as one of the commenters suggested, there IS one more option. It still requires you to do a manual setup in your servlets init method though. Here is how this would look like:
public void init(ServletConfig config) {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
You may also try your luck with #Configurable annotation on Servlet with weaving, but this might or might not work (I think Servlet might get initialized before Spring context is set up, so it still won't have the possibility to do its magic).
The ideal use of Servlet should be as Controller and underlying container manages its life cycle so we don't need Spring to manage its instance
so I can do a few things like DI and pass in database objects through Spring/Hibernate integration.
You should never have dependency of Servlet in your dao/services, Your servlet should be calling services which is perfectly suitable to manage with Spring DI context
regarding SpringBeanAutowiringSupport, note from the javadoc:
NOTE: If there is an explicit way to access the ServletContext, prefer such a way over using this class. The WebApplicationContextUtils class allows for easy access to the Spring root web application context based on the ServletContext.
Related
I am trying to configure spring app without web.xml,after googling i found webappintializer can do that, which internally uses servletcontainer initilizer,what is the use of servletcontainer initializer and how does this play a role in configuring servlets without web.xml
If you want to do initialization when the application starts or clean up when it is destroyed, you should use this interface.
When the application starts, the Servlet container calls the OnStart method of this interface, passing in as a parameter a set of all classes that implement, extend or are annotated with the type(s) declared in the HandlesTypes annotation.
The specification also add a number of methods to dynamically register Servlets, filters and listeners.
Following is example how you initialize your spring dispatcher Servlet :
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext ctx) throws ServletException {
ServletRegistration.Dynamic dispatcher = ctx.addServlet("dispatcher", new DispatcherServlet());
dispatcher .setLoadOnStartup(1);
dispatcher .addMapping("/");
}
}
WebApplicationInitializer - This provides programmatic way of registering servlets to your Servlet Context. Prior to servlet 3.0, you can do it only through web.xml. With servlet 3.0 specification you can register your dispatcher servlet to the servlet context, so they will be aware of your application context and function similar to servlets registered in your web.xml.
ServletContext is nothing but that provide interface method to interact with your servlet container. Its Javadoc is pretty good.
SpringServletContainerInitializer which is an implementation of ServletContainerInitializer is responsible for invoking the onStartup of WebApplicationInitializer and passes it a ref to ServletContext.
Both web.xml and WebApplicationInitializer can coexist and you can register additional servlets and i think you can override servlets as well.
You have tons of example out there in web to show you how to use WebApplicationInitializer
Hope this helps.
For some reasons, I can autowire in my controllers but not in a servlet I created.
This is the top part of my servlet:
#Component
public class MyServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
#Autowired
private CobiService cobiService;
In my web.xml this is the related configuration:
<servlet>
<servlet-name>convservlet</servlet-name>
<servlet-class>com.gim.servlets.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>convservlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
And this is the way I tell spring to scan for components:
<context:component-scan base-package="com.gim" />
For some reason my autowired object cobiService is null. Did I forget anything? What should I change?
Servlets are not managed by Spring, they are managed by the Servlet container (like Tomcat). Therefore Spring cannot inject dependencies into Servlets in a normal Spring way. You can however do something like the following:
public class MyServlet extends javax.servlet.http.HttpServlet {
private CobiService cobiService;
#Override
public void init() throws ServletException {
super.init();
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
cobiService = applicationContext.getBean(CobiService.class);
}
}
You can override your servlet init method, and do
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext (this);
SpringBeanAutowiringSupport
There is no need to do
implements javax.servlet.Servlet, since you are extending HttpServlet
There are two servlets being created. One is being created by the servlet container/Java EE container when the application initializes and reads the configured <servlet> tag inside the web.xml file. The other would be created when the Spring IOC container performs its component scanning upon initialization.
In this case, the first instance is not able to participate in dependency injection because it was not created within the Spring IOC container. In order to participate in dependency injection, a bean must be managed by the Spring IOC container. When the servlet container/Java EE container instantiates the servlet it has no knowledge of Spring's IOC container.
Unfortunately, when a request comes in that satisfies the url-pattern specified in the web.xml file for the servlet, the request is redirected to the first instance created by the servlet container/Java EE container which is not a bean and not a candidate for autowiring.
If you were to remove the servlet from web.xml and add a #RequestMapping annotation to the servlet, the second instance (which is an actual bean able to utilize autowiring) would be utilized for requests fullfilling the specified url pattern in the #RequestMapping, however at that point you pretty much have a controller.
So in summary, my recommendation would be to adhere to Spring conventions and use a controller class. The controller class will match and exceed the functionality provided by the Servlet.
In my SpringMVC project I need a certain tactic to call a class which does parse a xml file which does own my RMI server ip & port & the url must be relative to servletContext url :
HttpServletRequest request;
request.getServletContext().getRealPath("/WEB-INF/LABLAB/RMI-Config.xml")
I want to load these classes when I start my application in tomcat not when I call a Controller Class because my application depend to my RMI so before anything else I have to parse my file & using the IP & PORT fields to start connecting to my RMI & then call the rmi method to do some stuffs later on ...
now
how I'm gonna do it ? please tell me how I can initialize an instance of HttpServletRequest & give an intial value when I'm not on the Controller classes as well .
Thank you
You are using Spring, then you can create a class and implement IntializingBean. If you want to get hold of ServletContext you can simple use #Autowired annotation in your initializing bean. For e.g.:
#Component
public class SomeBean implements InitializingBean {
#Autowired
private ServletContext context;
public void afterPropertiesSet() throws Exception {
String path = context.getRealPath("/WEB-INF/LABLAB/RMI-Config.xml");
//do something.
}
}
As per docs:
IntializingBean - Interface to be implemented by beans that need to
react once all their properties have been set by a BeanFactory.
Or take a look here how to do this using ServletContextListener.
You need to implement the ServletContextListener interface and refer to it from your web.xml:
<listener>
<listener-class>InitializingListener</listener-class>
</listener>
The interface has a contextInitialized(ServletContextEvent sce) method, in which you can call sce.getServletContext(), so you don't need a HttpServletRequest.
If this doesn't work out, because you also need to access some of your Spring beans from the initializing class, then forget about implementing the ServletContextListener interface, and do the following instead:
Instantiate your initialization class via Spring (make a bean of that type).
Make the class implement ServletContextAware. This will cause Spring to inject the ServletContext into your class.
Define an init method on that bean (use the init-method bean attribute, or define a #PostConstruct annotated method)
Make all your Controller beans depend on the initializing bean by using the depends-on bean attribute. As a result, the initialaizing bean will be created before any Controller bean.
Without the last step, it cannot be guaranteed that your controllers won't start processing requests before the initialization bean finishes its work. However, specifying the depends-on attribute on each and every controller bean is also problematic, especially that they are usually created by applying the #Controller annotation instead of xml configuration. A nice workaround is described in this post.
I have a Spring web application with two contexts: one (applicationContext) built by ContextLoaderListener and a second (webContext) built by DispatcherServlet.
Within the applicationContext is a bean (org.springframework.security.authentication.DefaultAuthenticationEventPublisher) that fires spring context events.
But the receiver for the event is defined in the webContext. And that receiver did not get the event. (If put the receiver for test purpose in the applicationContext then it get the event, but I can not do this, because I need the webContexts for its functionality.)
So my question is, how to bridges the events from the applicationContext to webContext?
I had the same problem, solved mine by moving the beans creating the event to web-context. However you can solve your problem by manually wiring your event listener, something like this (this code is not compiled therefore it is untested):
#Component
public class BeanInWebContext implements ApplicationListener<SomeEvent> {
#Autowired
private ApplicationContext webContext;
#PostConstruct
public void registerAsListener() {
// get parent context
AbstractApplicationContext appContext = (AbstractApplicationContext) webContext.getParent();
// register self as a listener, this method is in AbstractApplicationContext
appContext.addApplicationListener(this);
}
#Override
public void onApplicationEvent(SomeEvent event) {
}
}
I think the actual answer is that you may want to configure your app differently (so that you only have one context)
I think in your web.xml you need to do something like this :
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/META-INF/applicationSpringConfig.xml
</param-value>
</init-param>
</servlet>
But to answer the deeper question. Someone else points out that you can use includes in your spring file (indeed in the above you can have more than one springconfig specified in your dispatcher servlet).
But when you include other context files you do not share instances of beans, only definitions.
Modularising Spring applications has been the only real downside of spring in comparison with EJB etc. That led spring into using OSGi.
And the answer to your underlying question of how to share spring context, officially you share spring bean instances between contexts using OSGi (spring dm)
Try moving the event publisher to the web context file, where it should have visibility over the whole application context. A similar issue occurs when configuring method security in the parent application context. The parent application context (loaded by ContextLoaderListener) isn't aware of the child (web) context.
You can also use a single application context for the entire application if you don't really need the parent-child relationship between the two. Often it just gets in the way and it is easier if all beans were defined in the same space.
As stated in documentation for the spring framework the simple ApplicationEvent mechanism is only designed to be used within the same application context, I am not aware that it is possible to propagate events to child contexts.
If you need a more advanced solution you might look into using a more enhanced solution like Java Message Service or Spring Integration.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events
We can use the import tag to import/bridge the 2 different contexts created in a way the visibility of the events/beans are available and shared.
<import resource="applicationContext_name.xml"/>
In this import the context xml which is configured to be created from ContextLoaderListener in the context xml of the DispatcherServlet.
I am new to spring MVC. I am looking for a place in my spring mvc applicationwhere I can initialize all sorts of things in the application.
usually I did that in the init() method of the my main servlet but now the dispatcher servlet is of spring and I cannot overide the init function.
what is the best practice?
Thanks.
Use a ServletContextListener and define it in web.xml:
<listener>
<listener-class>com.company.YourListenerClass</listener-class>
</listener>
(you make a class which implements ServletContextListener and implement the contextInitialized() method, where you place your initialization code)
All beans can have an init-method. See the documentation. I suppose that the best practice will be to use this method for every bean you define. A bean can have references to other beans if this is required.