Is it possible to have one controller to handle multiple forms in spring?
For example, i have 3 steps registration that map with "/register" url. Is it possible to have only one RegisterController that can handle all registration's steps form submit?
That depends on the style of controllers you're using. If you use Spring 2.0-style form controllers (e.g. a subclass of SimpleFormController or AbstractFormController), then you might have some difficulty. If, however, you're using Spring 2.5-style annotated #Controllers, then these are very flexible, and you should be able to handle pretty much any cmplexity you desire in one controller class.
Like skaffman said you can use #Controller that are very flexible. But if you're writting a Wizard, you should take a look at AbstractWizardFormController. It handles all steps and validation of a wizard. It also keeps the same backing object for each form submission. When your wizard is on the final action, you can take this object and update it at the database.
You can find an exemple at :
http://www.cs.bham.ac.uk/~aps/syllabi/2004_2005/issws/h04/spring.html#AbstractWizardFormController
you can use multi action controller .In Spring Single Controller Handel Multipal Method by method name resolver
<bean id="id" class="controller class"> <property name="name"><ref bean="service Name"/></property > <property
name="methodNameResolver"><ref bean="name of Resolver"/></property>
</bean>
------------------------------------------------------------------------ <bean id="name of Resolver"
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings"> <props> <prop
key="/url.com">MethodName</prop> </props> </property> </bean> This can
be Help you use single controller
------------------------------------------------------------------------
https://jira.springsource.org/browse/SPR-5534
Question
For example, i have 3 steps registration that map with "/register" url. Is it possible to have only one RegisterController that can handle all registration's steps form submit?
Proposed Solution:
Spring 3 - Spring-mvc controller using servlet mapping to your controller. Using the mapping in your url request you will always go to the same controller.
You can create a servlet & servlet mapping that maps to your controller through the spring DispatcherServlet.
Create a servlet & servlet mapping to handle your requests.
Web.XML
<servlet>
<description>
</description>
<display-name>TestServ</display-name>
<servlet-name>main</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping>
Create a spring config file associated to the servlet.
In this case since the name of my servlet is test, it will be test-servlet.xml file that goes into your WEB-INF folder at the root.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean name="/registration/*" class="register.ResgisterController"/>
Create your controller class to map your requests.
package registration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import registration.User;
#Controller
public final class RegistrationController {
public RosterController() {
}
//Spring lets you You can access your spring mvc model from the controller automatically
#RequestMapping
public void list(Model model) {
//do something
}
//Extract a registration id from the request and you can use in your model
#RequestMapping
public void member(#RequestParam("registrationId") Integer id, Model model) {
//do something
}
}
you will have to use 'MultiActionController', but there are some complication in validation porting if you will use this controller.
Solution of similar problem is given # http://www.scribd.com/doc/20156448/Multi-Action-Controller-With-Validation
you can use Spring webFlow and annotate your controller as #controller
Related
I am studying for the Spring Core certification and I have some doubts related this question:
What is the #Controller annotation used for? How can you create a
controller without an annotation?
So I know that the #Controller annotation indicates that a particular class serves the role of a controller. The #Controller annotation acts as a stereotype for the annotated class, indicating its role. The dispatcher scans such annotated classes for mapped methods and detects #RequestMapping annotations.
So a controller class is something like this:
#Controller
public class AccountController {
#RequestMapping("/listAccounts")
public String list(Model model) {...}
}
}
Ok, this is pretty clear for me but what exactly means create a controller without an annotation? How can I do it? By XML configuration or how?
Tnx
public class AccountController extends MultiActionController {
public ModelAndView listAccounts() {
// your code
return new ModelAndView("listAccounts.bean", "msg", "As you want")
}
}
web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.bean</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml
<bean name="/listAccounts.bean" class="p1.AccountController"></bean>
I've come across this: Spring MVC 3.1 without annotations?
It would seem that you can actually create a controller without annotations (I've been working with Spring for little over a year and haven't encountered such a scenario, I don't know why one would want to do this, apart from certification questions of course) and the way to do it is by using a special configuration in the dispatcher-servlet XML file.
Just to comment the reasons why someone would like to configure Spring programatically or throuth XML, it is because it takes some to scan all the files looking for the annotations during runtime, so if we disable the scan and configure it manually the application will be available to service requests much faster and that is very important on high demand sceneraios.
Is there any way to remove .action/.html/(.any) extension in Spring MVC like Struts2.
I'm looking for a clean URL in my address bar.
Like: http://localhost:8080/SpringProject/Dashboard
Rather than: http://localhost:8080/SpringProject/Dashboard.html
Solution That I have found :
Added two line in my spring-servlet.xml file
<mvc:default-servlet-handler/>
<mvc:annotation-driven />
and modify my web.xml
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
that's all
I also post this answer in my blog javalink2me.blogspot
The support for #Controller and #RequestMapping is provided by Spring by default. However, by enabling mvc:annotation-driven you get support for processing the requests that are mapped to annotated controller methods, such as declarative validation, formatting and conversion service.
In servlet-config.xml
<mvc:default-servlet-handler/>
<mvc:annotation-driven />
In WEB.xml
<servlet-mapping>
<servlet-name>home</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
You just need to configure your controller with the path you want.
#Controller
public class MyController {
#Inject
private MyService myService;
#RequestMapping("/mypath")
public String showPathWithouteExtension(Model model) {
return "myjspfile";
}
}
With a controller like this, you just need to call.
http://server:port/myapp/mypath
and boom, no extension. :)
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've been trying to get Spring to inject an #Autowired dependency into my application without avail. What am I doing wrong?
I created a bean called TimeService. Its job is to return the current time to anyone that asks.
package com.foxbomb.springtest.domain.time;
import java.util.Date;
import org.springframework.stereotype.Service;
#Service
public class TimeService {
public TimeService() {
super();
System.out.println("Instantiating TimeService...");
}
public Date getTime() {
return new Date();
}
}
Of course, I need to tell Spring about this, so I added the following to web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Great, and some Spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config base-package="com.foxbomb.springtest.domain" />
</beans>
Now all we need is a calling class that would like to use this dependency. Unfortunately, the #Autowired here seems to do nothing:
package com.foxbomb.springtest;
import...
#Configurable
public class TimeManager {
public TimeManager() {
super();
System.out.println("Instantiating TimeManager...");
}
#Autowired
private TimeService timeService;
public Date getTime() {
return timeService.getTime();
}
}
And lastly, a JSP that wants to display the time:
<%#page import="com.foxbomb.springtest.ApplicationContextImpl"%>
<%#page import="com.foxbomb.springtest.TimeManager"%>
<html>
<head>
<title>Spring Test</title>
</head>
<body>
<h1>Autowired Dependencies....</h1>
<p>
Time is now <%=new TimeManager().getTime()%>!
</p>
</body>
</html>
But all I get is:
java.lang.NullPointerException
com.foxbomb.springtest.TimeManager.getTime(TimeManager.java:26)
When you access TimeManager object like this:
Time is now <%=new TimeManager().getTime()%>!
Spring does not know anything about this class. You are basically creating a new object, that's it!
Probably Spring creates an instance of TimeManager and injects dependencies properly (however you should use #Service rather than #Configuration annotation), but you are not using this instance in your JSP. Instead you are creating new, unmanaged instance, that is completely independent and unaware of Spring and dependencies...
I think this will work:
<%= WebApplicationContextUtils.
getWebApplicationContext(application).
getBean(TimeManager.class).
getTime() %>
Ugly? Sure. Because the whole approach of accessing services from JSP (view) is ugly. You should at least have a servlet that accesses Spring beans and puts result in request attributes. These attributes can then be accessed in JSP that you forward to - without ugly scriptlets.
If you really want to do this "the right" way, try Spring MVC or other popular Java web framework.
The #Autowire annotation in TimeManager was not recognized, because you told spring to start searching for annotation configuration information in the wrong package.
TimeManager is in com.foxbomb.springtest.
You have this configuration:
<context:annotation-config base-package="com.foxbomb.springtest.domain"/>
Notice that "com.foxbomb.springtest != com.foxbomb.springtest.domain".
Change your configuration to include this:
<context:annotation-config base-package="com.foxbomb.springtest"/>
Edit: Spring must instantiate TimeManager.
You will need to:
Have TimeManager be a data member of the controller class.
#Autowire TimeManager into the controller class (or get it from spring somehow).
Add the TimeManager to some appropriate scope (maybe session, maybe request, maybe application).
Access this TimeManager on your jsp page. Maybe something like this: It is now ${MyTimeManager.time} balonia watch time.
I tend to agree with #TomaszNurkiewicz; use a framework. However, a framework is way overkill for a lot of applications. If it's overkill for you, this answer details how to do it without all the manual work of getting the bean yourself. IMO doing it manually defeats the purpose.
You should be using #Component (or a child stereotype annotation) rather than #Configuration, and annotating your TimeManager with that. #Configuration is used for Java based container configuration, which is not what you are doing here.
Finally!
I got it working with the code in the above example. The trick was to set up Aspect/J and Weaving. This allows the #Configurable annotated class to automatically realize that its dependent on Spring to inject dependencies.
There wasn't much wrong with the base package :) I accidentally made a xml typo, trying to add the base-package attribute to the context:annotation-config tag. The correct definition was:
<context:annotation-config/>
<context:component-scan base-package="com.foxbomb.springtest"/>
I need actually eventually need to change it to com.foxbomb.springtext to include the class that was trying to access the bean too - as in the end it's also annotated.
Thanks everyone for your help!
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.