SpringMvc是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型,试图,控制器
三层架构简介
M(Model)模型:应用程序的核心功能,管理这个模块中用的数据和值
V(View)试图:试图提供模型的展示,管理模型如何显示给用户,它是应用程序的外观
C(Controller)控制器:对用户的输入做出反应,管理用户和视图的交互,是连接模型和视图的枢纽
那么引入SpringMvc主要解决了什么问题?
映射请求路径,封装参数,处理统一异常,多样的结果集视图,国际化,SpringMvc标签等功能
那么springMvc运行原理是什么呢?
1.客户端发送请求url到前端控制器
2.前端控制器将url交给处理器映射器handlermapping解析url(读取mvc配置文件),返回控制器对象controller
3.前端控制器将控制器对象controller交给处理器适配器handleradpter定位控制器,执行方法,返回模型视图modelandview
4.前端控制器将模型视图交给视图解析器ViewResolver进行解析,返回view
5.前端控制器将视图渲染,返回页面(将数据存储到request作用域)
6.前端控制器将页面返回客户端
了解了springmvc执行原理,那么初始化SpringMvc
1.下载支持包并添加jar包支持:
2.在项目src目录下添加context-mvc.xml
3.在web.xml配置web容器初始化SpringMvc框架
创建controller,控制层必须要实现Controller接口
在配置文件中需要配置如下信息
<?xml version="1.0" encoding="UTF-8"?>
<!-- 添加约束 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置处理器映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 配置处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
<!-- 配置自定义控制器 -->
<bean class="com.springmvc.day01.mycontroller1" name="/mycontroller1" />
<!-- 测试路径:http://127.0.0.1:8080/SpringMvc/mycontroller1 -->
</beans>
可见代码量也是不少的,也比较麻烦
在springmvc中启用注解会方便很多,那么在如何开启注解呢
首先在配置文件中开启注解
<!-- 开启注解 -->
<mvc:annotation-driven/>
再扫描配置包下的所有类
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.springmvc.day01"/>
最后配置视图结果集
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" ><!-- 模板解析器 默认jsp格式 -->
<!-- <property name="viewClass" value=""/> --><!-- 前置 -->
<property name="prefix" value="/WEB-INF/view/" /><!-- 后缀 -->
<property name="suffix" value=".jsp" /></bean>
那么对应的controller就可以写为
在指定前缀目录下创建jsp页面
运行后测试即可。
XML形式配置与注解形式对比
xml形式配置缺点:
1.代码量大
2.局限性,只能实现接口Controller重写一个方法
注解形式:
1.代码量小
2.添加注解Controller的类中可以自定义方法,只需要在方法上添加注解@RequestMapping
3.自定义返回值类型:ModelAndView,String,void
4.视图解析器规定解析方法:在xml中配置的前后缀
5.重定向|转发可以跳过解析规则:forward:|redirect:
既然开启了注解,那么介绍一下SpringMvc中那些常用的注解
@Controller表示一个Bean让Spring托管,与xml中的<bean />一样。
参数说明:value或者默认参数:配置Bean的名称与Bean节点中的id一致
@RequestMapping为配置请求的url路径
参数说明:path或者默认值:配置url访问路径
method:配置请求的方式,值为RequestMethod枚举的值,一般浏览器只支持GET/POST
produces:响应数据的格式。html格式为:text/html;charset=utf-8
json格式为:text/json;charset=utf-8
注意:如果@RequestMapping配置在方法上则为方法请求路径,如果配置在类上则在该类的所有方法请求访问时都需要加上这个类的url
@RequsetParam绑定一个参数到方法的参数中
参数说明:name或默认参数:绑定到当前参数的请求参数名称
required:是否为必须的请求参数,如果配置为true则请求不包含该参数会报错,flase则不会
defaultValue:如果请求值为空则将属性后的值设置为默认值
@CookieValue绑定一个Cookie值到方法传入参数
参数说明:Value:Cookie名称
Required:是否为必须的参数
举例:绑定CookieId到输入参数@CookieValue("jsessionid")
@RequestHeader绑定一个请求头部参数到方法的参数中
借助这些注解可以简单快速的开发SpringMvc应用,访问页面时可能有不同的语言要求,实现这个要求就需要配置国际化支持
添加国际化支持
1.加载Spring上下文对象支持国际化和监听器
<!-- 国际化,配置上下文 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:Spring-gjh.xml</param-value>
</context-param>
<!-- 开启国际化支持,配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2.在Spring-gjh.xml中添加国际化支持
<!-- 配置bean,id值不能修改 org.springframework.context.support.ResourceBundleMessageSource.class-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 添加属性 -->
<!-- 配置语言包名称 -->
<property name="basename" value="message"></property>
<!-- 如果在国际化资源文件(语言包)中找不到对应代码的信息,就用这个代码作为名称 -->
<property name="useCodeAsDefaultMessage" value="true"/>
</bean>
3.添加国际化文件:文件名必须这么写
message.properties:默认语言包
message._zh_CN.properties:中文语言包
message_en_US.properties:英文语言包
4.在页面上使用Spring提供的标签库显示国际化
在页面上属性内容时:
<spring:message code="key1" /> //code为国际化文件中的key值
页面国际化显示基本信息完成,那么对数据校验以及报错后也是要显示错误信息国际化的,则需要后台校验
1.在配置文件中启用校验
<!-- 开启注解驱动 -->
<mvc:annotation-driven validator="myValistor"/>
<!-- 校验器 -->
<bean id="myValistor" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器供应商 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
<!-- 添加支持国际化属性 引用国际化配置bean节点-->
<property name="validationMessageSource" ref="messageSource" />
</bean>
自定义校验规则并分组校验
自定义校验器类,实现校验器接口:注解要校验成的类型:实现接口ConstraintValidator(注解,"注解要校验成的类型")
手机号正则: String regex= "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$";
身份证15位:String regex = "^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{2}$";
身份证18位:String regex = "^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
并自定义两个注解@Phone,@Sfz
package com.springmvc.day05;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Documented
@Constraint(validatedBy={SfzValidator.class}) //指定注解校验类
@Target(value={java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD,java.lang.annotation.ElementType.ANNOTATION_TYPE,java.lang.annotation.ElementType.CONSTRUCTOR,java.lang.annotation.ElementType.PARAMETER})
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME) //生命周期
@ReportAsSingleViolation
@NotNull
@Size(min=(int) 1)
public @interface Sfz {
//添加属性
String message() default "身份证输入有误";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
package com.springmvc.day05;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* 自定义注解
* @author ljf
*添加元数据 针对于注解的注解
*在@Constraint中添加校验器类
*/
@Documented
@Constraint(validatedBy={PhoneValidator.class}) //指定注解校验类
@Target(value={java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD,java.lang.annotation.ElementType.ANNOTATION_TYPE,java.lang.annotation.ElementType.CONSTRUCTOR,java.lang.annotation.ElementType.PARAMETER})
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME) //生命周期
@ReportAsSingleViolation
@NotNull
@Size(min=(int) 1)
public @interface phone {
String message() default "手机号码输入有误";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
在对应的实体类属性上添加校验规则
@NotEmpty,不能为控
@Size(min=下限,max=上限)
@Email regex:正则表达式
已将配置了校验器支持国际化,那么错误信息即为语言包中的key,考虑到页面中不同字段要求不同,就可以将校验规则进行分组,那么在校验时就只需要采用某一组进行校验方便我们书写
首先声明两个组
package com.springmvc.day05;
public interface Group1 {
}
package com.springmvc.day05;
public interface Group2 {
}
只需要将校验规则分入对应的组即可,在校验规则中添加属性groups="{Group2.class,Group1.class}"
在Controller中校验页面数据必须有一个对象,在类对象之前加注解@Validated,标识这个参数需要校验,对其进行绑定
向叶面中添加输出错误信息
<!-- 输出校验错误信息 -->
<c:forEach items="${errors }" var="s">
${s.defaultMessage }
</c:forEach>
文件上传
1.SpringMvc支持文件上传
2.在controller方法中获取上传的文件
Restful格式路径
REST指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是Restful
格式对比:
非Restful风格url:http://localhost:8080/projectName/login.jsp?username=jf&passwd=121
Restful风格url:http://localhost:8080/projectName/login.jsp/jf/121
注意:使用Restful需要将web.xml中<url-pattern>修改
<url-patttern>/</url-pattern>
使用SpringMvcRestful风格路径
package com.springmvc.day06;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
* restful
* 风格
* 原始url: 协议://服务器地址:端口号/path?参数1=值1&参数2=值2
* restfulurl:协议://服务器地址:端口号/path/{值1}/{值2}
*
* 步骤:
* 1.将前端控制器url改为“/”
* 2.编写控制器实现restful
*
* @author ljf
*
*/
@Controller
@RequestMapping("restfulcontroller1")
public class RestfulController {
/*
* 原始url
* 传递两个参数
*
*/
@RequestMapping("restful1")
public String restful1(String name,Integer age){
System.out.println(name+age);
return "forward:/index.jsp";
}
/*
* restfulurl
* 传递两个参数
*
*/
@RequestMapping("restful2/{name}/{age}")
public String restful2(@PathVariable(value="name") String m,@PathVariable(value="age") Integer g){
System.out.println(m+g);
return "forward:/index.jsp";
}
}
可以在映射路径时增加多层目录,如果想要截取指定目录的值可以直接在对应的位置写入表达式:
{/路径参数名称},然后使用@PathVariable来获取路径中携带的参数
SpringMvc拦截器
springmvc的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
拦截器与过滤器的区别:
1.过滤器基于Servlet,运行原理是函数回调;拦截器是基于框架,运行原理是反射。
2.过滤器几乎过滤所有请求;拦截器只能拦截控制器方法url.
3.过滤器在初始化时只执行一次;拦截器在控制器生命周期间可以多次执行
自定义拦截器实现接口HandlerInterceptor
public class MyIntercepter implements HandlerInterceptor{}
拦截器中的方法
/***该方法在执行Controller的方法之前执行,如果返回值true则继续往下执行,如果返回 为false则打断执行*/
public boolean preHandle(...) throws Exception {
/*** 该方法会在执行Controller之后,在渲染视图之前执行,所有我们可以对ModeAndView进行操作*/
public void postHandle(......)throws Exception{
/*** 该方法在Controoler之后,在视图渲染完成后执行,所以我们可以在此处清理渲染资源,或者捕获系统当中发生的异常。*/
public void afterCompletion(......) throws Exception
在配置文件中配置拦截规则
<mvc:interceptors>
<!--外部拦截器,会拦截所有请求;一般不用-->
<bean></bean>
<!--内部拦截器-->
<mvc:interceptor>
<!--配置拦截路径-->
<mvc:mapping path="/**"/>
<!--配置放行路径-->
<mvc:exclude-mapping path="/myin1/get1"/>
<!--自定义拦截器-->
<bean class="com.springmvc.day07.myInterceptor1" />
</mvc:interceptor>
</mvc:interceptors>
拦截器应用场景
1.日志记录,记录请求信息的日志,以便进行信息监控信息统计,计算page view等
2.权限检查,如登录检测,进入处理器检测是否登录,如果没有直接返回登录页面
统一异常处理
HandlerExceptionResolver,SpringMvc中提供的统一异常处理器,只要实现这个接口就是统一异常处理器。
首先自定义一个异常,继承Exception
接着创建一个统一异常处理类并在配置文件中配置bean
/**
* 统一异常处理类
* 实现接口HandlerExceptionResolver
* @author ljf
*
*/
public class ExceptionController implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception exception) {//形参中的execption就是在各层捕获到的异常
//创建自定义异常
MyException me = null;
//对捕获到的异常进行判断,查看是否属于自定义异常
if(exception instanceof MyException){
//转换为自定义异常类型
me = (MyException) exception;
}else{
//获取异常信息,将异常信息传入自定义异常类中的属性message
String message = exception.getMessage();
me = new MyException(message);
}
//将获取到的异常信息通过modelandview存入作用域、页面
ModelAndView mv = new ModelAndView();
mv.addObject("error", me.getMessage());
mv.setViewName("view7");
return mv;
}
}
<!-- 异常处理器 -->
<bean class="com.springmvc.day08.ExceptionController"/>
在Controller方法中抛出异常
SpringMvc静态资源
SpringMvc不过滤静态文件,pringmvc拦截带来的问题:我们在配置web.xml时,将拦截的路径设置为‘/’则会拦截所有的访问路径,包括静态资源也会拦截,那就无法访问静态资源。
解决方式1:
重新配置DispatherServlet拦截为*.do(*.html)这样的URL,就不存在访问不到静态资源的问题。
解决方式2:
使用SpringMvc配置文件不拦截静态文件
配置静态资源不过滤
<mvc:resources mapping="/Images/**" location="/Images/"/>
<!--location表示webapp下包的所有文件;mapping表示以/static开头的所有访问请求 -->
注意:当location在WEB-INF中时,会遇到权限问题 需要在servlet.xml中<Context>标签中添加虚拟路径