When I previously developed servlet applications without Spring, I could read configuration parameters like this in my servlet:
#Override public void init() throws ServletException {
ServletContext sc = getServletContext();
String someSetting = sc.getInitParameter("someSetting");
}
However, I'm developing an application with Spring MVC now, which means I no longer have a servlet myself, but use Spring's DispatcherServlet.
Now, how can I access those init parameters in spring-servlet.xml to pass them to one of my beans?
I'm using Spring 3, and am a n00b with it. Every time I see a bean without an id, my mind flips XD
I guess you can #Inject a ServletContext in your controllers, and take the params from there.
Of better, use #Value together with a specificPropertyPlaceholderConfigurer. See this answer
Do you really need these to be init-parameters in the Servlet context?
In Spring you can just inject values into your beans from a properties file using PropertyPlaceholderConfigurer.
Related
I am using Spring Boot, and largely just using the autoconfiguration options for most of the components. However, I have found a few instances where I just want slightly different behaviour from the Beans.
What is the best/suggested approach to doing this? In many cases I don't want to have to turn off autoconfig just to change one property on the bean, so hoping there is some way I can sensibly update beans properties?
The case I have is the DispatcherServlet - I am happy with the autoconfig but I just want to change my DispatcherServlet so the DispatchOptionsRequest is set to true. I am hoping I don't need to turn off autoconfig and copy the configuration locally just to call that setter method?
The dispatcher servlet can be configured by declaring a bean of type DispatcherServlet named dispatcherServlet, then return an instance configured to your liking. This will override the previous declaration.
Example:
#Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setDispatchOptionsRequest(true);
return servlet;
}
I am developing a spring MVC application. When I try to use AnnotationConfigApplicationContext in my controller class I am getting the following error. I have no idea what this statement exactly means.
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping)
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
Error Message -->
java.lang.IllegalStateException:org.springframework.context.annotation.AnnotationConfigApplicationContext#116b3c0 has not been refreshed yet
Can someone explain me what went wrong here ? I am using Spring 4.0.1.. I am new to spring mvc.
When you are creating a new instance of an ApplicationContext (regardless which type) you are basically creating new instances of each and every bean configured in that ApplicationContext. That is nice the first time, it might work the second and depending on the amount of beans, the type of beans will crash after that. As the context will never be destroy (until the app crashed and is restarted) you will run into possible memory issues, performance issues, strange transactional problems etc.
A general rule of thumb is to never construct a new instance of an ApplicationContext but to use dependency injection instead.
If you really want access to the ApplicationContext put a field of that type in your controller and put #Autowired on it.
#Controller
public class MyController {
#Autowired
private ApplicationContext ctx;
….
}
Then you can do a lookup for the bean you need in the method. This can be handy if you use the ApplicationContext as a factory for your beans. If all the beans you need are singletons it is better to simply inject the bean you need.
#Controller
public class MyController {
#Autowired
private MappingFileGenerator mfg ;
….
}
Now Spring will inject the MappingFileGenerator and it is available for use in your methods. No need to create a new instance of an ApplicationContext.
More information is in the Spring Reference Guide.
#M.Deinum's comment will get quite a few more upvotes.
Think of creating a new ApplicationContext as instantiating a new (instance of an) application. Do you want to do that every time this (or any other method in said application) is called? No, you don't.
I'm guessing you think you do because you need access to your ApplicationContext in this method. To do that - i.e. to get access to the running application context (rather than creating a new one), you want to do
#Controller // or #Service / #Component / ... : tells Spring that this is a bean, and to inject the specified dependencies
class YourClass {
#Autowired // tells Spring that this object is a dependency should should be injected
ApplicationContext ctx;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping) {
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
The key here is the Autowired annotation, which tells Spring to inject the annotated object as a dependency.
I highly suggest following the links I've included (for starters), as what you're doing here suggests pretty strongly that you haven't wrapped your head around what DI is and does for you, and until you do, using it is likely to be counterproductive toward it's own ends for you.
In case it helps someone, i was having this issue on a new URL Mapping added to a gradle project, i was missing the first slash of the url and that causing this "illegalstate not refreshed yet" on my tests
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 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.
Is there a way to develop portlets with spring without using the DispatcherPortlet? I want to use other technologies for the UI, mainly Vaadin. Spring is used for DI and other stuff. Is there something similar to ContextLoaderListener class in the Portlet side?
Looking at the Spring documentation: You can create an ApplicationContext as follows:
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
Referencing xml files on your classpath.
You can then get beans by calling context.getBean("beanName"). No need for Spring MVC.
I was hoping for a more detailed answer than what Noel gave. Maybe there is some best practice to this? Here is my current solution:
Give the xml file locations as a init parameter to my portlet.
My init method looks something like this
#Override
public void init(PortletConfig config) throws PortletException {
super.init(config);
String configLocations = config.getInitParameter("contextConfigLocation");
ClassPathXmlApplicationContext springContext = new ClassPathXmlApplicationContext();
springContext.setConfigLocation(configLocations);
springContext.refresh();
config.getPortletContext().setAttribute(APPLICATION_CONTEXT_ATTRIBUTE, springContext);
}
Now I can access my applicationContext through PortletContext every time I need.
(ApplicationContext) portalContext.getAttribute(APPLICATION_CONTEXT_ATTRIBUTE);
APPLICATION_CONTEXT_ATTRIBUTE is just string constant that I made up. I'm still open for better solutions.
You can use PortletApplicationContextUtils to retrieve the web application context:
ApplicationContext ctx = PortletApplicationContextUtils.getWebApplicationContext(getPortletContext());
Then you just need to add some configuration on your web.xml.