SpringMVC详解

发布于:2022-12-04 ⋅ 阅读:(1585) ⋅ 点赞:(1)

一、SpringMVC

1.1 什么是SpringMVC

Java开源框架,Spring Framework的一个独立模块

MVC框架,在项目中开辟MVC层次架构

对控制器中的功能包装简化扩展践行工厂模式,功能架构在工厂之上。

1.2 MVC架构

1.2.1 概念

名称 职责
Model 模型:承载数据,并对用户提交请求进行计算的模块。分为两类,一类称为数据承载Bean,一类称为业务处理Bean。所谓数据承载Bean是值实体类,专门承载业务数据的,如Student、User等。而业务处理Bean则是指Service或Dao对象,专门用于处理用户提交请求的
View 视图:渲染数据,生成页面。对应项目中的Jsp , html 等 作用是与用户交互 展示数据
Controller 控制器:用于将用户请求转发给相应的Model进行处理,并处理Model的计算结果向用户提供相应响应

1.2.2 MVC工作流程:

  1. 用户通过View页面向服务端发送请求,可以是表单请求、超链接请求、AJAX请求等。

  2. 服务端Controller控制器接收到请求后对请求进行解析,找到相应的Model对用户请求进行处理。

  3. Model处理后,将处理结果再交给Controller。

  4. Controller接到处理结果后,根据处理结果找到要作为向客户端发回的响应View页面。页面经渲染后,再发给客户端。

1.2.3 优点

  • MVC是现下软件开发中的最流行的代码结构形态;

  • 人们根据负责的不同逻辑,将项目中的代码分成 M V C 3个层次;

  • 层次内部职责单一,层次之间耦合度低;

  • 符合低耦合 高内聚的设计理念。也实际有利于项目的长期维护。

二、开发流程

2.1 导入依赖

<!--——————————加入springMVC依赖—————————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

2.2 配置核心(前端)控制器(web.xml)

作为MVC框架,首先要解决的是:如何能够收到请求!

所以MVC框架大都会设计一款前端控制器,选型在 Servlet 或 Filter两者之一,在框架最前沿率先工作,接收所有请求。

