I am integrating Spring MVC into an existing project I have been working on. By integrating, I mean I am rewriting the project using Spring, and using much of my old code. I have already setup the environment and have began working on it. I will refer to this project as ProjectX.
I have already setup and configured my ProjectX-servlet.xml that holds the view-resolver bean, and the controller beans, etc. I want to set up an applicationContext.xml file that I can place all my DAO beans in such as ...
<bean id="MemberDAO" class="com.xxx.xxx.MemberDAO"/>
<bean id="ProductDAO" class="com.xxx.xxx.ProductDAO"/>
I want these values to be in the applicationContext.xml so that in my controllers I can do the following.
public SomeController extends SimpleFormController{
private MemberDAO memberDao;
private ProductDAO productDao;
...getter/setter methods for memberDao;
...getter/setter methods for productDao;
and the values will be available(injecting them into the controllers)
I have configured the controllers in the ProjectX-servlet.xml like the following definition.
<bean name="/SomeController.thm" class="com.xxx.xxx.controllers.SomeController">
<property name="memberDao" ref="MemberDAO"/>
<property name="productDao" ref="ProductDAO"/>
</bean>
I believe I need to configure something such as the following in my web.xml so that it knows to load the application context.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
My question is, what do I have to do following creating an applicationContext.xml file, to be able to do what I showed above and inject beans such as the ProductDAO and MemberDAO into my controlellers which are configured in the ProjectX-servlet.xml
I have been using Spring MVC for a contract for a couple months and am comfortable with how to use it, but I am new to configuring it on my own, for my own use, so I would appreciate if any advice or answers were explained a little easier for me.
Thank you
By convention, the name you give to your instance of DispatcherServlet will be associated with {name}-servlet.xml. This context will be a child to applicationContext.xml as you described, meaning it will have access to beans in applicationContext.xml.
Try the following in your web.xml:
<servlet>
<servlet-name>ProjectX</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ProjectX</servlet-name>
<url-pattern>/projectx/*</url-pattern>
</servlet-mapping>
You don't have to do anything special. You can continue injecting beans defined in applicationcontext.xml into the beans defined in xx-servlet.xml as if all of them are declared in same file. Do remember to use the attribute ref instead of ref-local as below.
<bean id="mycontroller" class="x.y.z.CustomerController>
<property name="service" ref="myservice"/><!--myservice defined in applicationcontext-->
</bean>
Unless I'm misunderstanding, the solution you're looking for is to use an import statement in your applicationContext.xml. This effectively combines the two XML files into a single context, allowing you to reference beans in either.
Ex:
<import resource="classpath:foo/bar/ProjectX-servlet.xml" />
You may or may not want to use "classpath." See section 3.2.2.1 in the Spring docs for more details.
Related
I have problem figuring out why My Jersey RESTful entry point can't find the Spring Bean that I configure when the app server starts. It kept getting NullPointerException after trying from
Spring DI - Autowired property is null in a REST service
NullPointerException on #Autowired attribute with Jersey and Spring for REST service
#Autowired is not working with jersey and spring
Integrating both spring mvc and Jersey, getting a null pointer when viewing a jersey endpoint
Jersey 2 + Spring: #Autowired is null
Web.xml
<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>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.testing.resource</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Spring-context.xml
<context:annotation-config />
<context:component-scan base-package="com.testing.config, com.testing.repository, com.testing.workflow" />
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>
Jersey servlet entry point
#Component
#Produces(MediaType.APPLICATION_JSON)
#Path("/{userId}/items")
public class UserResource
{
#Autowired
private UserWorkFlow userWorkFlow;
#GET
public Response getAllItems(#PathParam("userId") String userId)
{
// ** NullPointerException here complains about the UserWorkFlow
return Response.status(200).entity(userWorkFlow.getItemsFor(userId)).build();
}
}
Service layer
I also tried to make an interface for this but it didn't work.
#Service
public class UserWorkFlow
{
#Autowired
private AllItems allItems;
public List<Item> getItemsFor(String id)
{
return allItems.getItemsFor(id);
}
}
Repository and DAO
#Repository
public class AllItems
{
#Autowired
private ItemSql itemSql;
public List<Item> getItemsFor(String id)
{
return itemSql.getAllItemsFor(id);
}
}
MyBatis Mapper (this has a XML associated with it)
public interface UserSql
{
List<Item> getAllItemsFor(#Param("userId") String userId);
}
I also changed to com.sun.jersey from org.glassfish.jersey but didn't work. I am running out of ideas what could be wrong. Can anyone spot what did I do wrong ?
The link I provided for your previous question had links to four fully working examples. You could have easily just grabbed one of the examples and built on top of it.
I will just walk you through one of the examples. Maybe it was too hard to follow. I will use the Jersey 2.x with Spring XML config.
First, make sure you have the dependencies (only showing versions to ones not shown in the pom)
jersey-container-servlet: 2.22.1
spring-web: 3.2.3.RELEASE
commons-logging
jersey-spring3: 2.22.1. (Notice the snapshot project uses jersey-spring*4*. This is not yet released, and will be released in the next Jersey release)
Second, make sure your web.xml is in order
Third, add your applicationContext.xml to the project class-path.
Fouth, the MyApplication class listed in the previous web.xml.
If you follow the example to the T, you will have a working example. It may not be the exact way you want to configure your Spring components, but you will have a working example you can build off of and tweak around to see what works and what doesn't. When you get more comfortable, you can see the other examples (in the first link of this answer) for other configuration styles/options.
I have extended ServiceImpl and DAOImpl classes with SpringBeanAutowiringSupport, It solved my autowired null pointer exception.
I have a root context file (applicationContext.xml) and a child context file (subContext.xml).
I get a NoSuchBeanDefinitionException when I try to access a bean from applicationContext.xml in subContext.xml
subContext.xml bean:
<bean id="myInfo" factory-bean="myInfoFactory"
factory-method="getInstance" scope="request">
<aop:scoped-proxy proxy-target-class="true" />
applicationContext.xml bean:
<bean id="myInfoFactory"
class="org.wcw.factory.MyInfoFactory" />
Error I get is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myInfoFactory' is defined
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
portlet.xml
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/subContext.xml</value>
</init-param>
If I move the myInfoFactory bean declaration from applicationContext.xml to subContext.xml, it's not throwing that exception anymore. Not sure why it's invisible when declared in applicationContext.xml. I cannot permanently move the bean to subContext.xml because of my environment.
MyInfoFactory Class
public class MyInfoFactory {
private Logger log;
#Autowired
private PortletRequest request;
...
public MyInfo getInstance(PortletRequest request) {
}
...
}
The problem with this is not about the configuration itself, but about where you are trying to recover that particular bean.
If you try to inject that request-scoped bean, defined in the child ApplicationContext (subContext.xml) from within a Bean defined in the Parent application context, the one given by the listener (applicationContext.xml) you won't be able to 'see' that particular been.
This relationship works in the other direction. The child can actually see every been defined in the parent container.
One solution could be to move your request proxies to the parent, since they are proxies you can do that. Though, a runtime will be thrown if no request available when you try to use that particular bean.
This is something that I think should be very easy, but so far I have not been able to get it to work.
What I want to do is map my root path to a Spring MVC Controller. With a normal Servlet, I would just add a mapping for "/" in my web.xml, and it would pick it up quite well. But with Spring MVC, not so much.
I have tried many combinations, but none seem to work. I think the following one should work.
In web.xml:
<servlet-mapping>
<servlet-name>myDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
In my contextConfigLocation file:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
<property name="mappings">
<util:map>
<entry key="/" value-ref="rootController"/>
</util:map>
</property>
</bean>
<bean id="rootController" class="my.package.RootController">
Then obviously, there is the case of the controller itself. I have no clue how to map the method to the actual root path. My attempt is something like this:
public class RootController extends MultiActionController {
#RequestMapping("/")
public ModelAndView display(HttpServletRequest request, HttpServletResponse response) throws Exception {
final Map<String, Object> model = new HashMap<String, Object>();
model.put("someVariable", "Hello, MVC world.");
return new ModelAndView("rootPage", model);
}
}
So let's say my application runs on http://localhost:8080/app/, I want that exact URL to execute the method display. I do not want to type anything after /app/. In fact, some things after /app/ are mapped to other controllers, and that all works fine (and they have to keep working).
What am I missing here? Why is this not simply working? If I use the same url-pattern to map to a plain Servlet instance, it works fine and I reach the doGet method, but with Spring MVC I seem to missing some particular black magic to get this to work.
Instead of mapping to / you can declare a welcome page in your web.xml file:
<welcome-file-list>
<welcome-file>welcome.htm</welcome-file>
</welcome-file-list>
so your / path will be processed as /welcome.htm and then if your controller is correctly mapped to /welcome.htm it will process / as if it was a /welcome.htm request, without making changes to other configuration.
I'd recommend getting rid of the SimpleUrlHandlerMapping and just doing the following:
#Controller
#RequestMapping("/")
public class RootController
{
#RequestMapping(method=RequestMethod.GET)
public ModelAndView display(...)
{
...
}
}
This should get the result you want. Also, add <mvc:annotation-driven/> to your servlet context with a <context:component-scan base-package="some.package.path.to.controller" /> to have Spring wire up that controller.
Otherwise, you can probably map the URL with the SimpleUrlHandlerMapping as so:
<property name="mappings">
<value>
/*=rootController
</value>
<property>
If done this way, you can keep the bean defined for rootController.
For Spring Webflow, the suggestion from Boris Treukhov to use web-file-list led to my discovering a hack that worked for weblogic. For example, if welcome is a flow.xml file, do the following: Change the url-pattern from \ to *.html in web.xml. Place a dummy(empty) file welcome.htm into the folder src\main\webapp\WEB-INF. In SimpleUrlHandlerMapping map the value /welcome.htm=flowController. Finally, complete the registry, flow-location path="welcome.xml". Refer to link: http://forum.spring.io/forum/spring-projects/web/50547-welcome-file-list-with-spring.
I would like to use jsf annotations and some spring
annotations to inject a spring bean/service into a jsf managed bean.
(on the jsf bean i only want to use jsf annotations)
I dont want to use annotations like #named / #inject.
I have tried to find a solution on the net but without any luck.
Example
#ManagedBean
#ViewScoped
public class MyBean {
#ManagedProperty(value = "#{mySpringBean}")
private MySpringBean mySpringBean;
public void setMySpringBean(MySpringBean mySpringBean) {
this.mySpringBean = mySpringBean;
}
public void doSomething() {
//do something with mySpringBean
}
}
Is something like this possible without the use of xml. For example,
I would NOT like to use something like
FacesContextUtils.getWebApplicationContext(context).getBean("MySpringBean");
or in faces-config.xml
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>com.mytest.MyBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
<managed-property>
<property-name>mySpringBean</property-name>
<value>#{mySpringBean}</value>
</managed-property>
</managed-bean>
Is something like the above possible with annotations and without
defining all the jsf beans/properties and the spring beans/properties for
every bean in the config xml files?
If you already have Spring container why not use its #Autowired annotation. For that, Update your faces-config.xml as suggested by Boni. Then add these listeners to your web.xml after this
<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>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
Then add these to your applicationContext.xml
<context:component-scan base-package="com.examples" />
Now you can use Spring annotations and your bean will be something like this:
package com.examples;
#Component
#Scope(value="request")
public class MyBean {
#Autowired
private MySpringBeanClass mySpringBean;
}
Annotate your MySpringBeanClass with #Service
See Also:
#Scope("request") not working
Put this code in your faces-config.xml
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
Then in your ManageBean Constructor call;
WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
mySpringBean = ctx.getBean(MySpringBean.class);
MySpringBean mean your Spring Bean Class
Assuming you have configured Spring properly in web.xml and applicationContext.xml.
Make the following entry in faces-config.xml
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
Your sample code given above seems fine. What will happen with above entry is Managed Property will be first looked in beans managed by JSF if not found will be searched in beans managed by Spring. Your spring bean should have proper annotations marked and name given in #ManagedProperty should match with default/name given to bean.
As mentioned by #Boni that is not required it is auto injected. I have used settings as you desire.
A side note: Since you are opting for view scope please have a look at this link The benefits and pitfalls of #ViewScoped
I have a need to change the spring applicationContext.xml file that is used based upon a property, this property MUST be defined somewhere outside of the war file (ie. it can't be in web.xml). Currently, I've arrived at the following solution (see my answer below), wondering if there's a better way to do this?
Have you considered using the beanRefContext approach. (ContextSingletonBeanFactoryLocator). This way you can configure your spring config files (and their names) via another spring config file.
Then you can paramaterise that file by whatever means you deem appropriate and switch the file names that way.
The file looks like this:
<beans>
<bean id="businessBeanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="${NameOfBeanConfigFile}" />
</bean>
</beans>
And you can use PropertyPlaceHolderConfigurer to set the value of NameOfBeanConfigFile.
I like this approach as it means I can mix static bean config file names with dynamic bean config file names and thus don't have to duplicate bean config.
When I had to do a similar thing I would parameterise via a config file loaded as a URL resource (via jndi)
there are 4 parts to my solution. first, in web.xml of my application I define the following:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation1</param-name>
<param-value>classpath:applicationContext-1.xml</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation2</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>com.my.package.MyContextLoaderListener</listener-class>
</listener>
Then I extend ContextLoaderListener
package com.my.package;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
#Override
protected ContextLoader createContextLoader() {
return new MyContextLoader();
}
}
and ContextLoader
package com.my.package;
import javax.servlet.ServletContext;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
public class LnvContextLoader extends ContextLoader {
private static final String APP_CONTEXT_PROP = "MY_CONTEXT_LOAD_PARAM";
#Override
protected void customizeContext(ServletContext servletContext,
ConfigurableWebApplicationContext wac) {
//check for system property first, if not defined, check for env variable
String appContextParam = System.getProperty(APP_CONTEXT_PROP);
if(appContextParam==null)
{
appContextParam = System.getenv(APP_CONTEXT_PROP);
}
if(appContextParam!=null && !appContextParam.equals("")){
String initParam = servletContext.getInitParameter(appContextParam);
wac.setConfigLocation(initParam);
}
}
}
and finally, in my tomcat startup, I define the environment variable in setenv.bat
set MY_CONTEXT_LOAD_PARAM=contextConfigLocation1
this solution loads it from an environment variable, but the code is flexible and allows it to be set in a system property instead.
If you load your application context through the classpath, you can override it by placing another version of the applicationContext.xml in the server's classpath.
My solution would be to have a very simple applicationContext, that includes the real application context :
applicationContext.xml :
<beans>
<import resource="classpath:realContext.xml"/>
</beans>
If you want another context, add an applicationContext.xml to your server's classpath with :
<beans>
<import resource="classpath:realContext2.xml"/>
</beans>
And have realContext.xml and realContext2.xml packaged in your WAR. No need for fancy context listener.
Just my opinion, but I quite dislike to have WARs that are not self contained. I find it very convinient to have a single unit of deployement. So I would prefer to create 2 different versions of my WAR during the build process, one for each needed configuration.
Another solution, if you want to load a different bean depending on a given property, you can use a PropertyPlaceholderConfigurer and put the name of the bean as a property :
<beans>
<bean id="bean1" .../>
<bean id="bean2" .../>
<bean id="otherBean">
<property name="injectDifferentBean" ref="${property.containing.bean.name" />
</bean>
</beans>
and a property file with :
property.containing.bean.name=bean1
or
property.containing.bean.name=bean2