石一歌的SpringMVC笔记

SpringMVC

原理

640

图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

20200803105047852

xml配置实现

20200803105103912

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置DispatcherServlet:SpringMVC核心-->
    <servlet>
        <servlet-name>SpringMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--关联一个SpringMvc的resource配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:SpringMVC.xml</param-value>
        </init-param>
        <!--启动级别-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--匹配所有的请求: 
					/  :只匹配请求,不包含所有的.jsp
                      /* :匹配所有的请求,包括jsp页面
    -->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

SpringMVC.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--处理器映射器HandlerMapping:查找访问的url控制器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理器适配器HandlerAdapter:controller将处理好的数据返回给HandlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器ViewResolver:将后端处理好的数据和视图传给DispatchServlet,DS再交给ViewResolver先解析一遍,确认无误再传给前端
        必须熟悉,以后还要学模版引擎Thymeleaf/Freemarker...
        1 获取ModeAndView的数据
        2 解析ModeAndView的视图名字
        3 拼接视图名字,找到对应的视图 WEB-INF/jsp/hello.jsp
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!--BeanNameUrlHandlerMapping处理器:绑定跳转的url=页面访问的网址-->
    <bean id="/hello" class="com.ssl.controller.HelloController"/>
</beans>

编写控制层

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //1 创建modelAndView
        ModelAndView mv = new ModelAndView();
        //2 调用业务层,这里没有,就不写
        //3 封装对象,放在mv中添加
        mv.addObject("msg", "Hello SpringMvc");
        //4 封装要跳转的视图,WEB-INF/jsp/hello.jsp
        mv.setViewName("hello");
        return mv;
    }
}

编写视图层

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello</title>
</head>
<body>
<%--接受传递的参数--%>
${msg}
</body>
</html>

总结

20200803105123418

注解实现

资源过滤

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
       <resource>
           <directory>src/main/resources</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
   </resources>
</build>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
        version="4.0">

   <!--1.注册servlet-->
   <servlet>
       <servlet-name>SpringMVC</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:SpringMVC.xml</param-value>
       </init-param>
       <!-- 启动顺序,数字越小,启动越早 -->
       <load-on-startup>1</load-on-startup>
   </servlet>

   <!--所有请求都会被springmvc拦截 -->
   <servlet-mapping>
       <servlet-name>SpringMVC</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

</web-app>

/ 和 /* 的区别:< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。< url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

SpringMVC.xml

20200803105137927

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.ssl.controller"/>
    <!--让SpringMvc不处理静态资源。让.css,.js等不进视图解析器-->
    <mvc:default-servlet-handler/>
    <!--注解加载映射器、适配器,不用之前那么麻烦配置了-->
    <mvc:annotation-driven/>
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

编写控制层

2020080310520552

@Controller
public class HelloController {
    /**
     * @param model 模型
     * @return 被视图解析器处理:访问"/WEB-INF/jsp/hello.jsp资源
     * 访问的url:RequestMapping("/hello")
     */
    @RequestMapping("/hello")
    public String hello(Model model) {
        //封装数据
        model.addAttribute("msg", "Hello SpringMvc_annotation");
        //被视图解析器处理:访问"/WEB-INF/jsp/hello.jsp资源
        return "hello";
    }

编写视图层

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello</title>
</head>
<body>
${msg}
</body>
</html>

@Controller

乱码过滤器

配置在web.xml

	<filter>
        <filter-name>encode</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encode</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

乱码过滤增强

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * 解决get和post请求 全部乱码的过滤器
 */
public class GenericEncodingFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //处理response的字符编码
        HttpServletResponse myResponse = (HttpServletResponse) response;
        myResponse.setContentType("text/html;charset=UTF-8");

        // 转型为与协议相关对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 对request包装增强
        HttpServletRequest myRequest = new MyRequest(httpServletRequest);
        chain.doFilter(myRequest, response);
    }

    @Override
    public void init(FilterConfig filterConfig){
    }

}

//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

    private final HttpServletRequest request;
    //是否编码的标记
    private boolean hasEncode;

    //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
    public MyRequest(HttpServletRequest request) {
        super(request);// super必须写
        this.request = request;
    }

    // 对需要增强方法 进行覆盖
    @Override
    public Map getParameterMap() {
        // 先获得请求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post请求
            try {
                // 处理post乱码
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 处理get乱码
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }
        return super.getParameterMap();
    }

    //取一个值
    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回参数的第一个值
    }

    //取所有值
    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        return parameterMap.get(name);
    }
}

使用注解开发,@Controller注册进Spring容器,如果返回值是String,并且有具体的页面可以跳转,那么就会被视图解析器解析

@Controller
public class ControllerDemo2 {
    @RequestMapping("/demo2")
    public String gcore(Model model) {
        model.addAttribute("demo2", "demo2");
        return "demo2";
    }
}