此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。

  • 补充:DispatcherServlet前端控制器,是框架提供的,作用统一处理请求和响应,整个流程的控制中心,是由它来调用其他组件处理用户的请求。

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">
    <!--前端控制器-->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 局部参数:声明配置文件位置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- Servlet启动时刻:可选 -->
        <!--DispatcherServlet前端控制器是springmvc中非常重要的一个组件,内置了很多初始化的工作,所以让他随着服务器的启动而启动
            提前把初始化工作做好,避免第一次访问的时候速度很慢
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <!--
            这里设置的是前端控制器可以处理的请求路径/表示拦截处理jsp以外的所有请求
            也就是说处理以.jsp结尾的请求其他请求都要经过前端控制器,这里注意不要写成/*,/*表示匹配所有路径
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

2.3 后端控制器

等价于之前定义的Servlet。

@Controller // 交由spring创建bean对象
@RequestMapping("/hello") // 访问路径
public class HelloController {

    @RequestMapping("/hello1")
    public String hello(){
        return "index"; // 跳转到index.jsp
    }

    @RequestMapping("/hello2")
    public String hello2(){
        return "pages/main"; // 跳转到pages包下的main.jsp
    }
}

2.4 springmvc.xml文件配置

<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--—— 告知springmvc哪些包中存在被注解的类 ——-->
    <context:component-scan base-package="com.ymk.controller"/>
    <!--—— 注册注解开发驱动 ——-->
    <mvc:annotation-driven/>
    <!-- 视图解析器
         作用:1.捕获后端控制器的返回值="index"
              2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--—— 前缀 ——-->
        <property name="prefix" value="/"/>
        <!--—— 后缀 ——-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

2.5 浏览器访问

       URL地址栏输入:

  • http://localhost:8080/hello/hello1
  • http://localhost:8080/hello/hello2

三、接受请求参数

3.1基本类型参数

       请求参数和方法的形参同名即可。

  • springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss
  • 通过@DateTimeFormat可以修改默认日志格式
    @RequestMapping("/test1")
    public String test1Param(Integer id, String name, Boolean gender,@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
        // springmvc自动接收参数,并且转换参数类型
        System.out.println(id);
        System.out.println(name);
        System.out.println(gender);
        System.out.println(birthday);
        return "index";
    }

浏览器访问: http://localhost:8080/.../test1?id=001&name=张三&gender=true&birthday=2002-12-11

3.2实体收参

请求参数和实体类的属性需要同名。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Boolean gender;
    /**
     * 设置日期格式
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}
    @RequestMapping("/test2")
    public String test2Param(User user){
        System.out.println(user);
        return "index";
    }

浏览器访问:http://localhost:8080/.../test2?id=001&name=张三&gender=true&birthday=2000-11-22

3.3 数组收参

简单类型数组

<form action="http://localhost:8080/param/test3">
    <input type="checkbox" name="hobby" value="fb">足球
    <input type="checkbox" name="hobby" value="bb">篮球
    <input type="checkbox" name="hobby" value="vb">排球
    <input type="submit" value="提交">
</form>
    @RequestMapping("/test3")
    public String test3Param(String[] hobby){
        for (String s : hobby) {
            System.out.println(s);
        }
        return "index";
    }

浏览器访问:http://localhost:8080/.../test3?hobby=football&hobby=basketball 

3.4 集合收参

@Data
public class UserList {
	private List<User> users;
}
@RequestMapping("/test4")
public String testParam4(UserList userList){
    for(User user:userList.getUsers()){
        System.out.println(user);
    }
    return "index";
}

浏览器访问:

post请求:http://.../test4?userList[0].id=111&users[0].name=tom&users[0].gender=false&users[0].birthday=2000-04-02&users[1].id=2&.... 

3.5 路径参数

@RequestMapping("/hello/{id}")
// @PathVariable将{id}路径匹配到值赋给id参数
// 路径名和参数名相同则@PathVariable("id")可简写为 @PathVariable
public String testParam5(@PathVariable("id") Integer id){
    System.out.println("id:"+id);            
    return "index";
}
// http://localhost:8989/.../hello/10   {id}匹配到10

注意:@PathVariable将{id}路径匹配到值赋给id参数

路径名和参数名相同则@PathVariable("id")可简写为 @PathVariable

@RequestMapping("/hello/{username}")
public String testParam6(@PathVariable("username") String name){//将{username}路径匹配到的值赋给name参数
    System.out.println("username:"+name);
    return "index";
}
// http://localhost:8989/.../hello/tom   {username}匹配到tom

3.6中文乱码

       1.页面中字符集统一

  • JSP : <%@page  pageEncoding="utf-8" %>
  • HTML : <meta charset="UTF-8">

2.tomcat中字符集设置,对get请求中,中文参数乱码有效

 3.在web.xml中设置此filter,对post请求中,中文参数乱码有效

<!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
<filter>
    <filter-name>encoding</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>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

四、跳转

4.1转发

@Controller
@RequestMapping("/forw")
public class ForwardController {
    
    @RequestMapping("/test1")
    public String test1(HttpServletRequest request){
        System.out.println("test forward1");
        // 这也是转发,是走视图解析器的转发
        // return "index";

        //请求转发传值通过request.setAttribute
        request.setAttribute("name","zs");
        // 这种是不走视图解析器的转发
        return "forward:/page/success.jsp";
    }

    @RequestMapping("/test2")
    public String testForward2(){
        System.out.println("test forward2");

        // 转发到 /forw/test1
        return "forward:test1"; // 这种是相对路径(转发到本类中的test1)

        // 转发到 /forw/test1
        // return "forward:/forw/test1"; // 这种是绝对路径
    }
}
  • test1中model.addAttribute("name",name),在jsp中取值方式:${name}
  • 注意:转发也可以不走视图解析器,如:forward:/page/success.jsp

4.2重定向

@Controller
@RequestMapping("/redir")
public class RedirectController {

    @RequestMapping("/test1")
    public String test1(HttpSession session){
        System.out.println("test redirect1");

        // 重定向传值通过session.setAttribute
        session.setAttribute("name","ls");
        // 这种是不走视图解析器的转发
        return "redirect:/page/success.jsp";
    }

    @RequestMapping("/test2")
    public String testForward2(){
        System.out.println("test redirect2");
        
        // 重定向到 /redir/test1
        return "redirect:test1"; // 这种是相对路径(转发到本类中的test1)
        
        // 重定向到 /redir/test1
        // return "redirect:/redir/test1"; // 这种是绝对路径
    }
}

4.3 跳转细节

  1. 在增删改之后,为了防止请求重复提交,重定向跳转
  2. 在查询之后,可以做转发跳转

五、传值

C得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面。

  • 转发跳转:    Request作用域
  • 重定向跳转:Session作用域

5.1 Request和Session

转发传给页面值

    @RequestMapping("/test1")
    public String testServlet(HttpServletRequest request, User user){
        user.setName("张三");
        request.setAttribute("user",user);
        System.out.println("scope test1");
        return "forward:/page/success.jsp";
        //jsp中取值方式${user.name}
    }

重定向传给页面值

    @RequestMapping("test2")
    public String testServlet2(HttpSession session,User user){
        user.setName("李四");
        session.setAttribute("user",user);
        System.out.println("scope test1");
        return "forward:/page/success.jsp";
        //jsp中取值方式${user.name}
    }

5.2 JSP中取值

//jsp中用EL表达式 取值即可
<fmt:formatDate value="${user.birth}" pattern="yyyy-MM-dd"/> <br/>
${user.birth} <br>
${age}

5.3Model

    //model中的数据等价于放在了request中,会在V渲染之前,将数据复制一份给request
    @RequestMapping("/test3")
    public String testData(Model model){
        // model中的数据等价于放在了request汇总
        model.addAttribute("name", "王二1");

        //转发带.jsp不走视图解析器
        return "forward:/page/success.jsp";
        //jsp中取值方式${name}
    }

5.4 ModelAndView

 //modelandview 封装了model和view
    @RequestMapping("/test4")
    public ModelAndView testData(){//返回值类型为ModelAndView
        //新建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();

        // 使用model存储数据
        modelAndView.addObject("age",18);

        // 设置视图名,即如何跳转
        modelAndView.setViewName("page/success"); //等价 return "page/success";
        return modelAndView;
        //jsp中取值方式${age}
    }

5.5 @SessionAttributes[不用]

  • @SessionAttributes({"gender","name"}) :model中的 name和gender 会存入session中

  • SessionStatus 移除session

@Controller
@SessionAttributes({"gender","name"}) // model中的 name和gender 会存入session中
public class UserController {
    @RequestMapping("/hello")
    public String hello(Model m){
        m.addAttribute("gender",true); // 会存入session
        mv.addObject("name","zhj"); // 会存入session
        return "index";
    }
    @RequestMapping("/hello2")
    public String hello(SessionStatus status){
        // 移除通过SessionAttributes存入的session
        status.setComplete();
        return "index";
    }
}

5.6 补充

使用Map和ModelMap也可以在页面中也可以通过${user}取值。

    @RequestMapping("/test5")
    public String test5(Map<String,Object> map){
        User user = new User();
        user.setName("阿伟");
        user.setBirthday(new Date());
        map.put("user", user);
        return "page/success";
    }
    @RequestMapping("/test6")
    public String test6(ModelMap map){
        User user = new User();
        user.setName("阿伟");
        user.setBirthday(new Date());
        map.put("user", user);
        return "page/success";
    }

六、静态资源

6.1静态资源问题

静态资源:html,js文件,css文件,图片文件

静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 "/",是全局默认的Servlet.。所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可

在SpringMVC中DispatcherServlet也采用了 “/” 作为url-pattern, 则项目中不会再使用全局的Serlvet,则静态资源不能完成访问。

6.1.2 解决方案1

如果DispathcerServlet采用其他的url-pattern,web.xml中所有访问handler的路径都要以action结尾!!

<servlet>
  	<servlet-name>mvc9</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>mvc9</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

6.1.3 解决方案2【常用】

DispathcerServlet的url-pattern依然采用 "/",但追加配置

在springmvc.xml文件中配置以下内容。

<!-- 
  额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
  所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
  处理方式:将请求转会到tomcat中名为default的Servlet
  -->
<mvc:default-servlet-handler/>

6.1.4解决方案3

  • mapping是访问路径,location是静态资源存放的路径

  • 将/html/** 中 /**匹配到的内容,拼接到 /hhh/后 http://..../html/a.html 访问 /hhh/a.html

<mvc:resources mapping="/html/**" location="/hhh/"/>

七、Json处理

HttpMessageConverter 提供了两个注解:

  • @RequestBody 可以获取请求体,需要在控制器的方法形参上使用,使用请求体中的参数给当前形参赋值,将请求体中的json数据转为java对象
  • @ResponseBody 作用在控制器的方法上面,把响应的数据转为json两个类

7.1导入依赖

<!-- Jackson springMVC默认的Json解决方案选择是 Jackson,所以只需要导入jackson的jar,即可使用。-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

7.2 使用 @ResponseBody

@Controller
@RequestMapping("/json")
public class JsonController {
    /**
     * @ResponseBody 将handler返回值, 转为json(jackson), 并响应到客户端
     */
    @ResponseBody
    @RequestMapping("/test1")
    public User test1() {
        User user = new User(1, "张三", true, new Date());
        return user;
    }

    /**
     * @ResponseBody 还可以用在handler的返回值上
     */
    @RequestMapping("/test2")
    public @ResponseBody List<User> test2() {
        User user = new User(1, "李四", true, new Date());
        User user2 = new User(2, "李四", true, new Date());
        User user3 = new User(3, "王二", true, new Date());
        List<User> users = new ArrayList<>();
        users.add(user);
        users.add(user2);
        users.add(user3);
        return users;
    }

    /**
     * 如果返回值已经是字符串,则不需要转json,直接将字符串响应给客户端
     */
    @ResponseBody
    @RequestMapping(value="/test3",produces = "text/html;charset=utf-8") //produces 防止中文乱码
    public String test3(){
        System.out.println("hello world");
        return "你好";
    }
}

