Spring MVC的实现包括 实现Controller类和基于注解的Controller RequstMapping方式
依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.1.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.1.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency>
基于Controller接口的MVC
这里用到了一个通用接口Controller, Controller的实现类将作为spring的bean被加载到classpath中,在配置文件中定义uri 和bean之间的对应关系。 配置文件的加载在Web.xml中指定 同时Web.xml还负责定义全局的Dispatcher Servlet来接受根 /的 所有请求。
依赖:
1. Controller接口:
Package: org.springframework.web.servlet.mvc;
方法: ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
实现:
public class ProductInputController2 implements Controller { // GET 方法 返回表单 /// 这里返回ModelAndView的示例 参数代表JSP的名字 public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { return new ModelAndView("ProductForm"); } } public class ProductSaveController2 implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { productForm form = new productForm(0,httpServletRequest.getParameter("name"), Float.parseFloat(httpServletRequest.getParameter("price"))); product p = new product(form.get_id(),form.get_name(),form.get_price()); // 这里设置request的对象参数。 将来会在ProductDetails的view中用于显示对象属性 return new ModelAndView("ProductDetails","product",p); } }
2. 配置文件:
上面定义了两个controller的行为和数据,那么下一步就是定义请求uri和Controler的对应。这里在配置文件中的bean定义来实现
配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> /// 对应的Controller此处定义为Bean 对应的Name为path. <bean name="/product_input.action" class="controller.ProductInputController2"></bean> <bean name="/product_save.action" class="controller.ProductSaveController2"></bean> /// View Resolver使的我们在定义ModelAndview()实例的时候不用再指定path和后缀 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
3. 配置文件的加载:
通过Web.xml 中自定义DispatcherServlet的方式来实现。
Servlet: org.springframework.web.servlet.DispatcherServlet 专门用来处理根请求的分发
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>controllerServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>controllerServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
基于注解的MVC
基于注解的MVC主要解决的问题是一个文件定义一个controller(@Controller)多个request uri(@RequestMapping)。
并且uri和handlerRequest的影射关系不再需要在配置文件中指定。 在Controller的方法中就指定就可以了。
当然controller还是作为bean还是要借助配置文件被加载到classpath中的。类似于<context: component-scan />会加载所有controller文件并且是单例的
这里介绍一个例子:
1. Model: 这里定义一个将用于UI 上现实的Model(注意这个 Model的属性名称 必须和UI 上的控件Name对应,这样能实现对象与UI 的绑定)
public class productForm { private int id; private String name; private float price; public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public productForm(int id, String name, float price){ this.id =id; this.name =name; this.price =price; } public productForm(){} }
2. View: 输入jsp 页面定义了上面Model对应的组建(通过Name属性)。这样在POST 方法提交后可以在Controller方法的参数中获取对象
用于输入的表单
<!DOCTYPE HTML> <html> <head> <title>Add Product Form</title> <style type="text/css">@import url(css/main.css);</style> </head> <body> <div id="global"> <form action="product_save.action" method="post"> <fieldset> <legend>Add a product</legend> <p> <label for="name">Product Name: </label> <input type="text" id="name" name="name" tabindex="1"> </p> <p> <label for="description">Description: </label> <input type="text" id="description" name="description" tabindex="2"> </p> <p> <label for="price">Price: </label> <input type="text" id="price" name="price" tabindex="3"> </p> <p id="buttons"> <input id="reset" type="reset" tabindex="4"> <input id="submit" type="submit" tabindex="5" value="Add Product"> </p> </fieldset> </form> </div> </body> </html>
用于确认信息的表单
<!DOCTYPE HTML> <html> <head> <title>Save Product</title> </head> <body> <div id="global"> <h4>The product has been saved.</h4> <p> <h5>Details:</h5> Product Name: ${product.name}<br/> Price: $${product.price} </p> </div> </body> </html>
3. Controller:@Contoller注解用于定义Controller bean, 这个属性用于在配置文件的 <context: component-scan />
@RequestMapping()用于定义请求地址和handler之间匹配。 Value=""用于标示请求的uri. Method用于标示请求的方式
GET and POST
import model.product; import model.productForm; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Created by ygshen on 7/18/16. */ @Controller public class ProductController { private static Log logger = LogFactory.getLog(ProductController.class); // http://localhost:8080/springmvc/product_form2.action的时候将访问到这个method @RequestMapping(value = "/product_form2.action") public String productForm(){ logger.info("Request the product input form "); return "ProductForm"; } // http://localhost:8080/springmvc/product_save.action的时候将访问到这个method // 这里的参数 productForm能够自动根据输入表丹的属性初始化对象。 Model用于传递对象给下一个View。 类似与Request.setAttribute("product",p). @RequestMapping(value = "/product_save.action", method = {RequestMethod.POST, RequestMethod.PUT}) public String saveForm(productForm form, Model model){ logger.info("request the product save post method"); logger.info("Product Name: "+ form.getName()+ ". Product price: "+ form.getPrice()); product p = new product(form.getId(),form.getName(),form.getPrice()); model.addAttribute("product",p); return "ProductDetails"; } }
4. Controller的发现:
之前使用的<bean></bean>定义一个类是bean ,这里因为有@Controller 的存在已经证明是bean了,需要作的是扫描到他。
<context:component-scan /> 就是这个作用。
<?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:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="web.controller" /> <mvc:annotation-driven></mvc:annotation-driven> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
4. Controelr配置文件的定义:
web.xml中定义DispactcherServlet处理所有请求,并且Servlet中指定配置文件。 内容就是3中指定的。
Formatter、Converter
1. Converter接口
public interface Converter<S, T> { T convert(S var1); }
S是原数据类型,T是转换目标
如 日期输入框输入的都是字符串类型,Converter可以负责自动转换成日期类型 而不必在Controller里用代码判断和转换
public class StringToDateConverter implements Converter<String, Date> { public Date convert(String dateString) { try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); simpleDateFormat.setLenient(false); return simpleDateFormat.parse(dateString); } catch (ParseException exception){ throw new IllegalArgumentException("Invalid date formate"+ datePattern); } } private String datePattern; public StringToDateConverter(String _datePattern) { datePattern=_datePattern; } }
<form:errors path="publishTime" cssClass="error"></form:errors>
<label for="publishTime">PublishTime:</label>
<form:input path="publishTime" id="publishTimes" type="text"></form:input>
这里Input输入的是字符串,但是因为Converter的存在会自动实现字符串转成目标(Path=publishTime在Model中的属性被定义成 Date类型)类型。
前提是Converter必须在 configure文件中注册了
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="Formatters.StringToDateConverter"> <constructor-arg type="java.lang.String" value="MM-dd-yyyy"></constructor-arg> </bean> </set> </property> </bean> <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
2. Formatter: 作用和Converter相同,但是 元数据类型只能是String.
public interface Formatter<T> extends Printer<T>, Parser<T> { }
另外 Formatter的注册方式如下
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="formatters"> <set> <bean class="Formatters.StringToDateConverter"> <constructor-arg type="java.lang.String" value="MM-dd-yyyy"></constructor-arg> </bean> </set> </property> </bean> <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>