@RequestMapping

可以在类和方法上配置url访问路径

@Controller
@RequestMapping("/controller")
public class ControllerDemo3 {
    @RequestMapping("/demo3")
    public String gcore(Model model) {
        model.addAttribute("demo3", "demo3");
        return "demo3";
    }
}

转发&重定向

@Controller
@RequestMapping("/test")
public class DemoController {
    //原生转发,不走视图解析器
   	 @RequestMapping("/f1")
    public void F1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("demo","F1");
        request.getRequestDispatcher("/WEB-INF/jsp/demo.jsp").forward(request,response);
    }
    //SpringMVC转发,不走视图解析器
    @RequestMapping("/f2")
    public String F2(Model model) {
        model.addAttribute("demo","F2");
        return "forward:/WEB-INF/jsp/demo.jsp";
    }
    //视图解析器转发
    @RequestMapping("/f3")
    public String F3(Model model) {
        model.addAttribute("demo","F3");
        return "demo";
    }
    //SpringMVC转发,必须注释视图解析器
    @RequestMapping("/f4")
    public String F4(Model model) {
        model.addAttribute("demo","F4");
        return "/WEB-INF/jsp/demo.jsp";
    }
    
   
    //因为重定向是两次请求,两次响应,在服务器给定重定向后,实际上只是给了浏览器端一个下一次请求的地址,这时浏览器重新请求,如果jsp在WEB-INF下则会报404错误,因为对于WEB-INF下的jsp文件只能通过请求转发进行,而对于web下的jsp文件能正常进行访问
    //所以想要进行WEB-INF下jsp之间重定向只能通过controller之间跳转一个空的请求转发进行重定向,相当于用户第二次访问的是一个controller:
    
     //原生重定向
	@RequestMapping("/r1")
    public void R2(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.sendRedirect("/WEB-INF/demo2.jsp");
    }
    //SpringMVC重定向:测试结果是不走视图解析器
    @RequestMapping("/r2")
    public String R2() {
        System.out.println("跳转回首页index.jsp");
        return "redirect:index.jsp";
    }
}

RestFul

@Controller
public class RestFulController {

    /**
     * 原生的url:http://localhost:8080/springmvc_04/add?a=1&b=1
     */
    @RequestMapping("/add")
    public String getAdd1(int a, int b, Model model) {
        int result = a + b;
        model.addAttribute("add", "原生的url:结果为" + result);
        return "add";
    }

    /**
     * RestFul方式一:method = get
     * RequestMapping("/addRest/{a}/{b}" method=requestMethod.GET) = @GetMapping()
     * http://localhost:8080/springmvc_04/addRest/1/1
     */
    @GetMapping("/addRest/{a}/{b}")
    public String getAdd2(@PathVariable int a, @PathVariable int b, Model model) {
        int result = a + b;
        model.addAttribute("add", "Rest的url:结果为" + result);
        return "addRest";
    }

    /**
     * 复用相同的url
     * RestFul方式二:method=post,使用RestFul的话,请求的url和GET就一样了
     */
    @PostMapping("/addRest/{a}/{b}")
    public String getAdd3(@PathVariable int a, @PathVariable int b, Model model) {
        int result = a + b;
        model.addAttribute("add", "Rest的url:结果为" + result);
        return "addRest";
    }
}

使用method属性指定请求类型

用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

//映射访问路径,必须是POST请求
@RequestMapping(value = "/hello",method = {RequestMethod.POST})
public String index2(Model model){
   model.addAttribute("msg", "hello!");
   return "test";
}

数据处理

提交的域名称和处理方法的参数名不一致

  • 前端提交的name和后端映射器接受的形参名不用一样,再形参前@RequestParam("xxx")更改名称一致

前端提交表单域,后端直接使用实体类

  • 后端参数封装如果成一个pojo,前端传过来的name会自动pojo中的成员属性,不匹配的属性=null/0

数据显示

ModelAndView

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("name",name);
   return "hello";
}

Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("msg",name);
   return "test";
}

Json

对象互转

要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:

var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//结果是 {a: 'Hello', b: 'World'}

要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:

var json = JSON.stringify({a: 'Hello', b: 'World'});
//结果是 '{"a": "Hello", "b": "World"}'

Jackson

依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
</dependency>

控制层

  • @RestControoler: 该类下所有方法不走视图解析器,返回一个json数据 前后端分离使用

  • @ResponseBody:该方法不走视图解析器,返回一个json数据

@Controller
public class UserController {