7.3 使用@RestController

Controller类上加了@RestController注解,等价于在类中的每个方法上都加了@ResponseBody.

@Controller
@RestController // 等价于 @Controller + @ResponseBody
public class JsonController{
    @RequestMapping("/test1")
    public User test1() {
        User user = new User(1, "张三", true, new Date());
        return user;
    }

 @RequestMapping("/test2")
    public List<User> test2() {
        User user = new User(1, "李四", true, new Date());
        User user2 = new User(2, null, true, new Date());
        User user3 = new User(3, "王二", true, new Date());
        List<User> users = new ArrayList<>();
        users.add(user);
        users.add(user2);
        users.add(user3);
        return users;
    }
}

7.4 使用@RequestBody

@RequestBody接受参数

7.4.1 定义Handler

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Boolean gender;
    private Date birthday;
}
    /**
     * @RequestBody 将请求体中的json数据转换为java
     */
    @RequestMapping("/test4")
    public User test4(@RequestBody User user){
        // 前端必须发送 json格式的字符串 后端才可以使用@RequestBody接受
        return user;
    }

 注意:前端必须发送json格式的字符串,后端才可以使用@RequestBody接受。

7.4.2 Ajax发送json

<button id="btn">ajax请求</button>
<script>
    $("#btn").click(function () {
        var user = {id: 1, name: "ls"};
        // 将 json对象 转为 json串
        var userStr = JSON.stringify(user);
        // 将 json串 转为 json对象
        var userObj = JSON.parse(userStr);
        $.ajax({
            url: "http://localhost:8080/json/test4",
            type: "post",
            contentType:"application/json",
            data: userStr,
            success: function (res) {
                console.log(res)
            }
        })
    })
</script>
  • JSON.stringify(xx):将 json对象 转为 json串
  • JSON.parse(xx):将 json串 转为 json对象

7.5 Jackson常用注解

7.5.1 日期格式化

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Boolean gender;
    // 设置日期格式,需要加上时区
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date birthday;
}

7.5.2 属性名修改 

@JsonProperty("new_name")

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    
    // 响应时用新的属性名username
    @JsonProperty("username")
    private String name;
    private Boolean gender;
    private Date birthday;
}

 7.5.3属性忽略

@JsonIgnore

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    
    // 忽略gender属性
    @JsonIgnore
    private Boolean gender;
    private Date birthday;
}

 7.5.4 null和empty属性排除

Jackson 默认会输出null值的属性,如果不需要,可以排除。

  • @JsonInclude(JsonInclude.Include.NON_NULL):null值 属性不输出
  • @JsonInclude(value= JsonInclude.Include.NON_EMPTY):empty属性不输出( 空串,长度为0的集合,null值)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;

    // 若"name==null" 忽略此属性
    @JsonInclude(JsonInclude.Include.NON_NULL) 
    private String name;
    private Boolean gender;

    // 若birthday长度为0或==null 忽略此属性
    @JsonInclude(value= JsonInclude.Include.NON_EMPTY) 
    private Date birthday;
}

7.5.5 自定义序列化

@JsonSerialize(using = MySerializer.class):使用MySerializer输出某属性

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;

    // 再次输出此属性时,使用MySerializer输出
    @JsonSerialize(using = MySerializer.class)
    private Double salary;
    private Boolean gender;
    private Date birthday;
}

 创建自定义序列化器。

public class MySerializer extends JsonSerializer<Double> {
    // value即 Double salary的值
    @Override
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {

        // 将Double salary的值 四舍五入
        String number = BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_UP).toString();

        // 输出 四舍五入后的值
        gen.writeNumber(number);
    }
}

7.6 FastJson

7.6.1 导入依赖

<!-- FastJson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.54</version>
</dependency>

7.6.2 安装FastJson

<mvc:annotation-driven>
    <!-- 安装FastJson,转换器 -->
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <!-- 声明转换类型:json -->
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

7.6.3 使用

@ResponseBody @RequestBody @RestController 使用方法不变

7.6.4 常用注解

  • 日期格式化:@JSONField(format="yyyy/MM/dd")

  • 属性名修改:@JSONField(name="birth")

  • 忽略属性:    @JSONField(serialize = false)

  • 包含null值:

    • @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue):默认会忽略所有null值,有此注解会输出null

    • @JSONField(serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty):null的String输出为""

  • 自定义序列化:@JSONField(serializeUsing = MySerializer2.class)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable{
	@JSONField(serialize = false)
    private Integer id;

    @JSONField(name="NAME",serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
	private String name;

    @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue) 
    private String city;

	@JSONField(format="yyyy/MM/dd")
	private Date birth;

    @JSONField(serializeUsing = MySerializer2.class)
    private Double salary;
}
public class MySerializer2 implements ObjectSerializer {
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        // salary属性值
        Double value = (Double) object;
        // 在salary后拼接 “元”
        String text = value + "元";
        // 输出拼接后的内容
        serializer.write(text); 
    }
}
new User(1,null,null,new Date(),100.5);
// 如上对象,转换json:
{NAME:"",city:null,"birth":"2020/12/12","salary":"100.5元"}

八、异常解析器

8.1 现有方案,分散处理

Controller中的每个Handler自己处理异常

  • 此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理
public String xxx(){
    try{
    	...
    }catch(Exception1 e){
    	e.printStackTrace();
        return "redirect:/xx/error1";
    }catch(Exception2 e){
    	e.printStackTrace();
        return "redirect:/xx/error2";
    }
}

8.2 异常解析器,统一处理

Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。

定义一个 “异常解析器” 集中捕获处理所有异常

  • 此种方案,在集中管理异常方面,更有优势!
