MVC(Model View Controller):一种用于设计创建web应用程序【表现层】的模式
1、Model(模型):数据模型,用于【封装】数据
2、View(视图):页面视图,用于【展示】数据
jsp、html
3、 Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑,上面两个受其调度
servlet、SpringMVC
客户浏览器向Controller发送请求,Controller调度View和Model,响应给客户浏览器
SpringMVC:是一种基于Java实现MVC模型的轻量级web框架
入门案例:(先用骨架cocoo…web创建web,再添加相关的java、test)
pom文件:
4.0.0
com.lcl
spring_base
1.0-SNAPSHOT
war
UTF-8
1.8
1.8
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet.jsp
jsp-api
2.1
provided
org.springframework
spring-context
5.1.9.RELEASE
org.springframework
spring-webmvc
5.1.9.RELEASE
org.springframework
spring-web
5.1.9.RELEASE
org.apache.tomcat.maven
tomcat7-maven-plugin
2.1
80
/
本来用servlet:浏览器请求,web.xml映射到servlet类处理,先打印一句话,再跳转到相应页面(随便给个.jsp页面)
UserServlet
com.lcl.web.UserServlet
UserServlet
/
package com.lcl.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("user servlet is running......");
req.getRequestDispatcher("success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
使用SpringMVC:
一、服务器启动:
1、加载web.xml中的DispatcherServlet
2、读取spring-mvc.xml(相当于applicationContext.xml文件,配置bean的那个文件,为了区分)中的配置,加载所有的com.lcl包中的bean类资源
3、读取bean中方法上的@RequestMapping(“/save”)的内容
二、处理请求:
1、DispatcherServlet配置【拦截】所有请求
2、使用请求路径与所加载的@RequestMapping(“/save”)的内容进行比对
3、执行对应的方法
4、根据方法的返回值在webapp目录中找到对应的页面并展示
DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath*:spring-mvc.xml
DispatcherServlet
/
package com.lcl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
@RequestMapping("/save")
public String save(){
System.out.println("user controller is running......");
return "success.jsp";
}
}
思路:
大致的就是,浏览器发出请求,加载web.xml中的DispatcherServlet,这个东东,先读取spring-mvc.xml中的配置,加载所有的com.lcl包中的bean类资源,然后拦截所有请求。在所有bean类资源中,找方法上的@RequestMapping,其内容和路径对应上,则执行对应方法,再根据方法的返回值在webapp目录中找到对应的页面并展示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RsUiDCVU-1656900510214)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220117163020921.png)]
基础配置:
1、Controller加载控制:SpringMVC的处理器对应的bean必须按照规范格式开发,为避免加入无效的bean,可以通过bean加载过滤器进行包含设定或排除设定,表现层bean标注通常设定为 @Controller
spring-mvc.xml中改成:
这个与@ComponentScan里面的excludeFilter类似
业务层和数据层的bean加载由spring控制,表现层的bean加载由SpringMVC单独控制
2、静态资源加载
核心控制器拦截的是所有请求,需要对静态资源请求进行放行,通过配置放行资源
还可以使用简化格式放行所有普通资源调用,无需上面的一一枚举
3、中文乱码处理:SpringMVC提高专用的中文字符过滤器,用于处理乱码问题
在web.xml中添加
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
CharacterEncodingFilter
/*
4、注解驱动:干掉配置文件spring-mvc.xml、web.xml
package com.lcl.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //干掉beans
@ComponentScan(
value = "com.lcl",
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
) //干掉包扫描
//干掉静态资源加载,实现接口WebMvcConfigurer
public class SpringMVCConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
registry.addResourceHandler("/js/**").addResourceLocations("/img/");
registry.addResourceHandler("/css/**").addResourceLocations("/img/");
}
/*简化格式,放行所有普通资源*/
/* @Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}*/
}
package com.lcl.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
//干掉web.xml
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfiguration.class);//加载那个配置类,获取bean资源
return ctx;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};//拦截所有请求
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
/*中文乱码处理*/
@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,"/*");
}
}
请求
一、请求参数:SpringMVC将传递的参数封装到处理器方法的形参中,达到快速访问参数的目的
1、普通参数类型:参数名与处理器方法形参名保持一致
@RequestMapping("/requestParam1")
public String requestParam1(String name,int age){
System.out.println(name+","+age);
return "page.jsp";
}
http://localhost/requestParam1?name=lcl&age=25
形参注解:位于处理器方法形参的前方,用于绑定请求参数与对应处理方法形参间的关系
//@RequestParam 第一个参数value指定请求参数名,require指定必须给请求参数,defaultValue参数默认值(不给的话即默认值,require也不会报错)
@RequestMapping("/requestParam2")
public String requestParam2(@RequestParam(value = "userName",required = true,defaultValue = "lcl") String name){
System.out.println(name);
return "page.jsp";
}
// http://localhost/requestParam2?userName=lcl2
2、POJO类型参数(实体类类型,传参时与对应实体类的属性名保持一致)
//User类有name和age两个属性
@RequestMapping("/requestParam3")
public String requestParam3(User user){
System.out.println(user);
return "page.jsp";
}
//http://localhost/requestParam3?name=lcl&age=25
3、复杂POJO类型参数
3.1
参数冲突
当POJO类型属性名与其他形参同名时,将同时被赋同一个值(此处的age会同时赋给user的age,以及另一个参数的age),建议用@RequestParam加以区分
http://localhost/requestParam3?name=lcl&age=25
@RequestMapping("/requestParam4")
public String requestParam4(User user,int age){
System.out.println("user="+user+",age="+age);
return "page.jsp";
}
3.2
属性是列表啥的,可能有多个值
用&赋予多个值即可
http://localhost/requestParam6?nick=lcl&nick=xwh
@RequestMapping("/requestParam6")
public String requestParam6(Person person){
System.out.println(person);
return "page.jsp";
}
3.3
属性是列表啥的,可能有多个值,且列表里面是对象
http://localhost/requestParam7?addresses[0].city=feidong&addresses[1].province=anhui
@RequestMapping("/requestParam7")
public String requestParam7(Person person){
System.out.println(person.getAddresses());
return "page.jsp";
}
3.4
属性是map集合,键值对
http://localhost/requestParam8?addressMap["job"].city=feidong&addressMap["home"].province=anhui
@RequestMapping("/requestParam8")
public String requestParam8(Person person){
System.out.println(person.getAddressMap());
return "page.jsp";
}
4、数组类型传参
和列表差不多,用&传多个值即可
@RequestMapping("/requestParam9")
public String requestParam9(String[] nick){
System.out.println(nick[0]+","+nick[1]);
return "page.jsp";
}
http://localhost/requestParam9?nick=lcl&nick=xwh
5、集合类型传参
注意!要用@RequestParam注解,将多个参数打包成参数数组后,springMVC才能识别该数据格式
原因:springMVC默认将List作为对象处理,赋值前创建对象,但是List是接口,不能创建对象,用ArrayList对象可以创建,但没有nick属性,数据为空
@RequestMapping("/requestParam10")
public String requestParam10(@RequestParam("nick") List nick){
System.out.println(nick);
return "page.jsp";
}
http://localhost/requestParam10?nick=lcl&nick=xwh
对应的类:
package com.lcl.domian;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.lcl.domian;
public class Address {
private String province;
private String city;
private String address;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", address='" + address + '\'' +
'}';
}
}
package com.lcl.domian;
import java.util.List;
import java.util.Map;
public class Person {
private String name;
private Integer age;
private Address address;
private List nick;
private List addresses;
private Map addressMap;
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;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List getNick() {
return nick;
}
public void setNick(List nick) {
this.nick = nick;
}
public List getAddresses() {
return addresses;
}
public void setAddresses(List addresses) {
this.addresses = addresses;
}
public Map getAddressMap() {
return addressMap;
}
public void setAddressMap(Map addressMap) {
this.addressMap = addressMap;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
", nick=" + nick +
", addresses=" + addresses +
", addressMap=" + addressMap +
'}';
}
}
二、类型转换器
以Date为例
@RequestMapping("/requestParam11")
public String requestParam11(Date data){
System.out.println(data);
return "page.jsp";
}
http://localhost/requestParam11?data=2022/01/29
默认的格式就是上面的2022/01/29,其他的报错,那么就可以用类型转换器来修改格式
在配置文件中配置bean,修改格式
修改后只能用对应格式访问
http://localhost/requestParam11?data=2022-01-29
日期类型格式转换简化版,用注解
@RequestMapping("/requestParam12")
public String requestParam12(@DateTimeFormat(pattern = "yyyy-MM-dd") Date data){
System.out.println(data);
return "page.jsp";
}
http://localhost/requestParam12?data=2022-01-29
在配置文件中,只需要开启注解驱动即可,那个改格式的不需要了
自定义类型转:日期为例
写一个类定义格式,实现Converter接口
package com.lcl.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyDateConverter implements Converter {
@Override
public Date convert(String s) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = df.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
配置文件的修改,把上面的类变成bean资源set注入,启动驱动服务
@RequestMapping("/requestParam13")
public String requestParam13(Date data){
System.out.println(data);
return "page.jsp";
}
http://localhost/requestParam13?data=2022-01-29
三、请求映射:@RequestMapping(“****”)
不仅可以放在处理器类的方法上(绑定请求地址与对应处理方法间的关系),还可以放在处理器类上(为当前处理器中所有方法设定公共的访问路径前缀)
若是在类上加,那么在访问路径上还得加上对应的路径(此处是/user),例如在类上加@RequestMapping(“/user”)
那么访问路径为:
http://localhost/user/requestURL1
响应
@Controller
public class UserController {
//转发和重定向的区别,看地址栏,转发不变,重定向会变成对应页面的路径
@RequestMapping("/showPage")
public String showPage(){
System.out.println("user mvc controller is running...");
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 "forward:/WEB-INF/page/page.jsp"; //转发,这里可以
}
@RequestMapping("/showPage3")
public String showPage3(){
System.out.println("user mvc controller is running...");
return "redirect:page.jsp"; //重定向
}
@RequestMapping("/showPage4")
public String showPage4(){
System.out.println("user mvc controller is running...");
return "redirect:/WEB-INF/page/page.jsp"; //重定向,这里不行,报错
}
}
页面访问快捷设定:展示页面的保存位置通常固定,且结构相似,可以设定通用的访问路径,简化页面配置格式。只适用于转发的默认格式
在配置文件中:
@RequestMapping("/showPage5")
public String showPage5(){
System.out.println("user mvc controller is running...");
return "page";
}
将【前缀/WEB-INF/page/】和【page】以及【后缀.jsp】拼接
/*无返回值,此时用访问路径showPage6和前缀后缀拼接跳转页面*/
@RequestMapping("/showPage6")
public void showPage6(){
System.out.println("user mvc controller is running...");
}
【携带数据】页面跳转设定
/*携带数据页面跳转设定*/
/*三种方法,依次递增地推荐,第三种最好*/
@Controller
public class BookController {
@RequestMapping("/showPageAndData1")
public String showPageAndData1(HttpServletRequest request){
request.setAttribute("name","lcl");
return "page";
}
@RequestMapping("/showPageAndData2")
public String showPageAndData2(Model model){
model.addAttribute("name","xwh");
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(888.88d);
model.addAttribute("book",book);
return "page";
}
@RequestMapping("/showPageAndData3")
public ModelAndView showPageAndData3(ModelAndView modelAndView){
//ModelAndView modelAndView = new ModelAndView();//替换形参
modelAndView.addObject("name","xwh");
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(888.88d);
modelAndView.addObject("book",book);
modelAndView.setViewName("page");
return modelAndView;
}
/*以下两种注意把页面访问快捷设定配置注释掉*/
@RequestMapping("/showPageAndData4")
public ModelAndView showPageAndData4(ModelAndView modelAndView){
modelAndView.setViewName("forward:page.jsp");//转发
return modelAndView;
}
@RequestMapping("/showPageAndData5")
public ModelAndView showPageAndData5(ModelAndView modelAndView){
modelAndView.setViewName("redirect:page.jsp");//重定向
return modelAndView;
}
}
返回数据
@Controller
public class AccountController {
@RequestMapping("/showData1")
public void showData1(HttpServletResponse response) throws IOException {
response.getWriter().write("message");
}
@RequestMapping("/showData2")
@ResponseBody /*要加上这一注解表明下面是响应体,而不是跳转页面*/
public String showData2(){
return "{'name':'Jock'}";
}
/*使用json来表示数据*/
@RequestMapping("/showData3")
@ResponseBody
public String showData3() throws JsonProcessingException {
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(888.88d);
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(book);
}
/*下面方法更简单,直接返回对应的数据即可,但需要开启驱动,由json处理*/
@RequestMapping("/showData4")
@ResponseBody
public Book showData4(){
Book book = new Book();
book.setName("SpringMVC入门案例");
book.setPrice(888.88d);
return book;//需要在配置文件中加 ,不然没有转换器,加后由json处理,乱码问题都解决了
}
@RequestMapping("/showData5")
@ResponseBody
public ArrayList showData5(){
Book book1 = new Book();
book1.setName("book1");
book1.setPrice(666.66d);
Book book2 = new Book();
book2.setName("book2");
book2.setPrice(888.88d);
ArrayList arrayList = new ArrayList();
arrayList.add(book1);
arrayList.add(book2);
return arrayList;
}
}
Servlet相关接口替换方案
注解式参数数据封装底层原理:。。。。。
@Controller
@SessionAttributes(names = {"age","gender"})
public class UserController {
@RequestMapping("/servletApi")
public String servletApi(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "page";
}
/*head数据获取,形参注解,绑定请求头数据与对应处理方法形参间的关系*/
/*在配置文件中开启驱动*/
@RequestMapping("/headApi")
public String headApi(@RequestHeader("Accept-Encoding") String headMsg){
System.out.println(headMsg);
return "page";
}
/*cookie数据获取,形参注解,绑定请求cookie数据与对应处理方法形参间的关系*/
@RequestMapping("/cookieApi")
public String cookieApi(@CookieValue("JSESSIONID") String jsessionid){
System.out.println(jsessionid);
return "page";
}
/*Session数据获取,形参注解,绑定请求Session数据与对应处理方法形参间的关系*/
/*先放值再取值*/
@RequestMapping("/setSessionData")
public String setSessionData(HttpSession session){
session.setAttribute("name","lcl");
return "page";
}
@RequestMapping("/sessionApi")
public String sessionApi(@SessionAttribute("name") String name){
System.out.println(name);
return "page";
}
/*Session数据设置,类注解,声明放入session范围的变量名称,适用于Model类型数据传参*/
//搭配 @SessionAttributes(names = {"age","gender"})
//先放后取
@RequestMapping("/setSessionData2")
public String setSessionData2(Model model){
model.addAttribute("age",39);
model.addAttribute("gender","男");
return "page";
}
@RequestMapping("/sessionApi2")
public String sessionApi2(@SessionAttribute("age") int age,@SessionAttribute("gender") String gender){
System.out.println(age);
System.out.println(gender);
return "page";
}
}
异步调用(用jquery和ajax)
在配置文件中添加
以及在webapp下创建目录,将jquery的js文件导入
1、异步请求传参
使用@RequestBody注解,在形参上加注解,将异步提交的数据组织成请求参数格式,并赋值给形参
ajax.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
访问springmvc后台controller
访问springmvc后台controller,传送Json格式POJO
访问springmvc后台controller,传送Json格式List
@Controller
public class AjaxController {
@RequestMapping("/ajaxController")
public String ajaxController(@RequestBody String message){//获取异步调用的请求参数,文本数据
System.out.println("ajax request is running..."+message);
return "page.jsp";
}
@RequestMapping("/ajaxPojoToController")
public String ajaxPojoToController(@RequestBody User user){//获取异步调用的请求参数,json数据,封装到User对象
System.out.println("controller pojo:"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
public String ajaxListToController(@RequestBody List userList){//获取异步调用的请求参数,集合数据
System.out.println("controller pojo:"+userList);
return "page.jsp";
}
}
2、异步请求响应(success:回调函数,可以看看之前的笔记)
//2、异步请求响应
//字符串
//为id="testAjaxReturnString"的组件绑定点击事件
$("#testAjaxReturnString").click(function () {
$.ajax({
type:"POST",
url:"ajaxReturnString",
success:function (data) {
alert(data);
}
});
});
//Json数据
//为id="testAjaxReturnJson"的组件绑定点击事件
$("#testAjaxReturnJson").click(function () {
$.ajax({
type:"POST",
url:"ajaxReturnJson",
success:function (data) {
alert(data['name']+","+data['age']);
}
});
});
//Json数组数据
//为id="testAjaxReturnJsonList"的组件绑定点击事件
$("#testAjaxReturnJsonList").click(function () {
$.ajax({
type:"POST",
url:"ajaxReturnJsonList",
success:function (data) {
alert(data.length);
alert(data[0]["name"]);
alert(data[1]["age"]);
}
});
});
})
//2、异步请求响应
//字符串
@RequestMapping("/ajaxReturnString")
@ResponseBody
public String ajaxReturnString(){
System.out.println("controller return string...");
return "page.jsp";
}
//Json数据
@RequestMapping("/ajaxReturnJson")
@ResponseBody
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jock");
user.setAge(25);
return user;
}
//Json数组数据
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
public ArrayList ajaxReturnJsonList(){
System.out.println("controller return json pojo...");
User user1 = new User();
user1.setName("Jock");
user1.setAge(25);
User user2 = new User();
user2.setName("Fuck");
user2.setAge(26);
ArrayList users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
3、跨域访问 :当通过域名A下的操作访问域名B下的资源时,称为跨域访问
准备工作:C:\Windows\System32\drivers\etc中的hosts改一下内容:127.0.0.1 www.jock.com
//3、跨域访问
//为id="testCross"的组件绑定点击事件
$("#testCross").click(function () {
$.ajax({
type:"POST",
url:"cross",
success:function (data) {
alert("跨域调用信息反馈:"+data["name"]+","+data["age"]);
}
});
});
//3、跨域访问
@RequestMapping("/cross")
@ResponseBody
@CrossOrigin
public User cross(HttpServletRequest request){
System.out.println("controller cross..."+request.getRequestURL());
User user = new User();
user.setName("Jock");
user.setAge(25);
return user;
}
上面就有两个地址访问,一个是localhost,一个是http://www.jock.com/ajax.jsp。不同域名,不同源
若是ajax.jsp中的url改成http://www.jock.com/cross,则localhost这个访问会报错,需要加注解@CrossOrigin,就不报错了
拦截器简介
1)拦截器是一种【动态拦截】【方法调用】的机制
2)作用(增强功能):
1、在指定的方法调用前后执行预先设定后的代码
2、阻止原始方法的执行
3)核心原理:AOP思想
4)拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器和过滤器的区别:前者属于MVC技术,而后者属于Servlet技术。前者只能针对SpringMVC的访问进行增强,而后者对所有访问都能进行增强
拦截器开发:
没有拦截器,那么直接运行原始功能结束,有拦截器则,先拦截,执行前置拦截,若return是true,则放行,执行原始功能,再执行后置拦截器,最后执行完成拦截器
1)制作拦截器功能类(通知),实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置运行------al");
return true;//true表示向下放行,false表示不向下放行,原始功能以及下面两个都不执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后置运行------bl");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("完成运行------cl");
}
}
2)配置拦截器的执行位置(切入点),配置文件中配置
拦截器配置与方法参数
//拦截所有,/*代表根路径下任意名称,不再往下匹配目录,/**表示根路径及其子路径任意名称
//拦截以handleRun开头的
//不拦截b开头的
拦截器类中方法参数自己看
多拦截器配置:前置拦截器执行顺序即是配置文件中的配置顺序,后置拦截器执行顺序与前置相反,完成拦截器执行顺序跟后置拦截器执行顺序相同
多拦截器,若是有return的false,那么此位置后面的前置不执行了,还有所有的后置都不执行,直接跳到此位置前一个的完成拦截器往后执行(此位置前面的前置都执行了,那么对应的完成拦截器也执行,只是所有的后置都不执行)
责任链模式:一种行为模式。沿着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务。优缺点略
异常处理
//异常处理器,实现HandlerExceptionResolver接口
@Component
public class ExceptionResolve implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
System.out.println("my exception is running...");
ModelAndView modelAndView = new ModelAndView();
//定义异常现象出现后,反馈给用户查看的信息
modelAndView.addObject("msg","出错啦,宝贝~");
//定义异常现象出现后,反馈给用户查看的页面
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
用if来判断不同异常进行不同处理,条件:e instanceof 异常名
使用注解来处理异常(更提倡,简单明了,且处理异常的时间更早,可以拦截入参类型转换异常,上面的则不行)
@Component
/*这注释必须加,对Controller增强*/
@ControllerAdvice
/*使得return不指向对应页面,只是字符串,可以写在对应的方法上,此处表示所有方法都如此*/
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler(NullPointerException.class)
public String doNullException(){
return "空指针异常";
}
@ExceptionHandler(ArithmeticException.class)
public String doArithmeticException(){
return "ArithmeticException";
}
@ExceptionHandler(Exception.class)
public String Exception(){
return "all";
}
}
异常处理方案
package com.lcl.exception;
//用户异常,继承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);
}
}
package com.lcl.exception;
//系统异常,继承RuntimeException,覆盖所有构造方法
public class SystemException extends RuntimeException{
public SystemException() {
}
public SystemException(String message) {
super(message);
}
public SystemException(String message, Throwable cause) {
super(message, cause);
}
public SystemException(Throwable cause) {
super(cause);
}
public SystemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.lcl.controller;
import com.lcl.domain.User;
import com.lcl.exception.BusinessException;
import com.lcl.exception.SystemException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
@Controller
public class AjaxController {
@RequestMapping("/save")
@ResponseBody
public List save(@RequestBody User user){
System.out.println("user controller save is running ...");
//模拟业务层发起调用产生了异常
//除0异常
//int i = 1/0;
//空指针异常
/*String str = null;
str.length();*/
//把自己自定义的异常包装一下
if(user.getName().trim().length() < 8){
throw new BusinessException("对不起,用户名长度不够八位,请重新输入!");
}
if(user.getAge() < 0){
throw new BusinessException("对不起,年龄必须大于0!");
}
if(user.getAge() > 100){
throw new SystemException("服务器连接失败,请尽快检查处理!");
}
User user1 = new User();
user1.setName("Jock");
user1.setAge(25);
User user2 = new User();
user2.setName("Fuck");
user2.setAge(26);
ArrayList users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
}
package com.lcl.exception;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@Component
@ControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(BusinessException.class)
public String doBusinessException(Exception e, Model m){
m.addAttribute("msg",e.getMessage());//获取包装的异常的信息
return "error.jsp";
}
@ExceptionHandler(SystemException.class)
public String doSystemException(Exception e, Model m){
m.addAttribute("msg",e.getMessage());
return "error.jsp";
}
@ExceptionHandler(Exception.class)
public String doException(Exception e, Model m){
m.addAttribute("msg",e.getMessage());
return "error.jsp";
}
}
实用技术
1、文件上传下载
MultipartResolver定义了文件上传过程中的相关操作,并对通用性操作进行了封装
MultipartResolver接口底层实现类CommonsMultipartResolver,并未自主实现文件的上传下载对应的功能,而是调用了apache的文件上传下载组件
导入相应的坐标:
commons-fileupload
commons-fileupload
1.4
将CommonsMultipartResolver配成bean资源
上传页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--action值对应上传类的方法名,enctype="multipart/form-data不能少--%>
上传操作:
@Controller
public class FileUploadController {
@RequestMapping("/fileupload")
public String fileupload(MultipartFile file) throws IOException {
/*参数file和fileupload页面中的input的name属性值对应。获取对应的文件。再通过相应的方法操作文件*/
System.out.println("file upload is running..."+file);
/*文件上传*/
file.transferTo(new File("abc.jpeg"));
return "page.jsp";
}
}
上传操作的注意事项及解决,还有多文件上传操作
@Controller
public class FileUploadController {
@RequestMapping("/fileupload")
/*参数file和fileupload页面中的input的name属性值对应。获取对应的文件。再通过相应的方法操作文件*/
/*request参数,获取自己创建的一个目录的路径,然后将文件保存到此目录下*/
/*file、file1、file2多文件上传,同样对应input的name属性值*/
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException {
System.out.println("file upload is running..."+file);
if(!file.isEmpty()){
//用file.getSize()来要求文件在一定范围内,此处就不写了
//获取文件名,不是getName()方法,此方法只能获取参数或者说input的name属性值
String filename = file.getOriginalFilename();
//设置文件上传的保存路径,增加request参数,获取自己创建的一个目录的路径,然后将文件保存到此目录下
String realPath = request.getServletContext().getRealPath("/images");
System.out.println(realPath);
/*文件上传,指定保存路径和文件名称(此处文件原名称)*/
file.transferTo(new File(realPath,filename));
//重名问题解决:自己定一个命名策略,原名称只是一部分。使用UUID
/*文件上传,指定文件名称*/
//file.transferTo(new File("abc.jpeg"));
}
if(!file1.isEmpty()){
String filename = file1.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/images");
file1.transferTo(new File(realPath,filename));
}
if(!file2.isEmpty()){
String filename = file2.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/images");
file2.transferTo(new File(realPath,filename));
}
return "page.jsp";
}
}
2、Rest:一种网络资源的访问风格,定义了网络资源的访问方式,与传统风格相区分开
行为约定:GET、POST、PUT、DELETE 查询、保存、更新、删除
Restful:按照Rest风格访问网络资源,隐藏资源的访问行为,通过地址无法得知做的是何种操作(传统方式可以得知)
/*
@Controller
@ResponseBody
*/
/*上面两个合并成下面的@RestController*/
@RestController
@RequestMapping("/user/")
public class UserController {
@RequestMapping("/restLocation")
public String restLocation(){
System.out.println("restful is running......");
return "success.jsp";
}
/*restful风格*/
@RequestMapping("{id}")
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running......get:"+id);
return "success.jsp";
}
//四种行为,GET、POST、PUT、DELETE 查询、保存、更新、删除
@RequestMapping(value = "{id}",method = RequestMethod.GET)
//@GetMapping("{id}")上面的简化格式
public String get(@PathVariable Integer id){
System.out.println("restful is running......get:"+id);
return "success.jsp";
}
@RequestMapping(value = "{id}",method = RequestMethod.POST)
//@PostMapping("{id}")上面的简化格式
public String post(@PathVariable Integer id){
System.out.println("restful is running......post:"+id);
return "success.jsp";
}
@RequestMapping(value = "{id}",method = RequestMethod.PUT)
//@PutMapping("{id}")上面的简化格式
public String put(@PathVariable Integer id){
System.out.println("restful is running......put:"+id);
return "success.jsp";
}
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
//@DeleteMapping("{id}")上面的简化格式
public String delete(@PathVariable Integer id){
System.out.println("restful is running......delete:"+id);
return "success.jsp";
}
}
<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" language="java" %>
数据添加成功
HiddenHttpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
HiddenHttpMethodFilter
DispatcherServlet
专用工具:postman 可以发送Restful风格请求工具,方便开发调试,首次运行需要联网注册
3、表单校验
校验位置:1)客户端校验 2)服务端校验
校验内容与对应方式:
1)格式校验:
客户端:使用JS技术,利用正则表达式
服务端:使用【校验框架】
2)逻辑校验:
客户端:使用AJAX发送要校验的数据,在服务端完成逻辑校验,返回校验结果
服务端:接收到完整请求后,在执行业务操作前,完成逻辑校验
表单校验框架:
JSR(Java规范提案) JCP(Java社区)
303规范:提供bean属性相关校验规则
Hibernate框架中包含一套独立的校验框架hibernate-validator
导入坐标:
javax.validation
validation-api
2.0.1.Final
org.hibernate
hibernate-validator
6.1.0.Final
入门案例(对一个属性进行校验)
起始页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
添加员工-用于演示表单校验
<%--页面发起请求,找Controller资源,对应的方法,@Valid再对employee开启校验,
将出错信息封装(errors接消息,model传消息),下面的${name}将出错信息给客户提示--%>
校验操作:
@Controller
public class EmployeeController {
@RequestMapping(value = "/addemployee")
//@Valid表明对employee开启了校验,在实体类中定义规则
//errors用来判断是否出错,接收出错信息
//model传输出错信息
public String addemployee(@Valid Employee employee, Errors errors, Model model){
if(errors.hasErrors()){//如果出错
List fieldErrors = errors.getFieldErrors();//获取出错集合
for (FieldError fieldError : fieldErrors) {//打印出错信息
//System.out.println(fieldError.getField());//哪个属性出错
//System.out.println(fieldError.getDefaultMessage());//自定义出错的信息
model.addAttribute(fieldError.getField(),fieldError.getDefaultMessage());
}
return "addemployee.jsp";
}
return "success.jsp";
}
}
实体类的属性上定义校验规则
public class Employee {
//定义规则
@NotBlank(message = "姓名不能为空")
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;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
实际开发可能需要的校验规则:1、同一个字段有多个约束条件 2、引用类型字段如何校验 3、根据业务不同需要调整是否参与校验
1、同一个字段有多个约束条件
@NotNull(message = "请输入您的年龄")
@Max(value = 60,message = "年龄最大值不允许超过60岁")
@Min(value = 18,message = "年龄最小值不允许低于18岁")
private Integer age;
2、引用类型字段如何校验
//@Valid开启校验,规则写在对应实体类的属性上
@Valid
private Address address;
package com.lcl.domian;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
public class Address implements Serializable {
@NotBlank(message = "请输入省份名称")
private String provinceName;
@NotBlank(message = "请输入城市名称")
private String cityName;
@NotBlank(message = "请输入详细地址")
private String detail;
@NotBlank(message = "请输入邮政编码")
@Size(max = 6,min = 6,message = "邮政编码由6位组成")
private String zipCode;
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
@Override
public String toString() {
return "Address{" +
"provinceName='" + provinceName + '\'' +
", cityName='" + cityName + '\'' +
", detail='" + detail + '\'' +
", zipCode='" + zipCode + '\'' +
'}';
}
}
错误提示:有多个属性,requestScope[‘address.provinceName’]
省:${requestScope['address.provinceName']}
3、根据业务不同需要调整是否参与校验(分组校验):
3.1先创建一个接口
3.2然后在属性规则注解中加一个属性groups = {GroupA.class}
//根据业务不同需要调整是否参与校验,分组校验
@NotBlank(message = "姓名不能为空",groups = {GroupA.class})
private String name;
//同一个字段有多个约束条件
@NotNull(message = "请输入您的年龄",groups = {GroupA.class})
@Max(value = 60,message = "年龄最大值不允许超过60岁")
@Min(value = 18,message = "年龄最小值不允许低于18岁")
private Integer age;
上面的只有name的@NotBlank和age的@NotNull会分组校验,其他的都不会参与校验
3.3只需要改一处注解@Validated(value = {GroupA.class}),这个注解可以用于分组校验,而@Valid不可以用于分组校验
@Controller
public class EmployeeController {
/*根据业务不同需要调整是否参与校验(分组校验),只对GroupA.class进行校验*/
@RequestMapping(value = "/addemployee")
public String addemployee(@Validated(value = {GroupA.class}) Employee employee, Errors errors, Model model){
if(errors.hasErrors()){
List fieldErrors = errors.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
System.out.println(fieldError.getField());
System.out.println(fieldError.getDefaultMessage());
model.addAttribute(fieldError.getField(),fieldError.getDefaultMessage());
}
return "addemployee.jsp";
}
return "success.jsp";
}
SSM整合(Spring+SpringMVC+MyBatis):看代码或视频