Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
Spring-MVC.xml
<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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.ws.webp.controller" />
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8value>
list>
property>
bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" />
list>
property>
bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1" />
bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<property name="maxUploadSize" value="10485760000" />
<property name="maxInMemorySize" value="40960" />
bean>
<mvc:annotation-driven/>
<context:annotation-config/>
<mvc:resources mapping="/script/**" location="/script/" />
<mvc:resources mapping="/app/**" location="/app/" />
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/images/**" location="/images/" />
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messagevalue>
property>
<property name="defaultEncoding">
<value>UTF-8value>
property>
bean>
beans>
注释很清楚。
<mvc:resources mapping="/script/**" location="/script/" />
<script type="text/javascript" src="<%=request.getContextPath() %>/script/jquery-1.9.1.js">script>
注意:在使用SpringMVC框架的时候配置了静态资源访问<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />后,发现访问页面出现404错误,注意是访问页面404,并不是静态资源访问404,后来发现我的spring-servlet.xml配置文件中的启用spring mvc 注解使用的是<context:annotation-config />,后来添加<mvc:annotation-driven/>后访问成功!
web.xml 配置:
<servlet>
<servlet-name>SpringMVCservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
<async-supported>trueasync-supported>
servlet>
<servlet-mapping>
<servlet-name>SpringMVCservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1" />
bean>
当处理器返回“index”时,InternalResourceViewResolver解析器会自动添加前缀和后缀:/WEB-INF/jsp/index.jsp
注意:这里的order表示视图解析的优先级,数目越小优先级越大(即:0为优先级最高,所以优先进行处理视图),
InternalResourceViewResolver在项目中的优先级必须设置为最低,也就是order要最大。不然它会阻碍其他
视图解析器。为什么呢?
解释如下:
我们知道,当处理器返回逻辑视图时(也就是return “string”),要经过视图解析器链,前面的解析器能处理的,就不会继续往下传播。
如果不能处理就要沿着解析器链继续寻找,直到找到合适的视图解析器
但是对于解析器InternalResourceViewResolver来说,不管能不能解析它都不会返回null,也就是说它拦截了所有的逻辑视图,让后续的解析器得不到执行,所以InternalResourceViewResolver必须放在最后。
代码示例:
@Controller
@RequestMapping("/login")
public class LoginController {
private static Logger logger = Logger.getLogger(LoginController.class);
@Resource
private IPersonService personService;
@RequestMapping("/person")
@ResponseBody
private Object register(Person person){
logger.debug("ws--register-------");
person.setState(0);
return ResponseUtils.sendSuccess(this.personService.registerPerson(person));
}
1、@Controller
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
2、@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping注解有六个属性,下面我们把她分成三类进行说明(下面有相应示例)。
1、 value, method;
value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
2、consumes,produces
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3、params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
3、@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
2)@Resource
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
4、@ModelAttribute和 @SessionAttributes
代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
@SessionAttributes即将值放到session作用域中,写在class上面。
5、@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
6、@requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter(“name”),它有三个常用参数:defaultValue = “0”, required = false, value = “isApp”;defaultValue 表示设置默认值,required 铜过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
7、@ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
8、@Component
相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议。
9、@Repository
用于注解dao层,在daoImpl类上面注解。
注解详情:http://www.cnblogs.com/leskang/p/5445698.html
@RequestMapping("/test")
public String test(Map<String,Object> map,Model model,ModelMap modelMap){
map.put("names", Arrays.asList("caoyc","zhh","cjx"));
model.addAttribute("time", new Date());
modelMap.addAttribute("city", "ChengDu");
modelMap.put("gender", "male");
return "hello";
}
jsp页面
time:${param.time}
names:${param.names }
city:${param.city }
gender:${requestScope.gender }
重定向和转发有一个重要的不同:当使用转发时,JSP容器将使用一个内部的方法来调用目标页面,新的页面继续处理同一个请求,而浏览器将不会知道这个过程。 与之相反,重定向方式的含义是第一个页面通知浏览器发送一个新的页面请求。因为,当你使用重定向时,浏览器中所显示的URL会变成新页面的URL, 而当使用转发时,该URL会保持不变。重定向的速度比转发慢,因为浏览器还得发出一个新的请求。同时,由于重定向方式产生了一个新的请求,所以经过一次重 定向后,request内的对象将无法使用。
怎么选择是重定向还是转发呢?通常情况下转发更快,而且能保持request内的对象,所以他是第一选择。但是由于在转发之后,浏览器中URL仍然指向开始页面,此时如果重载当前页面,开始页面将会被重新调用。如果你不想看到这样的情况,则选择转发。
转发和重定向的区别
不要仅仅为了把变量传到下一个页面而使用session作用域,那会无故增大变量的作用域,转发也许可以帮助你解决这个问题。
重定向:以前的request中存放的变量全部失效,并进入一个新的request作用域。
转发:以前的request中存放的变量不会失效,就像把两个页面拼到了一起。
//1、请求重定向:// 重定向到toList请求
//<1> 不带参数的重定向
//方式一:使用ModelAndView
return new ModelAndView("redirect:/toList");
//方式二:返回String
return "redirect:/ toList";
//<2> 带参数的重定向
//方式一:自己手动拼接url
return new ModelAndView("redirect:/toList?param1="+value1+"¶m2="+value2);
// 弊端:传中文可能乱码
// 方式二:用RedirectAttributes类
//使用addAttribute方法,自动给你拼接url
// 使用方法:
public String save(@ModelAttribute("form") Bean form,RedirectAttributes attr){
...
attr.addAttribute("param", value);
return "redirect:/toList";
}
//在toList方法中可以通过获得参数的方式获取参数
//2、请求转发:// 转发到toList请求
//<1> 不带参数的转发
//方式一:使用ModelAndView
return new ModelAndView("forward:/toList");
// 方式二:返回String
return "forward:/toList";
//<2> 带参数的转发
//方式一:使用ModelAndView
return new ModelAndView("forward:/toList?param1="+value1+"¶m2="+value2");
// 方式二:返回String
return "forward:/ toList?param1="+value1+"¶m2="+value2";
//一
@Controller
public class ManagerController {
@Resource
private ManagerService managerServiceImpl;
@RequestMapping(value = "manager/login.do",method = RequestMethod.GET)
public ModelAndView login(ManagerModel managerModel,HttpSession httpSession){
ManagerModel manager = managerServiceImpl.getManager(managerModel);
if(manager!=null){
manager.setPassword("");
httpSession.setAttribute("manager", manager);
return new ModelAndView(new RedirectView("../admin/main.jsp"));
}else{
return new ModelAndView(new RedirectView("../admin/login.jsp"));
}
}
@RequestMapping(value = "manager/logout.do",method = RequestMethod.GET)
public String logout(HttpSession httpSession){
httpSession.getAttribute("manager");
return "success";
}
}
//二
@Controller
@SessionAttributes("manager")
public class ManagerController {
@Resource
private ManagerService managerServiceImpl;
@RequestMapping(value = "manager/login.do",method = RequestMethod.GET)
public ModelAndView login(ManagerModel managerModel,ModelMap model){
ManagerModel manager = managerServiceImpl.getManager(managerModel);
if(manager!=null){
manager.setPassword("");
model.addAttribute("manager", manager);
return new ModelAndView(new RedirectView("../admin/main.jsp"));
}else{
return new ModelAndView(new RedirectView("../admin/login.jsp"));
}
}
@RequestMapping(value = "manager/logout.do",method = RequestMethod.GET)
public String logout(@ModelAttribute("manager")ManagerModel managerModel){
return "success";
}
}
springmvc.xml:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login/**" />
<bean class="com.lava.lavafaq.springmvc.LoginInterceptor">
<property name="excludeUrls">
<list>
<value>/login/loginHtmlvalue>
<value>/login/loginvalue>
list>
property>
bean>
mvc:interceptor>
mvc:interceptors>
package com.lava.lavafaq.springmvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UrlPathHelper;
public class LoginInterceptor implements HandlerInterceptor {
private static Logger logger = Logger.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
// 不在验证的范围内
String uri = getURI(request);
if (exclude(uri)) {
return true;
}
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
logger.info("Pedirect to login page session username="+username);
if (username == null|| username.length()==0 ) {
logger.info("Pedirect to login page");
response.sendRedirect(getLoginUrl(request,"/login/loginHtml"));
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
/**
* 获得第三个路径分隔符的位置
*
* @param request
* @throws IllegalStateException
* 访问路径错误,没有三(四)个'/'
* http://localhost:8080/lavaFAQ/login/loginHtml
* getURI=/login/loginHtml uri=/lavaFAQ/login/loginHtml ctxPath=/lavaFAQ
*/
private static String getURI(HttpServletRequest request)
throws IllegalStateException {
UrlPathHelper helper = new UrlPathHelper();
String uri = helper.getOriginatingRequestUri(request);
String ctxPath = helper.getOriginatingContextPath(request);
int start = 0;
if (!StringUtils.isBlank(ctxPath)) {
start = ctxPath.length();
}
logger.info("getURI="+uri.substring(start)+" uri="+uri+" ctxPath="+ctxPath);
return uri.substring(start);
}
private boolean exclude(String uri) {
if (excludeUrls != null) {
for (String exc : excludeUrls) {
if (uri.startsWith(exc)) {
return true;
}
}
}
return false;
}
//lavaFAQ---request.getContextPath()
private String getLoginUrl(HttpServletRequest request,String loginUrl) {
StringBuilder buff = new StringBuilder();
if (loginUrl.startsWith("/")) {
String ctx = request.getContextPath();
if (!StringUtils.isBlank(ctx)) {
buff.append(ctx).append(loginUrl);
}
}
logger.info("getLoginUrl="+buff.toString());
return buff.toString();
}
private String[] excludeUrls;
public void setExcludeUrls(String[] excludeUrls) {
this.excludeUrls = excludeUrls;
}
}
//登录请求
@RequestMapping("/login")
private String login(Person person,ModelMap modelMap,HttpSession httpSession){
String result = this.personService.loginPerson(person);
if(result.equals(Canstants.loginSuccess)){
//登录成功存储Session
httpSession.setAttribute("username", person.getMail());
return "homepage";
}
modelMap.addAttribute("result",result);
return "redirect:/login/loginHtml";
}
Spring的Converter可以将一种类型转换成另一种类型。在使用时,必须编写一个实现org.springframework.core.convert.converter.Converter接口的java类。这个接口的声明如下
public interface Converter {
T convert(S var1);
}
//这里的S表示源类型,T表示目标类型。下面展示了一个将String类型转换成Date类型的Converter
public class DateConverter implements Converter<String, Date> {
public static final DateFormat DF_LONG = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
public static final DateFormat DF_SHORT = new SimpleDateFormat("yyyy-MM-dd");
public static final DateFormat DF_YEAR = new SimpleDateFormat("yyyy");
public static final DateFormat DF_MONTH = new SimpleDateFormat("yyyy-MM");
/**
* 短类型日期长度
*/
public static final int SHORT_DATE = 10;
public static final int YEAR_DATE = 4;
public static final int MONTH_DATE = 7;
@Override
public Date convert(String text) {
text = text.trim();
if (!StringUtils.hasText(text)) {
return null;
}
try {
if (text.length() <= YEAR_DATE) {
return DF_YEAR.parse(text);
} else if (text.length() <= MONTH_DATE) {
return DF_MONTH.parse(text);
} else if (text.length() <= SHORT_DATE) {
return DF_SHORT.parse(text);
} else {
return DF_LONG.parse(text);
}
} catch (ParseException ex) {
ex.printStackTrace();
}
return null;
}
}
为了使用Spring MVC应用程序定制的Converter,需要在配置文件中添加一个conversionService bean。Bean的类名称必须为org.springframework.context.support.ConversionServiceFactoryBean。这个bean必须包含一个converters属性,它列出要在应用程序中使用的所有定制Converter。
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.lava.lavafaq.springmvc.DateConverter" />
list>
property>
bean>
之后,还需要给annotation-driven元素的conversion-service属性赋上bean名称,如下
<mvc:annotation-driven conversion-service="conversionService" />
Formatter和Converter一样,也是将一种类型转换成另一种类型。但是,Formatter的源类型必须是一个String。不在追述。