public class MyExResolver implements HandlerExceptionResolver{
	/**
	 * 异常解析器:主体逻辑
	 * 执行时刻:当handler中抛出异常时,会执行:捕获异常,并可以跳到错误页面
	 */
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		ex.printStackTrace();//打印异常栈
		//创建一个ModelAndView
		ModelAndView mv = new ModelAndView();
		//识别异常
		if (ex instanceof Exception1) {
			mv.setViewName("redirect:/xxx/error1");
		}else if(ex instanceof Exception2){
			mv.setViewName("redirect:/xxx/error2");
		}else{
			mv.setViewName("redirect:/xxx/error");
		}
		return mv;
	}
}
<!-- 声明异常解析器 -->	
<bean class="com.baizhi.exception.resolver.MyExResolver"></bean>

8.3 自定义异常

package com.glls.exception;

import java.io.Serializable;

public class CustomerException extends Exception implements Serializable {

    private static final long serialVersionUID = -5212079010855161498L;

    public CustomerException() {
    }

    public CustomerException(String message) {
        super(message);
    }
}

8.4 自定义异常解析器1

进行页面跳转的全局异常处理的方式,开发中不常用。

package com.ymk.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 进行页面跳转的全局异常处理的方式,开发中不常用
 */
public class CustomExceptionResolver implements HandlerExceptionResolver {
    /**
     * 异常解析器:主体逻辑
     * 执行时刻:当handler中抛出异常时,会执行:捕获异常,并可以跳到错误页面
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        CustomerException customerException = null;
        ModelAndView modelAndView = new ModelAndView();

        ex.printStackTrace();
        if (ex instanceof  CustomerException){

            // 预料之中的异常
            customerException = (CustomerException) ex;
            modelAndView.addObject("error",customerException.getMessage());
        } else {

            // 如果系统抛出的异常,不是自定义异常,可以重新构造一个未知错误异常
            modelAndView.addObject("error","对不起,服务器繁忙,请联系管理员");
        }
        modelAndView.setViewName("page/error");
        return modelAndView;
    }
}

 在springmvc.xml中添加配置。

<!-- 声明异常解析器 -->	
 <bean id="handlerExceptionResolver" class="com.glls.exception.CustomExceptionResolver"/>

8.5 自定义异常解析器2【常用】

向前端响应json的全局异常处理方式,开发中常用。

封装一个后端给前端响应的数据结构,封装了状态码、信息等。

先创建一个枚举类用来定义提示信息。

package com.ymk.common;

import lombok.Getter;
@Getter // get方法
public enum ResultCodeEnum {
    //枚举值
    SUCCESS(true,"操作成功",20000),
    UNKNOWN_REASON(false,"操作失败",999),
    BAD_SQL_GRAMMAR(false,"sql语法错误",520),
    ERROR(false,"操作失败",444);

    private Boolean success;
    private String message;
    private Integer code;

    ResultCodeEnum(Boolean success, String message, Integer code) {
        this.success = success;
        this.message = message;
        this.code = code;
    }
}
package com.ymk.common;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * 封装一个后端给前端响应的数据结构,封装了状态码、信息等
 */
@Data
public class R {

    /**
     * 响应的状态码
     */
    private Integer code;

    /**
     * 响应的信息
     */
    private String message;

    /**
     * 是否成功
     */
    private Boolean success;

    /**
     * 封装响应的数据
     */
    private Map<Object, Object> data = new HashMap<>();

    public R() {
    }

    /**
     * 返回成功的结果
     * @return
     */
    public static R ok() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }

    /**
     * 返回失败的结果
     * @return
     */
    public static R error() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.ERROR.getSuccess());
        r.setCode(ResultCodeEnum.ERROR.getCode());
        r.setMessage(ResultCodeEnum.ERROR.getMessage());
        return r;
    }

    public static R setResult(ResultCodeEnum resultCodeEnum) {
        R r = new R();
        r.setSuccess(resultCodeEnum.getSuccess());
        r.setCode(resultCodeEnum.getCode());
        r.setMessage(resultCodeEnum.getMessage());
        return r;
    }

    /**
     * 创建一个R对象去调success方法,然后把失败或成功的值传进去
     */
    public R success(Boolean success) {
        this.setSuccess(success);
        return this;
    }

    /**
     * 创建一个R对象去调message方法,然后把信息传进去
     */
    public R message(String message) {
        this.setMessage(message);
        return this;
    }

    /**
     * 创建一个R对象去调code方法,然后把状态码传进去
     */
    public R code(Integer code) {
        this.setCode(code);
        return this;
    }

    /**
     * 创建一个R对象去调data方法,封装一个键值
     */
    public R data(Object key, Object value) {
        this.data.put(key, value);
        return this;
    }

    /**
     * 创建一个R对象去调data方法,封装一个map
     */
    public R data(Map<Object, Object> map) {
        this.setData(map);
        return this;
    }
}
package com.ymk.exception;

import com.ymk.common.R;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
 * 向前端响应json的全局异常处理方式,开发中常用。
 */
@Component
@ControllerAdvice  // 对controller 进行增强
@ResponseBody
public class CustomExceptionResolver2 {

    /**
     * 自定义异常
     * 在@ExceptionHandler里面指定当前方法要处理的异常
     */
    @ExceptionHandler(CustomerException.class) // 处理自定义异常
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R handleEx(CustomerException customerException){

        return R.error().message(customerException.getMessage());
    }

