三层架构
MVC(Model View Controller),一种用于设计创建Web应用程序表现层的模式
Model(模型):数据模型,用于封装数据
View(视图):页面视图,用于展示数据
jsp
html
Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑
①导入SpringMVC相关坐标
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.1.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.9.RELEASEversion>
dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>80port>
<path>/path>
configuration>
plugin>
plugins>
build>
②定义表现层业务处理器Controller,并配置成spring的bean(等同于Servlet)
@Controller
public class UserController {
public void save(){
System.out.println("user mvc controller is running ...");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="com.itheima"/>
beans>
③web.xml中配置SpringMVC核心控制器,用于将请求转发到对应的具体业务处理器Controller中(等同于Servlet配置)
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:spring-mvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
④设定具体Controller的访问路径(等同于Servlet在web.xml中的配置)
//设定当前方法的访问映射地址
@RequestMapping("/save")
public void save(){
System.out.println("user mvc controller is running ...");
}
⑤设置返回页面
//设定当前方法的访问映射地址
@RequestMapping("/save")
//设置当前方法返回值类型为String,用于指定请求完成后跳转的页面
public String save(){
System.out.println("user mvc controller is running ...");
//设定具体跳转的页面
return "success.jsp";
}
xml方式
<context:component-scan base-package="com.itheima">
<context:include-filter
type="annotation"
expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<mvc:resources mapping="/img/**" location="/img/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:default-servlet-handler/>
SpringMVC提供专用的中文字符过滤器,用于处理乱码问题
配置在 web.xml 里面
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
CharacterEncodingFilter
/*
@Configuration
@ComponentScan(value = "com.itheima",includeFilters =
@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = {Controller.class})
)
public class SpringMVCConfiguration implements WebMvcConfigurer{
//注解配置放行指定资源格式
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/img/**").addResourceLocations("/img/");
// registry.addResourceHandler("/js/**").addResourceLocations("/js/");
// registry.addResourceHandler("/css/**").addResourceLocations("/css/");
// }
//注解配置通用放行资源的格式
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();;
}
}
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//创建Servlet容器时,使用注解的方式加载SPRINGMVC配置类中的信息,并加载成WEB专用的 //ApplicationContext对象
//该对象放入了ServletContext范围,后期在整个WEB容器中可以随时获取调用
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfiguration.class);
return ctx;
}
//注解配置映射地址方式,服务于SpringMVC的核心控制器DispatcherServlet
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
//乱码处理作为过滤器,在servlet容器启动时进行配置,相关内容参看Servlet零配置相关课程
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.INCLUDE),false,"/*");
}
}
删除web.xml
删除spring-mvc.xml
小节
基于servlet3.0规范,配置Servlet容器初始化配置类,初始化时加载SpringMVC配置类
转化SpringMVC核心配置文件
转化为注解(例如: spring处理器加载过滤)
转化为bean进行加载
按照标准接口进行开发并加载(例如:中文乱码处理、静态资源加载过滤)
参数名与处理器方法形参名保持一致
访问URL: http://localhost/requestParam1?name=itheima&age=14
@RequestMapping("/requestParam1")
public String requestParam1(String name ,String age){
System.out.println("name="+name+",age="+age);
return "page.jsp";
}
@RequestParam 的使用
类型: 形参注解
位置:处理器类中的方法形参前方
作用:绑定请求参数与对应处理方法形参间的关系
访问URL: http://localhost/requestParam1?userName=itheima
@RequestMapping("/requestParam2")
public String requestParam2(@RequestParam(
name = "userName",
required = true,
defaultValue = "itheima") String name){
System.out.println("name="+name);
return "page.jsp";
}
当POJO中使用简单类型属性时, 参数名称与POJO类属性名保持一致
访问URL: http://localhost/requestParam3?name=itheima&age=14
Controller
@RequestMapping("/requestParam3")
public String requestParam3(User user){
System.out.println("name="+user.getName());
return "page.jsp";
}
POJO类
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
参数冲突
当POJO类型属性与其他形参出现同名问题时,将被同时赋值
建议使用@RequestParam注解进行区分
访问URL: http://localhost/requestParam4?name=itheima&age=14
@RequestMapping("/requestParam4")
public String requestParam4(User user,String age){
System.out.println("user.age="+user.getAge()+",age="+age);
return "page.jsp";
}
复杂POJO类型参数
当POJO中出现对象属性时,参数名称与对象层次结构名称保持一致
访问URL: http://localhost/requestParam5?address.province=beijing
public class User {
private String name;
private Integer age;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
@RequestMapping("/requestParam5")
public String requestParam5(User user){
System.out.println("user.address="+user.getAddress().getProvince());
return "page.jsp";
}
当POJO中出现List,保存对象数据,参数名称与对象层次结构名称保持一致,使用数组格式描述集合中对象的位置
访问URL: http://localhost/requestParam7?addresses[0].province=bj&addresses[1].province=tj
public class User {
private String name;
private Integer age;
private List<Address> addresses;
}
public class Address {
private String province;
private String city;
private String address;
}
@RequestMapping("/requestParam7")
public String requestParam7(User user){
System.out.println("user.addresses="+user.getAddress());
return "page.jsp";
}
当POJO中出现Map,保存对象数据,参数名称与对象层次结构名称保持一致,使用映射格式描述集合中对象的位置
访问URL: http://localhost/requestParam8?addressMap[’home’].province=bj&addressMap[’job’].province=tj
public class User {
private String name;
private Integer age;
private Map<String,Address> addressMap;
}
public class Address {
private String province;
private String city;
private String address;
}
@RequestMapping("/requestParam8")
public String requestParam8(User user){
System.out.println("user.addressMap="+user.getAddressMap());
return "page.jsp";
}
数组类型参数
请求参数名与处理器方法形参名保持一致,且请求参数数量> 1个
访问URL: http://localhost/requestParam9?nick=Jockme&nick=zahc
@RequestMapping("/requestParam9")
public String requestParam9(String[] nick){
System.out.println(nick[0]+","+nick[1]);
return "page.jsp";
}
集合类型参数
保存简单类型数据,请求参数名与处理器方法形参名保持一致,且请求参数数量> 1个
访问URL: http://localhost/requestParam10?nick=Jockme&nick=zahc
@RequestMapping("/requestParam10")
public String requestParam10(@RequestParam("nick") List<String> nick){
System.out.println(nick);
return "page.jsp";
}
注意: SpringMVC默认将List作为对象处理,赋值前先创建对象,然后将nick作为对象的属性进行处理。由于
List是接口,无法创建对象,报无法找到构造方法异常;修复类型为可创建对象的ArrayList类型后,对象可
以创建,但没有nick属性,因此数据为空。此时需要告知SpringMVC的处理器nick是一组数据,而不是一个单
一数据。通过@RequestParam注解,将数量大于1个names参数打包成参数数组后, SpringMVC才能识别该数
据格式,并判定形参类型是否为数组或集合,并按数组或集合对象的形式操作数据。
小节
请求POJO类型参数获取
POJO的简单属性
POJO的对象属性
POJO的集合属性(存储简单数据)
POJO的集合属性(存储对象数据)
名称冲突问题
SpringMVC对接收的数据进行自动类型转换,该工作通过Converter接口实现
标量转换器
StringToBooleanConverter String→Boolean
ObjectToStringConverter Object→String
StringToNumberConverterFactory String→Number( Integer、 Long等)
NumberToNumberConverterFactory Number子类型之间(Integer、 Long、 Double等)
StringToCharacterConverter String→java.lang.Character
NumberToCharacterConverter Number子类型(Integer、 Long、 Double等)→java.lang.Character
CharacterToNumberFactory java.lang.Character→Number子类型(Integer、 Long、 Double等)
StringToEnumConverterFactory String→enum类型
EnumToStringConverter enum类型→String
StringToLocaleConverter String→java.util.Local
PropertiesToStringConverter java.util.Properties→String
StringToPropertiesConverter String→java.util.Properties
集合、数组相关转换器
ArrayToCollectionConverter 数组→集合( List、 Set)
CollectionToArrayConverter 集合( List、 Set) →数组
ArrayToArrayConverter 数组间
CollectionToCollectionConverter 集合间( List、 Set)
MapToMapConverter Map间
ArrayToStringConverter 数组→String类型
StringToArrayConverter String→数组, trim后使用“,”split
ArrayToObjectConverter 数组→Object
ObjectToArrayConverter Object→单元素数组
CollectionToStringConverter 集合( List、 Set) →String
StringToCollectionConverter String→集合( List、 Set), trim后使用“,”split
CollectionToObjectConverter 集合→Object
ObjectToCollectionConverter Object→单元素集合
默认转换器
ObjectToObjectConverter Object间
IdToEntityConverter Id→Entity
FallbackObjectToStringConverter Object→String
SpringMVC对接收的数据进行自动类型转换,该工作通过Converter接口实现
声明自定义的转换格式并覆盖系统转换格式
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="org.springframework.format.datetime.DateFormatter">
<property name="pattern" value="yyyy-MM-dd"/>
bean>
set>
property>
bean>
日期类型格式转换(简化版)
名称: @DateTimeFormat
类型: 形参注解、成员变量注解
位置:形参前面 或 成员变量上方
作用:为当前参数或变量指定类型转换规则
范例:
public String requestParam12(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
System.out.println("date="+date);
return "page.jsp";
}
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
注意:依赖注解驱动支持
自定义类型转换器,实现Converter接口,并制定转换前与转换后的类型
<bean id="myDateConverter" class="com.itheima.converter.MyDateConverter"/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myDateConverter"/>
set>
property>
bean>
//自定义类型转换器,实现Converter接口,接口中指定的泛型即为最终作用的条件
//本例中的泛型填写的是String,Date,最终出现字符串转日期时,该类型转换器生效
public class MyDateConverter implements Converter<String, Date> {
//重写接口的抽象方法,参数由泛型决定
public Date convert(String source) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
//类型转换器无法预计使用过程中出现的异常,因此必须在类型转换器内部捕获,不允许抛出,框架无法预计此类异常如何处理
try {
date = df.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
通过注册自定义转换器,将该功能加入到SpringMVC的转换服务ConverterService中
<mvc:annotation-driven conversion-service="conversionService"/>
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/requestURL2")
public String requestURL2() {
return "page.jsp";
}
}
名称: @RequestMapping
类型: 类注解
位置:处理器类定义上方
作用:为当前处理器中所有方法设定公共的访问路径前缀
范例:
访问路径: /user/requestURL1
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/requestURL2")
public String requestURL2() {
return "page.jsp";
}
}
常用属性
@RequestMapping(
value="/requestURL3", //设定请求路径,与path属性、 value属性相同
method = RequestMethod.GET, //设定请求方式
params = "name", //设定请求参数条件
headers = "content-type=text/*", //设定请求消息头条件
consumes = "text/*", //用于指定可以接收的请求正文类型(MIME类型)
produces = "text/*" //用于指定可以生成的响应正文类型(MIME类型)
)
public String requestURL3() {
return "/page.jsp";
}
@RequestMapping("/showPage1")
public String showPage1() {
System.out.println("user mvc controller is running ...");
return "forward:page.jsp";
}
@RequestMapping("/showPage2")
public String showPage2() {
System.out.println("user mvc controller is running ...");
return "redirect:page.jsp";
}
注意:页面访问地址中所携带的 /
展示页面的保存位置通常固定,且结构相似,可以设定通用的访问路径,简化页面配置格式
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
/bean>
public String showPage3() {
return "page";
}
如果未设定了返回值,使用void类型,则默认使用访问路径作页面地址的前缀后缀
//最简页面配置方式,使用访问路径作为页面名称,省略返回值
@RequestMapping("/showPage5")
public void showPage5() {
System.out.println("user mvc controller is running ...");
}
方式一:使用HttpServletRequest类型形参进行数据传递
@RequestMapping("/showPageAndData1")
public String showPageAndData1(HttpServletRequest request) {
request.setAttribute("name","itheima");
return "page";
}
方式二:使用Model类型形参进行数据传递
@RequestMapping("/showPageAndData2")
public String showPageAndData2(Model model) {
model.addAttribute("name","itheima");
Book book = new Book();
book.setName("SpringMVC入门实战");
book.setPrice(66.6d);
model.addAttribute("book",book);
return "page";
}
方式三:使用ModelAndView类型形参进行数据传递,将该对象作为返回值传递给调用者
//使用ModelAndView形参传递参数,该对象还封装了页面信息
@RequestMapping("/showPageAndData3")
public ModelAndView showPageAndData3(ModelAndView modelAndView) {
//ModelAndView mav = new ModelAndView(); 替换形参中的参数
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(66.66d);
//添加数据的方式,key对value
modelAndView.addObject("book",book);
//添加数据的方式,key对value
modelAndView.addObject("name","Jockme");
//设置页面的方式,该方法最后一次执行的结果生效
modelAndView.setViewName("page");
//返回值设定成ModelAndView对象
return modelAndView;
}
方式一:基于response返回数据的简化格式,返回JSON数据
//使用jackson进行json数据格式转化
@RequestMapping("/showData3")
@ResponseBody
public String showData3() throws JsonProcessingException {
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(66.66d);
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(book);
}
使用SpringMVC提供的消息类型转换器将对象与集合数据自动转换为JSON数据
//使用SpringMVC注解驱动,对标注@ResponseBody注解的控制器方法进行结果转换,由于返回值为引用类型,自动调用jackson提供的类型转换器进行格式转换
@RequestMapping("/showData4")
@ResponseBody
public Book showData4() {
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(66.66d);
return book;
}
需要手工添加信息类型转换器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
list>
property>
bean>
方式三:使用SpringMVC注解驱动简化配置
<mvc:annotation-driven/>
HttpServletRequest / HttpServletResponse / HttpSession
SpringMVC提供访问原始Servlet接口API的功能,通过形参声明即可
@RequestMapping("/servletApi")
public String servletApi(HttpServletRequest request,
HttpServletResponse response, HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
request.setAttribute("name","itheima");
System.out.println(request.getAttribute("name"));
return "page.jsp";
}
Head数据获取
名称: @RequestHeader
类型: 形参注解
位置:处理器类中的方法形参前方
作用:绑定请求头数据与对应处理方法形参间的关系
范例:
@RequestMapping("/headApi")
public String headApi(@RequestHeader("user-Agent") String head){
System.out.println(head);
return "page.jsp";
}
Cookie数据获取
名称: @CookieValue
类型: 形参注解
位置:处理器类中的方法形参前方
作用:绑定请求Cookie数据与对应处理方法形参间的关系
范例:
@RequestMapping("/cookieApi")
public String cookieApi(@CookieValue("JSESSIONID") String jsessionid){
System.out.println(jsessionid);
return "page.jsp";
}
Session数据获取
名称: @SessionAttribute
类型: 形参注解
位置:处理器类中的方法形参前方
作用:绑定请求Session数据与对应处理方法形参间的关系
范例:
@RequestMapping("/sessionApi")
public String sessionApi(@SessionAttribute("name") String name){
System.out.println(name);
return "page.jsp";
}
Session数据设置(了解)
名称: @SessionAttributes
类型: 类注解
位置:处理器类上方
作用:声明放入session范围的变量名称,适用于Model类型数据传参
范例:
@Controller
@SessionAttributes(names={"name"})
public class ServletController {
@RequestMapping("/setSessionData2")
public String setSessionDate2(Model model) {
model.addAttribute("name", "Jock2");
return "page.jsp";
}
}
注解式参数数据封装底层原理
数据的来源不同,对应的处理策略要进行区分
Head
Cookie
Session
SpringMVC使用策略模式进行处理分发
顶层接口: HandlerMethodArgumentResolver
实现类: ……
<a href="javascript:void(0);" id="testAjax">访问controller</a>
<script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
$("#testAjax").click(function(){ //为id="testAjax"的组件绑定点击事件
$.ajax({ //发送异步调用
type:"POST", //请求方式: POST请求
url:"ajaxController", //请求参数(也就是请求内容)
data:'ajax message', //请求参数(也就是请求内容)
dataType:"text", //响应正文类型
contentType:"application/text", //请求正文的MIME类型
});
});
});
</script>
名称: @RequestBody
类型: 形参注解
位置:处理器类中的方法形参前方
作用:将异步提交数据组织成标准请求参数格式,并赋值给形参
范例:
@RequestMapping("/ajaxController")
public String ajaxController(@RequestBody String message){
System.out.println(message);
return "page.jsp";
}
@RequestMapping("/ajaxPojoToController")
//如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
//注意:POJO中的属性如果请求数据中没有,属性值为null,POJO中没有的属性如果请求数据中有,不进行映射
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
//如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的对象数组,数据将自动映射到集合参数中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
@RequestMapping("/ajaxReturnJson")
@ResponseBody
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jockme");
user.setAge(40);
return user;
}
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的保存POJO对象的集合转成json数组格式数据
public List ajaxReturnJsonList(){
System.out.println("controller return json list...");
User user1 = new User();
user1.setName("Tom");
user1.setAge(3);
User user2 = new User();
user2.setName("Jerry");
user2.setAge(5);
ArrayList al = new ArrayList();
al.add(user1);
al.add(user2);
return al;
}
名称: @CrossOrigin
类型: 方法注解 、 类注解
位置:处理器类中的方法上方 或 类上方
作用:设置当前处理器方法/处理器类中所有方法支持跨域访问
范例:
@RequestMapping("/cross")
@ResponseBody
//使用@CrossOrigin开启跨域访问
//标注在处理器方法上方表示该方法支持跨域访问
//标注在处理器类上方表示该处理器类中的所有处理器方法均支持跨域访问
@CrossOrigin
public User cross(HttpServletRequest request){
System.out.println("controller cross..."+request.getRequestURL());
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
拦截器( Interceptor)是一种动态拦截方法调用的机制
作用:
核心原理: AOP思想
拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器VS过滤器
归属不同: Filter属于Servlet技术, Interceptor属于SpringMVC技术
拦截内容不同: Filter对所有访问进行增强, Interceptor仅针对SpringMVC的访问进行增强
实现HandlerInterceptor接口
//自定义拦截器需要实现HandleInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
//处理器运行之前执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("前置运行----a1");
//返回值为false将拦截原始处理器的运行
//如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
return true;
}
//处理器运行之后执行
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("后置运行----b1");
}
//所有拦截器的后置执行全部结束后,执行该操作
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("完成运行----c1");
}
//三个方法的运行顺序为 preHandle -> postHandle -> afterCompletion
//如果preHandle返回值为false,三个方法仅运行preHandle
}
配置拦截器
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/showPage"/>
<bean class="com.itheima.interceptor.MyInterceptor"/>
mvc:interceptor>
mvc:interceptors>
注意:配置顺序为先配置执行位置,后配置执行类
原始方法之前运行
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
原始方法运行后运行,如果原始方法被拦截,则不执行
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
拦截器最后执行的方法,无论原始方法是否执行
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion");
}
参数
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<mvc:exclude-mapping path="/b*"/>
<bean class="包+类名"/>
mvc:interceptor>
mvc:interceptors>
责任链模式
责任链模式是一种行为模式
特征:
沿着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务
优势:
独立性:只关注当前节点的任务,对其他任务直接放行到下一节点
隔离性:具备链式传递特征,无需知晓整体链路结构,只需等待请求到达后进行处理即可
灵活性:可以任意修改链路结构动态新增或删减整体链路责任
解耦:将动态任务与原始任务解耦
弊端:
链路过长时,处理效率低下
可能存在节点上的循环引用现象,造成死循环,导致系统崩溃
HandlerExceptionResolver接口(异常处理器)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("异常处理器正在执行中");
ModelAndView modelAndView = new ModelAndView();
//定义异常现象出现后,反馈给用户查看的信息
modelAndView.addObject("msg","出错啦! ");
//定义异常现象出现后,反馈给用户查看的页面
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
根据异常的种类不同,进行分门别类的管理,返回不同的信息
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("my exception is running ...."+ex);
ModelAndView modelAndView = new ModelAndView();
if( ex instanceof NullPointerException){
modelAndView.addObject("msg","空指针异常");
}else if ( ex instanceof ArithmeticException){
modelAndView.addObject("msg","算数运算异常");
}else{
modelAndView.addObject("msg","未知的异常");
}
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
@Component
@ControllerAdvice
public class ExceptionAdvice {
}
@ExceptionHandler(Exception.class)
@ResponseBody
public String doOtherException(Exception ex){
return "出错啦,请联系管理员! ";
}
异常定义格式
//自定义异常继承RuntimeException,覆盖父类所有的构造方法
public class BusinessException extends RuntimeException {
public BusinessException() {
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
异常触发方式
if(user.getName().trim().length()<4) {
throw new BusinessException("用户名长度必须在2-4位之间,请重新输入! ");
}
通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常消息
SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error.jsp"/>
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error"/>
map>
property>
bean>
上传文件过程分析
MultipartResolver接口
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.3version>
dependency>
文件上传下载实现
页面表单
<form action="/fileupload" method="post" enctype="multipart/form-data">
上传LOGO: <input type="file" name="file"/><br/>
<input type="submit" value="上传"/>
form>
SpringMVC配置
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
bean>
控制器
@RequestMapping(value = "/fileupload")
public void fileupload(MultipartFile file){
file.transferTo(new File("file.png"));
}
@RequestMapping(value = "/fileupload")
//参数中定义MultipartFile参数,用于接收页面提交的type=file类型的表单,要求表单名称与参数名相同
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException {
System.out.println("file upload is running ..."+file);
// MultipartFile参数中封装了上传的文件的相关信息
// System.out.println(file.getSize());
// System.out.println(file.getBytes().length);
// System.out.println(file.getContentType());
// System.out.println(file.getName());
// System.out.println(file.getOriginalFilename());
// System.out.println(file.isEmpty());
//首先判断是否是空文件,也就是存储空间占用为0的文件
if(!file.isEmpty()){
//如果大小在范围要求内正常处理,否则抛出自定义异常告知用户(未实现)
//获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用
String fileName = file.getOriginalFilename();
//设置保存的路径
String realPath = request.getServletContext().getRealPath("/images");
//保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题
file.transferTo(new File(realPath,file.getOriginalFilename()));
}
//测试一次性上传多个文件
if(!file1.isEmpty()){
String fileName = file1.getOriginalFilename();
//可以根据需要,对不同种类的文件做不同的存储路径的区分,修改对应的保存位置即可
String realPath = request.getServletContext().getRealPath("/images");
file1.transferTo(new File(realPath,file1.getOriginalFilename()));
}
if(!file2.isEmpty()){
String fileName = file2.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/images");
file2.transferTo(new File(realPath,file2.getOriginalFilename()));
}
return "page.jsp";
}
GET(查询) http://localhost/user/1 GET
POST(保存) http://localhost/user POST
PUT(更新) http://localhost/user PUT
DELETE(删除) http://localhost/user DELETE
**注意:**上述行为是约定方式,约定不是规范,可以打破,所以称Rest风格,而不是Rest规范
//设置rest风格的控制器
@RestController
//设置公共访问路径,配合下方访问路径使用
@RequestMapping("/user/")
public class UserController {
//rest风格访问路径完整书写方式 /user/1
@RequestMapping("/user/{id}")
//使用@PathVariable注解获取路径上配置的具名变量,该配置可以使用多次
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running ....");
return "success.jsp";
}
//rest风格访问路径简化书写方式,配合类注解@RequestMapping使用
@RequestMapping("{id}")
public String restLocation2(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收GET请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.GET)
//接收GET请求简化配置方式
@GetMapping("{id}")
public String get(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收POST请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.POST)
//接收POST请求简化配置方式
@PostMapping("{id}")
public String post(@PathVariable Integer id){
System.out.println("restful is running ....post:"+id);
return "success.jsp";
}
//接收PUT请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.PUT)
//接收PUT请求简化配置方式
@PutMapping("{id}")
public String put(@PathVariable Integer id){
System.out.println("restful is running ....put:"+id);
return "success.jsp";
}
//接收DELETE请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
//接收DELETE请求简化配置方式
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
<filter>
<filter-name>HiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilterfilter-name>
<servlet-name>DispatcherServletservlet-name>
filter-mapping>
开启SpringMVC对Restful风格的访问支持过滤器,即可通过页面表单提交PUT与DELETE请求
页面表单使用隐藏域提交请求类型,参数名称固定为_method,必须配合提交类型method=post使用
<form action="/user/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit"/>
form>
Restful请求路径简化配置方式
@RestController
public class UserController {
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
public String restDelete(@PathVariable String id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
postman 是 一款可以发送Restful风格请求的工具,方便开发调试。首次运行需要联网注册
数据可以随意输入,导致错误的结果。后端表单校验的重要性。
JSR(Java Specification Requests):Java 规范提案
303:提供bean属性相关校验规则
JSR规范列表
Web Service技术
Java Date与Time API (JSR 310)
Java API for RESTful Web Services (JAX-RS) 1.1 (JSR 311)
Implementing Enterprise Web Services 1.3 (JSR 109)
Java API for XML-Based Web Services (JAX-WS) 2.2 (JSR 224)
Java Architecture for XML Binding (JAXB) 2.2 (JSR 222)
Web Services Metadata for the Java Platform (JSR 181)
Java API for XML-Based RPC (JAX-RPC) 1.1 (JSR 101)
Java APIs for XML Messaging 1.3 (JSR 67)
Java API for XML Registries (JAXR) 1.0 (JSR 93)
JCP(Java Community Process):Java社区
Hibernate框架中包含一套独立的校验框架hibernate-validator
导入坐标
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>6.1.0.Finalversion>
dependency>
注意:
tomcat7 :搭配hibernate-validator版本5...Final
tomcat8.5↑ :搭配hibernate-validator版本6...Final
1. 开启校验
名称:@Valid 、 @Validated
类型:形参注解
位置:处理器类中的实体类类型的方法形参前方
作用:设定对当前实体类类型参数进行校验
范例:
@RequestMapping(value = "/addemployee")
public String addEmployee(@Valid Employee employee) {
System.out.println(employee);
}
2.设置校验规则
名称:@NotNull
类型:属性注解 等
位置:实体类属性上方
作用:设定当前属性校验规则
范例:
每个校验规则所携带的参数不同,根据校验规则进行相应的调整
具体的校验规则查看对应的校验框架进行获取
public class Employee{
@NotNull(message = "姓名不能为空")
private String name;//员工姓名
}
3.获取错误信息
@RequestMapping(value = "/addemployee")
public String addEmployee(@Valid Employee employee, Errors errors, Model model){
System.out.println(employee);
if(errors.hasErrors()){
for(FieldError error : errors.getFieldErrors()){
model.addAttribute(error.getField(),error.getDefaultMessage());
}
return "addemployee.jsp";
}
return "success.jsp";
}
通过形参Errors获取校验结果数据,通过Model接口将数据封装后传递到页面显示
<form action="/addemployee" method="post">
员工姓名:<input type="text" name="name"><span style="color:red">${name}span><br/>
员工年龄:<input type="text" name="age"><span style="color:red">${age}span><br/>
<input type="submit" value="提交">
form>
通过形参Errors获取校验结果数据,通过Model接口将数据封装后传递到页面显示
页面获取后台封装的校验结果信息
@NotNull(message = "请输入您的年龄")
@Max(value = 60,message = "年龄最大值不允许超过60岁")
@Min(value = 18,message = "年龄最小值不允许低于18岁")
private Integer age;//员工年龄
名称:@Valid
类型:属性注解
位置:实体类中的引用类型属性上方
作用:设定当前应用类型属性中的属性开启校验
范例:
public class Employee {
//实体类中的引用类型通过标注@Valid注解,设定开启当前引用类型字段中的属性参与校验
@Valid
private Address address;
}
注意:开启嵌套校验后,被校验对象内部需要添加对应的校验规则
public interface GroupOne {
}
public String addEmployee(@Validated({GroupOne.class}) Employee employee){
}
@NotEmpty(message = "姓名不能为空",groups = {GroupOne.class})
private String name;//员工姓名
整合步骤分析
SSM(Spring+SpringMVC+MyBatis)
表结构
最重要的5个步骤
Part0: 项目基础结构搭建
public interface UserDao {
public boolean save(User user); public boolean update(User user);
public boolean delete(Integer uuid); public User get(Integer uuid);
public List<User> getAll(int page,int size);
public interface UserService {
public boolean save(User user); public boolean update(User user);
public boolean delete(Integer uuid);
public User get(Integer uuid);
public List<User> getAll(int page, int size);
/**
用户登录
@param userName 用户名
@param password 密码信息
@return
*/
public User login(String userName,String password);
}
Part1 : Spring环境配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.itheima"/>
beans>
Part1 : Mybatis配置事务
MyBatis映射
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.UserDao">
<insert id="save" parameterType="user">
insert into user(userName,password,realName,gender,birthday)values(#{userName},#{password},#{realName},#{gender},#{birthday})
insert>
<delete id="delete" parameterType="int">
delete from user where uuid = #{uuid}
delete>
<update id="update" parameterType="user">
update user set userName=#{userName},password=#{password},realName=#{realName},gender=#{gender},birthday=#{birthday} where uuid=#{uuid}
update>
<select id="get" resultType="user" parameterType="int">
select * from user where uuid = #{uuid}
select>
<select id="getAll" resultType="user">
select * from user
select>
<select id="getByUserNameAndPassword" resultType="user" >
select * from user where userName=#{userName} and password=#{password}
select>
mapper>
Mybatis核心配置
<tx:annotation-driven transaction-manager="txManager"/>
<context:property-placeholder location="classpath*:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.itheima.domain"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">mysqlprop>
<prop key="reasonable">trueprop>
props>
property>
bean>
array>
property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
**Part2:**单元测试整合junit
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testDelete(){
User user = new User(); userService.delete(3);
}
}
**Part3:**SpringMVC
web.xml配置
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:spring-mvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
spring-mvc.xml
<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/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">
<mvc:annotation-driven/>
<context:component-scan base-package="com.itheima.controller"/>
beans>
controller层
@RestController
@RequestMapping("/user") public class UserController {
@PostMapping
public boolean save(User user) {
System.out.println("save ..." + user); return true;
}
@PostMapping("/login")
public User login(String userName,String password){
System.out.println("login ..." + userName + " ," +password);
return null;
}
}
**Part4:**Spring整合SpringMVC
web.xml加载Spring环境
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
Controller调用Service
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public boolean save(User user){
return userService.save(user);
}
}
**Part5-1:**表现层数据封装
u操作是否成功 | true/false | 格式A |
---|---|---|
u单个数据 | 1,100,true | 格式B |
u对象数据 | json对象 | 格式C |
u集合数据 | json数组 | 格式D |
代码
public class Result {
// 操作结果编码
private Integer code;
// 操作数据结果
private Object data;
// 消息
private String message;
public Result(Integer code) {
this.code = code;
}
public Result(Integer code, Object data) {
this.code = code;
this.data = data;
}
}
状态码常量可以根据自己的业务需求设定
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer SAVE_ERROR = 20010;
//其他编码
}
controller 调用
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public Result save(User user){
boolean flag = userService.save(user);
return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERROR);
}
@GetMapping("/{uuid}")
public Result get(@PathVariable Integer uuid){
User user = userService.get(uuid);
return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user);
}
}
**Part5-2:**自定义异常
定义BusinessException
public class BusinessException extends RuntimeException {
//自定义异常中封装对应的错误编码,用于异常处理时获取对应的操作编码
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code) {
this.code = code;
}
public BusinessException(String message, Integer code) {
super(message);
this.code = code;
}
public BusinessException(String message, Throwable cause,Integer code) {
super(message, cause);
this.code = code;
}
public BusinessException(Throwable cause,Integer code) {
super(cause);
this.code = code;
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace,Integer code) {
super(message, cause, enableSuppression, writableStackTrace);
this.code = code;
}
}
@GetMapping("/{uuid}")
public Result get(@PathVariable Integer uuid){
User user = userService.get(uuid);
//模拟出现异常,使用条件控制,便于测试结果
if (uuid == 10 ) throw new BusinessException("查询出错啦,请重试!",Code.GET_ERROR);
return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user);
}
@Component
@ControllerAdvice
public class ProjectExceptionAdivce {
@ExceptionHandler(BusinessException.class)
@ResponseBody
//对出现异常的情况进行拦截,并将其处理成统一的页面数据结果格式
public Result doBusinessException(BusinessException e){
return new Result(e.getCode(),e.getMessage());
}
}
同前期设置,添加事务注解驱动
@Configuration
//扫描组件,排除SpringMVC对应的bean,等同于
@ComponentScan(value = "com.itheima",excludeFilters = {
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes = {Controller.class})})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
//等同于 ,导入的默认名称为transactionManager
@EnableTransactionManagement
public class SpringConfig {
//等同于
@Bean("transactionManager")
public DataSourceTransactionManager getDataSourceTxManager(@Autowired DataSource dataSource){
DataSourceTransactionManager dtm = new DataSourceTransactionManager();
//等同于
dtm.setDataSource(dataSource);
return dtm;
}
}
同前期设置,添加@EnableWebMvc注解
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
}
EnableWebMvc