   @RequestMapping("/json1")
   @ResponseBody
   public String json1() throws JsonProcessingException {
       //创建一个jackson的对象映射器,用来解析数据
       ObjectMapper mapper = new ObjectMapper();
       //创建一个对象
       User user = new User("秦疆1号", 3, "男");
       //将我们的对象解析成为json格式
       String str = mapper.writeValueAsString(user);
       //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
       return str;
  }
}

FastJson

依赖

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.60</version>
</dependency>

使用

public class FastJsonDemo {
   public static void main(String[] args) {
       //创建一个对象
       User user1 = new User("秦疆1号", 3, "男");
       User user2 = new User("秦疆2号", 3, "男");
       User user3 = new User("秦疆3号", 3, "男");
       User user4 = new User("秦疆4号", 3, "男");
       List<User> list = new ArrayList<User>();
       list.add(user1);
       list.add(user2);
       list.add(user3);
       list.add(user4);
       
       //Java对象 转 JSON字符串
       String str1 = JSON.toJSONString(list);
       // JSON字符串 转 Java对象
       User jp_user1=JSON.parseObject(str2,User.class);

       //Java对象 转 JSON对象
       JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
       //JSON对象 转 Java对象
       User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
   }
}

乱码问题

  • 单个解决
@RequestMapping(value = "/json1",produces = "application/json;charset=utf-8")
  • 配置视图解析器解决
<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       </bean>
       <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               </bean>
           </property>
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

Json时间工具类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

public class JsonUtils {
   
   public static String getJson(Object object) {
       return getJson(object,"yyyy-MM-dd HH:mm:ss");
  }

   public static String getJson(Object object,String dateFormat) {
       ObjectMapper mapper = new ObjectMapper();
       //不使用时间差的方式
       mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
       //自定义日期格式对象
       SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
       //指定日期格式
       mapper.setDateFormat(sdf);
       try {
           return mapper.writeValueAsString(object);
      } catch (JsonProcessingException e) {
           e.printStackTrace();
      }
       return null;
  }
}

Ajax

  • jquery实现
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
    <%--加载动态的JQ资源--%>
    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <script>
        /*
        AJax后端必须懂的东西:
        url:后端接受的地址
        data:后端接受到的请求参数,json数据格式=前后端分离时候,后端传的数据便于前端接受就是json数据
        success:后端接受成功返回的函数
        error:后盾失败接受返回的函数
        */
        function username() {
            $.post({
                url: "${pageContext.request.contextPath}/a1",
                data: {"name": $("#username").val()},
                success: function (data, status) {
                    console.log("data:" + data);
                    console.log("status:" + status);
                }
            })
        }

    </script>
</head>
<body>
<%--实现Ajax异步请求
    1 绑定单击事件
    2 单击事件函数使用Jq:$.post({})
--%>
用户名:<input type="text" id="username" onclick="username()">
</body>
</html>
  • Ajax异步请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Ajax</title>

    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <script>
        /*页面加载完*/
        $(document).ready(function () {
            /*绑定btn单击事件*/
            $("#btn").click(function () {
                /*JQ使用Ajax异步请求*/
                $.post("${pageContext.request.contextPath}/a2", function (data) {
                    // data接受返回的值
                    //console.log(data);
                    var html = "";
                    for (let i = 0; i < data.length; i++) {
                        html += "<tr>" +
                            "<td>" + data[i].name + "</td>" +
                            "<td>" + data[i].age + "</td>" +
                            "<td>" + data[i].sex + "</td>" +
                            +"<tr>"
                    }
                    $("#content").html(html);
                });
            });

        })
    </script>
</head>
<body>
<input type="button" id="btn" value="加载数据">
<table>
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>
    <tbody id="content">

    </tbody>

</table>
</body>
</html>
  • Ajax登录验证
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login</title>
    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <script>
        function nameFun() {
            $.post({
                url: "${pageContext.request.contextPath}/a3",
                data: {"name": $("#name").val()},
                success: function (data) {
                    //console.log(data);
                    if (data.toString() === "用户名成功") {
                        $("#userInfo").css("color", "green");
                    } else {
                        $("#userInfo").css("color", "red");
                    }
                    $("#userInfo").html(data);
                }
            })
        }

        function passwordFun() {
            $.post({
                url: "${pageContext.request.contextPath}/a3",
                data: {"password": $("#password").val()},
                success: function (data) {
                    if (data.toString() === "密码正确") {
                        $("#userPasswordInfo").css("color", "green");
                    }else {
                        $("#userPasswordInfo").css("color", "red");
                    }
                    $("#userPasswordInfo").html(data);
                }
            })
        }
    </script>
</head>
<body>
<p>
    用户名:<input type="text" id="name" onclick="nameFun()">
    <%--span提示信息--%>
    <span id="userInfo"></span>
</p>
<p>
    用户密码:<input type="password" id="password" onclick="passwordFun()">
    <span id="userPasswordInfo"></span>