    /**
     * 除零异常
     */
    @ExceptionHandler(ArithmeticException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R handleEx(ArithmeticException arithmeticException){

        return R.error().message("除零异常");
    }

    /**
     * 缺少请求参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R handleHttpMessageNotReadableException(
            MissingServletRequestParameterException ex) {
        return R.error().message("缺少必要的请求参数");
    }

    /**
     * 系统异常 预期以外异常
     *
     * 项目中,我们一般都会比较详细的去拦截一些常见异常,拦截 Exception 虽然可以一劳永逸,
     * 但是不利于我们去排查或者定位问题。实际项目中,可以把拦截 Exception 异常写在 GlobalExceptionHandler
     * 最下面,如果都没有找到,最后再拦截一下 Exception 异常,保证输出信息友好。
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R handleEx(Exception Exception){

        return R.error().message("请联系管理员");
    }
}

九、拦截器

9.1 作用

作用:抽取handler中的冗余功能

9.2 定义拦截器

         执行顺序:

  1. preHandle:前置拦截,请求到达 handler(controller) 之前进行拦截,方法的返回值是boolean类型,返回true就放行 , 返回false就拦截。
  2. postHandle:后置拦截,是controller执行后响应回来执行的方法,是在视图解析器解析视图之前执行的,所以在数据渲染之前在ModelAndView中再次渲染修改数据。
  3. afterCompletion:在页面渲染完之后执行,整个请求都执行结束了才执行,可以在这个方法中做一些收尾操作,如:记录日志、资源清理,还可以得到请求中出现的异常对象,对异常进行处理分析。
  • 前端请求路径顺序:过滤器—>Servlet(前端控制器)—>拦截器—>Controller
package com.ymk.interceptor;

import com.ymk.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 配置拦截器步骤:
 * 1.创建拦截器类
 * 2.将拦截器交给spring容器
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 前置拦截 请求到达 handler(controller) 之前进行拦截
     * 方法的返回值是boolean类型,返回true就放行 , 返回false就拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器的前置拦截--在请求到达目标controller 之前 进行拦截");
        // true 表示放行  false  表示拦截,不放行
        return true; // 放行 后续的拦截器或controller就会执行

        // 结论:可以在这个放法中对请求进行条件判断,满足条件返回true放行,不满足返回false
    }

    /**
     * 后置拦截,是controller执行之后响应回来执行的方法
     * 是在视图解析器解析视图之前执行的,所以在数据渲染之前在ModelAndView中
     * 再次渲染修改数据
     * @param modelAndView:封装了要跳转的页面以及数据
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 获得将要跳转的视图名称
        String viewName = modelAndView.getViewName();
        System.out.println(viewName);

        // controller执行后响应回来的数据可以获取到,也可以进行修改
        Map<String, Object> model = modelAndView.getModel();

        // 获取响应的值
        User user = (User) model.get("user");
        System.out.println(user);

        // 修改响应的值
        user.setName("ls");
        System.out.println("后置拦截--");
    }

    /**
     * 在页面渲染完之后执行,整个请求都执行结束了才执行,
     * 可以在这个方法中做一些收尾操作,如:记录日志、资源清理
     * 还可以得到请求中出现的异常对象,对异常进行处理分析
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after----");
    }
}

9.3 配置拦截路径

在springmvc.xml中配置拦截器。

    <!--——配置拦截器——-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--——配置拦截的路径——-->
            <mvc:mapping path="/inter/test1"/>
            <mvc:mapping path="/inter/test2"/>
            <mvc:mapping path="/inter/test*"/> <!-- test开头 -->
            <mvc:mapping path="/inter/*"/>     <!-- inter/开头 -->
            <mvc:mapping path="/**"/> <!-- /** 任意多级任意路径 -->
            <mvc:exclude-mapping path="/inter/login"/>   <!--不拦截此路径-->
            <bean class="com.ymk.interceptor.MyInterceptor"></bean>   <!--拦截器类-->
        </mvc:interceptor>
    </mvc:interceptors>

9.4登录拦截

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("判断有没有登录");
        HttpSession session = request.getSession();

        if (session.getAttribute("name") != null){
            System.out.println("登录成功");
            return true;//session有值不拦截,返回true
        } else {
            //没有登录重定向到首页
            System.out.println("没有登录");

            session.setAttribute("error","请先登录");
            response.sendRedirect(request.getContextPath()+"/index.jsp");
            return false;//拦截返回false
        }

    }
}

Controller层

@Controller
@RequestMapping("/login")
public class UserController {
    @RequestMapping("/login1")
    public String login(HttpSession session, Model model, String name, String password){
        System.out.println("用户名和密码:"+name+":"+password);

        //登录成功将name存到域里
        if (name.equals("jack") && password.equals("123")){
            session.setAttribute("name",name);
            return "house/add";//跳转到add.jsp中
        } else {
            model.addAttribute("error","用户名或密码错误");
            return "index";//跳转到登录界面
        }
    }
}

十、上传与下载

文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver

10.1 文件上传三要素

  1. 表单的提交方式 method="POST"

  2. 表单的enctype属性是多部分表单形式 enctype=“multipart/form-data"

  3. 表单项(元素)type="file"

<form action="/upload" enctype="multipart/form-data" method="post">
    用户:<input type="text" name="username"> <br />
    请选择要上传的文件:<input type="file" name="file"/><br />
    <input type="submit" value="upload">
</form>

10.2 enctype 属性值

  • application/x-www-form-urlencoded

默认方式只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式

  • multipart/form-data

这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

  • text/plain

除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件

注意一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。

10.3文件上传的方式

  1. 使用apache提供的工具类 commons-fileupload(麻烦)

  2. 使用servlet3.0版本,通过注解使用

  3. 使用SpirngMVC的MultipartResolver

Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类,因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

10.4 文件上传操作步骤

10.4.1 导入依赖

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
​
<!--servlet  需要这个依赖  并且 tomcat8 版本 要不会有 request 转换异常-->  
<dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>4.0.1</version>
     <scope>provided</scope>
</dependency>

10.4.2 编写表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <!--单文件上传-->
       <form action="${pageContext.request.contextPath}/upload" 
             enctype="multipart/form-data" method="post">
            用户:<input type="text" name="username"> <br />
            请选择要上传的文件:<input type="file" name="file"/><br />
            <input type="submit" value="上传">
        </form>
    </body>
</html>

10.4.3 配置文件上传组件

springmvc.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"/>
    </bean>

10.4.4 编写FileController 

package com.ymk.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping("/file")
public class FileController {
    
    // MultipartFile file:对应接受上传表单的文件项
    @RequestMapping("/upload")
    public String upload(MultipartFile file, String username, HttpServletRequest request) throws IOException {
        // 得到文件名
        String originalFilename = file.getOriginalFilename();

        // 假如对文件类型有要求可以做简单的逻辑判断
        // 获得文件后缀
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        // 文件的后缀是jpg 或者 png 才接受上传
        if (suffix.equalsIgnoreCase(".jpg") || suffix.equalsIgnoreCase(".png")) {
            String realPath = request.getServletContext().getRealPath("/");

            // 得到上传文件存放的路径
            File uploadDir = new File(realPath, "upload");

            // 如果路径不存在就创建
            if (!uploadDir.exists()){
                uploadDir.mkdirs();
            }

            // 把上传的文件写到上传目录中去,上传之后的文件名称根据需求看是否修改
            // 使用封装好的方法,底层是 io 流
            file.transferTo(new File(uploadDir, System.currentTimeMillis() + suffix));
            request.setAttribute("msg","文件上传成功");


            // 得到上传目录下所有的文件名
            String[] list = uploadDir.list();
            List<String> filerNames = Arrays.asList(list);

            // 把文件名放在域对象,跳转到页面展示
            request.setAttribute("fileNames",filerNames);
        
        } else {
            request.setAttribute("msg", "文件格式错误");
        }
        return "page/result";
    }

}

 result.jsp文件上传上去后的展示。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>上传的文件展示</title>
</head>
<body>
${msg}<br>
<c:forEach items="${fileNames}" var="fileName">
<img style="height: 100px;width: 100px" src="http://localhost:8080/upload/${fileName}">
</c:forEach>
</body>
</html>

10.4.5 多文件上传

<form action="${pageContext.request.contextPath}/method2" method="post" enctype="multipart/form-data" >
    用户名: <input type="text" name="name"> <br>
    文件: <input type="file" name="upload">
    文件: <input type="file" name="upload">
    文件: <input type="file" name="upload"><br>
    <input type="submit" value="提交">
</form>
    /**
     * 多文件上传
     */
    @RequestMapping("/upload2")
    public String upload2(MultipartFile[] files, String username, HttpServletRequest request) throws IOException {

        for (MultipartFile file : files) {

            // 得到文件名
            String originalFilename = file.getOriginalFilename();

            // 假如对文件类型有要求可以做简单的逻辑判断
            // 获得文件后缀
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

            // 文件的后缀是jpg 或者 png 才接受上传
            if (suffix.equalsIgnoreCase(".jpg") || suffix.equalsIgnoreCase(".png")) {
                String realPath = request.getServletContext().getRealPath("/");

                // 得到上传文件存放的路径
                File uploadDir = new File(realPath, "upload");

                // 如果路径不存在就创建
                if (!uploadDir.exists()) {
                    uploadDir.mkdirs();
                }

                // 把上传的文件写到上传目录中去,上传之后的文件名称根据需求看是否修改
                // 使用封装好的方法,底层是 io 流
                file.transferTo(new File(uploadDir, System.currentTimeMillis() + suffix));
                request.setAttribute("msg", "文件上传成功");

            } else {
                request.setAttribute("msg", "文件格式错误");
            }
        }
        return "page/result";
    }
}

10.5 文件下载

