SpringMVC介绍

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不能少--%>
<%--name值对应上传类的参数,通过参数操作相应文件--%> 上传LOGO:
上传照片:
上传任意文件:

上传操作:

@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" %>

数据添加成功

<%--下面一行使得PUT能够使用,还需要在web.xml中加一个过滤器--%>


  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}将出错信息给客户提示--%>
    
员工姓名:${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):看代码或视频

你可能感兴趣的:(servlet,java,mvc)