</p>
</body>
</html>
  • Json乱码解决
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--1 自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.nuc.controller"/>
    <!--2 注解驱动-->
    <mvc:annotation-driven/>
    <!--  使用JQ,使用静态资源过滤需要引用,动态就不需要-->
    <mvc:default-servlet-handler/>
    <!--注解加载映射器、适配器,解决Json数据中文乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <!--3 以上的是定死的代码,以下是配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

拦截器

  • 数据独立性:Servlet中的是过滤器,而拦截器是SpringMVC框架独有的,独享request和response
  • 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css等式不会拦截的
  • 拦截器是基于AOP思想的,和AOP实现是一样的,在application.xml中配置
  <!--拦截器配置-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--
           /**是拦截之后所有的请求,比如/admin/a1/2131
         -->
        <mvc:mapping path="/**"/>
        <bean class="com.nuc.config.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
  • 具体拦截内容
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //return true:执行下一个拦截器
        System.out.println("===========处理前,这里进行拦截处理=================");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("===========处理后,通常进行日志管理=================");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("===========清洁中=================");
    }
}
  • eg(登录拦截)
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        request.getRequestURL();
        //URL:http://localhost:8080/springmvc_07_interceptor/user//main
        System.out.println("URL:" + request.getRequestURL());
        //URI:/springmvc_07_interceptor/user//main
        System.out.println("URI:" + request.getRequestURI());

        if (session.getAttribute("username") == null || session.getAttribute("password") == null) {
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        } else if (session.getAttribute("username").equals("admin") && session.getAttribute("password").equals("123456")) {
            return true;
        }
        if (request.getRequestURI().contains("ogin")) {
            return true;
        }
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        return false;
    }
}

文件上传&下载

  • application.xml配置
 <!--文件上传配置-->
    <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>
  • Controller编写
@RestController
public class FileController {
    @RequestMapping("/upload")
    public String upFile(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
        //设置文件保存路径
        String path = request.getServletContext().getRealPath("/upload");
        System.out.println("path:" + path);
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        System.out.println("上传的文件地址:" + realPath);
        //CommonsMultipartFile的方法写文件,简化
        file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
        return "redirect:/index.jsp";
    }

    @RequestMapping("/upload2")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();

        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)) {
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : " + uploadFileName);

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:" + realPath);

        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); //文件输出流

        //读取写出
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }
    @RequestMapping(value="/download")
    public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
        //要下载的图片地址
        String  path = request.getServletContext().getRealPath("/upload");
        String  fileName = "白帝学园.zip";

        //1、设置response 响应头
        response.reset(); //设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8"); //字符编码
        response.setContentType("multipart/form-data"); //二进制传输数据
        //设置响应头
        response.setHeader("Content-Disposition",
                "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path,fileName);
        //2、 读取文件--输入流
        InputStream input=new FileInputStream(file);
        //3、 写出文件--输出流
        OutputStream out = response.getOutputStream();

        byte[] buff =new byte[1024];
        int index=0;
        //4、执行 写出操作
        while((index= input.read(buff))!= -1){
            out.write(buff, 0, index);
            out.flush();
        }
        out.close();
        input.close();
        return null;
    }
}
  • 前端页面
<form enctype="multipart/form-data" method="post" action="${pageContext.request.contextPath}/upload">
    <input type="file" name="file"/>
    <input type="submit" value="upload">
</form>
<a href="/download">点击下载</a>

@ModelAttribute

解决修改中为赋值对象为空的问题

  1. 利用隐藏域,在用户进行修改的时候,将对应的不能修改的字段进行隐藏

  2. 先从数据库中将用户信息给读取出来,然后将不能修改的字段重新进行赋值然后更新

  3. @ModelAttribute注解,springmvc提前执行取数据操作,再进行修改操作

使用总结

1、注解在没有返回值的方法上面
就如上面的例子注解在没有返回值的方法上面,用在POJO入参上面,或者我们可以在这里设定一些Request域中的对象,在前台可以直接使用。

	@ModelAttribute
    public void getUser(){
            maps.put("name", "zhangsan");
        }

2、注解在有返回值的方法上面,
这里要用value属性进行数据存储,会将返回值存储在value属性的值中,可以在前台直接获取。

    @ModelAttribute(value="user00")
    public User testModelAttribute(){
        User user =new User(1,"tom00", 12,"ff@163.com", "123400");
        return user;
    }

3、在方法参数上使用@ModelAttribute
在方法的参数中使用必须是修饰POJO类,这样的话,才能从相应的域中获取数据

		@RequestMapping("/TestModelArributes")
        public String TestModelArributes(@ModelAttribute( value="change") User user){
            System.out.println("打印"+user);
            return SUCCESS;
        }
posted @   Faetbwac  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示