    @RequestMapping("/download")
    // fileName是指jsp页面中点击下载带过来的fileName
    public void download(String fileName, HttpServletResponse response,HttpServletRequest request) throws IOException {
        // 告诉浏览器以下载的方式来打开
        // 设置响应头,告诉浏览器如何处理这个数据
        response.setHeader("content-disposition","attachment;filename="+fileName);
        System.out.println(fileName);

        // 输出流,目的地指向浏览器
        ServletOutputStream outputStream = response.getOutputStream();
        String realPath = request.getServletContext().getRealPath("/upload");

        // 得到要下载的文件对象
        File file = new File(realPath, fileName);

        // 把文件对象转为字节数组
        byte[] bytes = FileUtils.readFileToByteArray(file);

        // 指向浏览器的输出流,发送一个字节数组到客户端浏览器
        outputStream.write(bytes);
        outputStream.close();
    }
}

十一、验证码

11.1 作用

防止暴力攻击,前端安全保障

11.2 加入依赖

<!-- Kaptcha -->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

11.3 声明验证码组件

写在web.xml中

<servlet>
    <servlet-name>cap</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
    <init-param>
      <param-name>kaptcha.border</param-name>
      <param-value>no</param-value>
    </init-param>
    <init-param>
      <param-name>kaptcha.textproducer.char.length</param-name>
      <param-value>4</param-value>
    </init-param>
    <init-param>
      <param-name>kaptcha.textproducer.char.string</param-name>
      <param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>
    </init-param>
    <init-param>
      <param-name>kaptcha.background.clear.to</param-name>
      <param-value>211,229,237</param-value>
    </init-param>
    <init-param>
      <!-- session.setAttribute("captcha","验证码") -->
      <param-name>kaptcha.session.key</param-name>
      <param-value>captcha</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>cap</servlet-name>
    <url-pattern>/captcha</url-pattern>
  </servlet-mapping>

11.4 Page

<img src="${pageContext.request.contextPath}/captcha" style="width:85px" id="cap"/>
<script>
    $(function(){
        $("#cap").click(function(){
            //刷新验证码
            path = $(this).attr("src")+"?"+new Date().getTime();
            $(this).attr("src",path);
        });
    });
</script>

11.5登录实例

前端

<body>
<h1>登录页面</h1>
<form action="${pageContext.request.contextPath}/login/login1" method="post">
    用户名:<input type="text" name="name"><br>
    密码:<input type="password" name="password"><br>
    验证码:<input  type="text" name="captcha">
    <img src="${pageContext.request.contextPath}/captcha" style="width:85px" id="cap"/><br>
    <input type="submit" value="登录">
</form>
提示:${error}
</body>
<script src="js/jquery-3.6.0.js"></script>
<script>
    $(function(){
        $("#cap").click(function(){
            //刷新验证码
            path = $(this).attr("src")+"?"+new Date().getTime();
            $(this).attr("src",path);
        });
    });
</script>

后端

@Controller
@RequestMapping("/login")
public class UserController {
    @RequestMapping("/login1")
    public String login(HttpSession session, Model model, String name, String password,String captcha){
        System.out.println("用户名和密码:"+name+":"+password);
        //通过session取出来验证码
        String captcha1 = (String) session.getAttribute("captcha");
        //判断验证码与用户输入是否相等
        if (!captcha1.equals(captcha)){
            //不相等给出提示
            model.addAttribute("error","验证码错误");
            return "index";
        }
        if (name.equals("jack") && password.equals("123")){//登录成功将name存到域里
            session.setAttribute("name",name);
            return "house/add";//跳转到add.jsp中
        } else {
            model.addAttribute("error","用户名或密码错误");
            return "index";//跳转到登录界面
        }
    }
}

十二、REST

12.1 开发风格

是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTFUL。

两个核心要求:

  • 每个资源都有唯一的标识(URL)

  • 不同的行为,使用对应的http-method

访问标识 资源
http://localhost:8989/xxx/users 所有用户
http://localhost:8989/xxx/users/1 用户1
http://localhost:8989/xxx/users/1/orders 用户1的所有订单
请求方式 标识 意图
GET http://localhost:8989/xxx/users 查询所有用户
POST http://localhost:8989/xxx/users 在所有用户中增加一个
PUT http://localhost:8989/xxx/users 在所有用户中修改一个
DELETE http://localhost:8989/xxx/users/1 删除用户1
GET http://localhost:8989/xxx/users/1 查询用户1
GET http://localhost:8989/xxx/users/1/orders 查询用户1的所有订单
POST http://localhost:8989/xxx/users/1/orders 在用户1的所有订单中增加一个

12.2 优点

输出json:

12.3 使用

12.3.1 定义Rest风格的 Controller

@RequestMapping(value="/users",method = RequestMethod.GET)

等价于

@GetMapping("/users")

@RestController
public class RestController {
    @GetMapping("/users")
    public List<User> queryAllUsers(){
        System.out.println("get");
        List<User> users = ....
        return users;
    }
​
    @PostMapping("/users")
    public String addUser(@RequestBody User user){
        System.out.println("Post user :"+user);
        return "{status:1}";
    }
    
    @PutMapping("/users")
    public String updateUser(@RequestBody User user){
        System.out.println("Put user" user:"+user);
        return "{status:1}";
    }
​
    @GetMapping("/users/{id}")
    public String queryOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
        System.out.println("Get user id:"+id);
        return "{status:1}";
    }
​
    @DeleteMapping("/users/{id}")
    public String deleteOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
        System.out.println("delete user id:"+id);
        return "{status:1}";
    }
}

12.3.2 Ajax请求

<script>    
    function putUser(){ // 发送更新请求 (增加请求发送方式也是如此)
        var xhr = new XMLHttpRequest();
        //定义 put,delete,get,post方式 即可,不用定义_method
        xhr.open("put","${pageContext.request.contextPath}/rest04/users");
        // 设置请求头
        xhr.setRequestHeader("content-type","application/json");
        // 设置请求参数
        var user = {id:1,NAME:"shine",city:"bj","birth":"2020/12/12","salary":100.5};
        xhr.send(JSON.stringify(user));
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4 && xhr.status==200){
                var ret = xhr.responseText;
                // 解析json,并输出
                console.log(JSON.parse(ret));
            }
        }
        /*$.ajax({
            url:'${pageContext.request.contextPath}/rest04/users',
            type:'put',
            contentType:"application/json",//声明请求参数类型为 json
            data:JSON.stringify(user),// 转换js对象成json
            success:function(ret){
                console.log(JSON.parse(ret));
            }
        });*/
    }
​
    function delUser(){  // 发送删除请求
        var xhr = new XMLHttpRequest();
        //定义 put,delete,get,post方式 即可,不用定义_method
        xhr.open("delete","${pageContext.request.contextPath}/rest04/users/1");
        xhr.send();
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4 && xhr.status==200){
                var ret = xhr.responseText;
                console.log(JSON.parse(ret));
            }
        }
    }
</script>

十三、跨域请求

13.1 域

域:协议+IP+端口

13.2 Ajax跨域问题

  • Ajax发送请求时,不允许跨域,以防用户信息泄露。

  • Ajax跨域请求时,响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。

  • 互相信任的域之间如果需要ajax访问,(比如前后端分离项目中,前端项目和后端项目之间),则需要额外的设置才可正常请求

13.3 解决方案

方案1:

  • 在被访问方的Controller类上,添加注解(写前端的域)

@CrossOrigin("*") //允许此域发请求访问
public class SysUserController {
    ....
}

方案2:

  • 使用 request 和 response 设置 
  • 其中origin就是前端的域名
        // 允许的跨域
        String origin = request.getHeader("Origin");
        // 允许携带Cookie
        response.setHeader("Access-Control-Allow-Origin",origin);

  • 携带对方cookie,使得session可用

  • 在访问方,ajax中添加属性:withCredentials: true

$.ajax({
     type: "POST",
     url: "http://localhost:8989/web/sys/login",
     ...,
     xhrFields: {
       // 跨域携带cookie
       withCredentials: true
     }
});
或
var xhr = new XMLHttpRequest();
// 跨域携带cookie
xhr.withCredentials=true;

十四、SpringMVC执行流程

  • DispatcherServlet : 前端控制器 不需要程序员开发 由springmvc框架提供的,它的作用是 统一处理请求和响应 整个流程的控制中心 ,由他来调度其他组件 处理用户的请求
  • HandlerMapping: 处理器映射器 不需要程序员开发 有框架提供, 它的作用是根据 url ,method 等 信息 查找 handler 准确点说是 controller中的方法
  • Handler : 需要程序员开发的 controller
  • HandlerAdapter: 处理器适配器 不需要程序员开发 对handler 方法 进行 调用
  • ViewResolver: 视图解析器 不需要程序员开发 进行视图解析
  • View: 视图
  • Model: 数据

十五、Spring整合

15.1 整合思路

  • DispatcherServlet 启动的springMVC工厂== 负责生产C及springMVC自己的系统组件

  • ContextLoaderListener 启动的spring工厂== 负责生产其他所有组件

  • springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件

  • 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可

15.2 整合技巧

两个工厂不能有彼此侵入,即生产的组件不能有重合。

<!-- 告知SpringMVC  哪些包中 存在 被注解的类
	use-default-filters=true 凡是被 @Controller @Service  @Repository注解的类,都会被扫描
	use-default-filters=false 默认不扫描包内的任何类, 只扫描include-filter中指定的类
	只扫描被@Controller注解的类
-->
<context:component-scan base-package="com.zhj" use-default-filters="false">
 	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 告知Spring
     唯独不扫描@Controller注解的类 -->
<context:component-scan base-package="com.zhj" use-default-filters="true">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

15.3 ssm整合

15.3.1 创建maven的web工程ssm

15.3.2 添加依赖

<dependencies>
        <!--—————————mysql架包—————————-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>

        <!--————————mybatis架包————————-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <!--————————log4j日志架包————————-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!--————————加入servlet依赖(servlet的jar)————————-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <!--———————jsp的依赖(jsp相关的jar加进来)————————-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!--——————jstl架包——————-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--——————注解@Test测试类架包——————-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--——————逆向工程架包——————-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!--——————spring-context依赖中关联了其他核心依赖——————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--——————spring和mybatis整合时,需要的架包——————-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!--——————————加入springMVC依赖—————————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

        <!--——————自动生成get、set、构造方法等——————-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <!--————————导入aop依赖————————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--————————导入切面aspect依赖————————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--——————spring事务—————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.14.RELEASE</version>
        </dependency>

        <!--——————spring操作数据库——————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.14.RELEASE</version>
        </dependency>

        <!--——————spring测试相关依赖——————-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.0.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!--——————阿里的连接池——————-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

        <!--——————jackson架包——————-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.0</version>
        </dependency>

        <!--——————分页插件—————-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.0</version>
        </dependency>
        <!--——————文件上传—————-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>

        <!--——————验证码组件—————-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>javax.servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>


    <!--——把xml文件放在Java里会读取不到,通过下方配置可以读取——-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>*.xml</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>

        <!--——————————————————————————————————-->
        <!--——————————逆向工程的插件配置——————————-->
        <!--——————————————————————————————————-->
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>

                <!--————————指定资源文件的路径————————-->
                <configuration>
                    <configurationFile>src\main\resources\generator.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.47</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

 15.3.3 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">

    <!-- 上下文参数 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- spring 配置文件 -->
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- 封装了一个监听器,帮助加载 Spring 的配置文件 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--前端控制器-->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 局部参数:声明配置文件位置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- Servlet启动时刻:可选 -->
        <!--DispatcherServlet前端控制器是springmvc中非常重要的一个组件,内置了很多初始化的工作,所以让他随着服务器的启动而启动
            提前把初始化工作做好,避免第一次访问的时候速度很慢
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <!--
            这里设置的是前端控制器可以处理的请求路径/表示拦截处理jsp以外的所有请求
            也就是说处理以.jsp结尾的请求其他请求都要经过前端控制器,这里注意不要写成/*,/*表示匹配所有路径
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

15.3.4 applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--—————开启注解扫描,即让注解生效—————-->
    <context:component-scan base-package="com.ymk"/>

    <!--—————开启AOP自动代理配置—————-->
    <aop:aspectj-autoproxy/>

    <!--———————读取db.properties———————-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--—————1 druid数据源 实例化druid—————-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!--—————配置初始化大小、最小、最大——————-->
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <!--——————配置获取连接等待超时的时间———————-->
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <!--——配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒——— -->
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
        <!--————配置一个连接在池中最小生存的时间,单位是毫秒————-->
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
        <property name="testWhileIdle" value="true"/>
        <!--————这里建议配置为TRUE,防止取到的连接不可用—————-->
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="false"/>

    </bean>

    <!--—————2 创建Mybatis的工厂对象——————-->
    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--————————设置数据库连接池———————— -->
        <property name="dataSource" ref="dataSource"/>
        <!--—————加载mybatis主配置文件  classpath 表示classes目录所在路径—————-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--————————加载映射文件———————— -->
        <property name="mapperLocations" value="classpath:com/ymk/mapper/*.xml"/>
    </bean>
    <!--—————SqlSessionFactoryBean具体加载那个接口,adminDao,讲接口代理对象实例化出来
        spring就是依赖控制反转将对象创建出来的——————-->
    <!--———————3设置Mybatis的映射接口 ———————-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--——设置映射接口所在包,会将mapper下所有的接口实例化出一个对象,不用sqlSession.getMapper了——-->
        <property name="basePackage" value="com.ymk.mapper"></property>
    </bean>

</beans>

15.3.5 db.properties配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/empployee?useSSL=false
jdbc.username=root
jdbc.password=123456
# 配置初始化大小、最小、最大
jdbc.initialSize=5
jdbc.minIdle=3
jdbc.maxActive=20
# 配置获取连接等待超时的时间
jdbc.maxWait=0
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
jdbc.timeBetweenEvictionRunsMillis=600000
# 配置一个连接在池中最小生存的时间,单位是毫秒
jdbc.minEvictableIdleTimeMillis=300000

15.3.6 springmvc.xml配置文件

<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 告知springmvc  哪些包中 存在 被注解的类 -->
    <context:component-scan base-package="com.ymk" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 注册注解开发驱动 -->
    <mvc:annotation-driven>

    </mvc:annotation-driven>

    <!-- 视图解析器
	     作用:1.捕获后端控制器的返回值="index"
	          2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
	 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/"></property>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    
    <!--
  额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
  所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
  处理方式:将请求转会到tomcat中名为default的Servlet
  -->
    <mvc:default-servlet-handler/>

    <!--    <mvc:resources mapping="/image/**" location="/image/"/>-->


    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/inter/test1"/>-->
    <!--            <bean class="com.qf.java2110.interceptor.MyInter1"></bean>-->
    <!--        </mvc:interceptor>-->
    <!--&lt;!&ndash;        <mvc:interceptor>&ndash;&gt;-->
    <!--&lt;!&ndash;            <mvc:mapping path="/**"/> &lt;!&ndash; /** 任意多级任意路径 &ndash;&gt;&ndash;&gt;-->
    <!--&lt;!&ndash;            <mvc:exclude-mapping path="/inter/login"/>   &lt;!&ndash;不拦截此路径&ndash;&gt;&ndash;&gt;-->
    <!--&lt;!&ndash;            <mvc:exclude-mapping path="/json/test7"/>   &lt;!&ndash;不拦截此路径&ndash;&gt;&ndash;&gt;-->
    <!--&lt;!&ndash;            <bean class="com.qf.java2110.interceptor.LoginInterceptor"></bean>&ndash;&gt;-->
    <!--&lt;!&ndash;        </mvc:interceptor>&ndash;&gt;-->
    <!--    </mvc:interceptors>-->

    
    <!--文件上传配置-->
    <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"/>
    </bean>
</beans>

15.3.7 mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--————————使用外部properties文件————————-->
<!--    <properties resource="db.properties"/>-->
    <!--<settings>-->
    <!--<setting name="logImpl" value="LOG4J"/>-->
    <!--</settings>-->


    <!--——————给类起别名,路径为包直接扫描包,不用每次配置——————-->
    <typeAliases>
        <package name="com.ymk.model"/>
    </typeAliases>

    <!--——————————分页插件配置——————————-->
<!--    <plugins>-->
<!--        <plugin interceptor="com.github.pagehelper.PageHelper">-->
<!--            <property name="dialect" value="mysql"/>-->
<!--        </plugin>-->
<!--    </plugins>-->
</configuration>

15.3.8 log4j.properties配置文件

log4j.rootLogger=DEBUG, stdout, logfile
#log4j.logger.com.ymk.mapper=debug,stdout
log4j.category.org.springframework=ERROR
log4j.category.org.apache=INFO

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#\u5E74\u6708\u65E5\u65F6\u5206\u79D2,xxx
#log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
#\u5E74\u6708\u65E5\u65F6\u5206\u79D2
#log4j.appender.stdout.layout.ConversionPattern=[%p][%d{yy-MM-dd HH:mm:ss}][%c]%m%n
log4j.appender.stdout.layout.ConversionPattern=%p-[%c]%m%n

log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=${myweb.root}/WEB-INF/log/myweb.log
log4j.appender.logfile.MaxFileSize=512KB
log4j.appender.logfile.MaxBackupIndex=5
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

15.3.9 实例操作

创建实体创建类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin {
    private int id;
    private String user;
    private String password;
}

创建AdminMapper接口和AdminMapper.xml文件

public interface AdminMapper {
    Admin findAdminById(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ymk.mapper.AdminMapper">
    <select id="findAdminById" parameterType="int" resultType="admin">
        select *
        from admin
        where id = #{id};
    </select>
</mapper>

AdminService接口和AdminServiceImpl实现类

@Service("adminService")
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    @Override
    public Admin findAdminById(int id) {
        Admin admin = adminMapper.findAdminById(id);
        return admin;
    }
}

AdminController

@Controller
@RequestMapping("/admin")
public class AdminController {

    @Autowired
    private AdminService adminService;

    //通过id查询用户信息
    @RequestMapping("/{id}")
    public String fridUserById(@PathVariable("id")Integer id, Model model){
        Admin admin = adminService.findAdminById(id);
        model.addAttribute("admin",admin);
        System.out.println("AdminController:"+admin);
        return "index";
    }
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到