Spring MVC

START

  • author:空巷

  • WeChat Applet :Java空巷

  • QQ: 2399014502

  • Mail: ycw2399014502@163.com

  • WeiBo : KongXiang_

  • WeChat:

在这里插入图片描述

SpringMVC

推荐学习路径:http://c.biancheng.net/spring_mvc/

MVC设计模式

MVC 设计不仅限于 Java Web 应用,还包括许多应用,比如前端、PHP、.NET 等语言。之所以那么做的根本原因在于解耦各个模块。

MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责。

  • 模型:用于存储数据以及处理用户请求的业务逻辑。
  • 视图:向控制器提交数据,显示模型中的数据。
  • 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

基于 Servlet 的 MVC 模式的具体实现如下。

  • 模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)。
  • 视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
  • 控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

JSP中的MVC模式

Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如图 所示。

Spring MVC工作原理图

从图 可总结出 Spring MVC 的工作流程如下:

  1. 客户端请求提交到 DispatcherServlet。
  2. 由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller。
  3. DispatcherServlet 将请求提交到 Controller。
  4. Controller 调用业务逻辑处理后返回 ModelAndView。
  5. DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图。
  6. 视图负责将结果显示到客户端。

传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。 在 Service 下可以通过
Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL ,这样就能够满足异军突起的NoSQL 的使用了,它可以大大提高互联网系统的性能。

特点: 结构松散,几乎可以在 Spring MVC 中使用各类视图 松耦合,各个模块分离 与 Spring 无缝集成

代码

使用配置文件

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>springmvc-study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.5.Final</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source> <!-- 源代码使用的JDK版本 -->
                    <target>${java.version}</target> <!-- 需要生成的目标class文件的编译版本-->
                    <encoding>${project.build.sourceEncoding}</encoding><!-- 字符集编码 -->
        </configuration>
      </plugin>
    </plugins>
    <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>


</project>

在web工程下配置DispatcherServlet的servlet

<!--注册DispatcherServlet,这是springmvc的核心,就是个servlet-->
    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

在springMvc中,视图都放在WEB-INF中以保证视图安全。客户端必须通过访问servlet才能跳转到WEB-INF

<?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
    http://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">

    <!-- 处理映射器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- 处理器适配器 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/page/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

编写一个Controller,继承Controller接口

public class FirstController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
        //ModelAndView 封装了模型和视图
        ModelAndView mv = new ModelAndView();
        //模型里封装数据
        mv.addObject("HelloMvc","Hello springMVC!");
        //封装跳转的视图
        mv.setViewName("HelloMvc");
        //不是有个视图解析器吗
        //这玩意就是为了省事的,自动给你加个前缀后缀
        //就成了 /jsp/hellomvc.jsp 不就是拼串吗
        return mv;
    }
}

spingMvc默认去找WEB-INF下的spring-mvc-servlet.xml ,要创建一个spring-mvc-servlet.xml 文件

<bean id="/hellomvc" class="com.xinzhi.controller.FirstController"/>

使用注解

注解就要扫包

<!-- 自动扫包 -->
<context:component-scan base-package="com.xinzhi"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!-- 让springmvc自带的注解生效 -->
<mvc:annotation-driven />
@Controller//说明是Controller层
@RequestMapping("/user/") //路径
  public class UserController {
  //一个主路径可以有多个子路径,多个功能可以由一个servlet来处理
  @RequestMapping("login")
  public String login(Model model) {
    model.addAttribute("ogin","denlu!");
    return "Login";
  } 
  @RequestMapping("register")
  public String register(Model model) {
    model.addAttribute("egister","zhuce!");
    return "Register";
	}
}

聊聊整个过程 执行原理

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,在将结果返回给请求者。

img

执行原理

img

简要分析执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

    我们假设请求的url为 : http://localhost:8080/SpringMVC/hello

    如上url拆分成三部分:

    http://localhost:8080服务器域名

    SpringMVC部署在服务器上的web站点

    hello表示控制器

    通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

  6. Handler让具体的Controller执行。

  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

深入学习

视图模型分离

之前返回的是模型加视图,耦合性太高,有时候不需要跳转视图也会强行跳转使用Model参数

Spring 视图解析器是 Spring MVC 中的重要组成部分,用户可以在配置文件中定义 Spring MVC 的一个视图解析器(ViewResolver),示例代码如下:

 <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/page/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

看代码

@RequestMapping(value = "login")
public String login(Model model) {
    model.addAttribute("ogin","登录!");
    return "Login";
}
//视图解析器将会自动添加前缀和后缀。

@RequestMapping

这个注解很强大,可以放在方法上也可以放在类上,放在类上所有方法都默认加上该注解有六个参数

  • value:访问路径
  • method :指定请求类型,如post、get等
  • consumes :指定处理请求后提交的数据类型(Content-Type),如json,text
  • produces :指定返回值类型
  • params :指定request中必须有哪些参数,有才能让此方法执行
  • headers :指定request中必须包含某些指定的header值 ,有才能让此方法执行

例如:

@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

好处:

  • 一个一班处理一类事物,可以统一家前缀,好区分
  • 简化书写复杂度

内置统一字符集处理

在 web.xml 中配置一个字符集过滤器即可

<!--在web.xml里配置-->
<filter>
  <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

传参

建个首页

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    没注册的用户,请<a href="${pageContext.request.contextPath }/index/register"> 注册</a>!
  <br/>
    已注册的用户,去<a href="${pageContext.request.contextPath }/index/login"> 登录</a>!
</body>
</html>

建个实体类

package pojo;

public class UserForm {
    private String uname; // 与请求参数名称相同
    private String upass;
    private String reupass;

    // 省略getter和setter方法
}

不需要一个个的getParameter(),要什么直接拿

通过 @RequestParam 接收请求参数

通过 @RequestParam 接收请求参数适用于 get 和 post 提交请求方式,可以将“通过实体 Bean 接收请求参数”部分控制器类 UserController 中 register 方法

@RequestMapping("/register")
/**
* 通过@RequestParam接收请求参数
*/
public String register(@RequestParam String uname,
    @RequestParam String upass, Model model) {
    if ("zhangsan".equals(uname) && "123456".equals(upass)) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        // 在register.jsp页面上可以使用EL表达式取出model的uname值
        model.addAttribute("uname", uname);
        return "register"; // 返回 register.jsp
    }
}
@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

通过 @PathVariable 接收 URL 中的请求参数

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/user")
    // 必须节method属性
    /**
     * 通过@PathVariable获取URL的参数
     */
    public String register(@PathVariable String uname,@PathVariable String upass,Model model) {
        if ("zhangsan".equals(uname)
                && "123456".equals(upass)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            // 在register.jsp页面上可以使用EL表达式取出model的uname值
            model.addAttribute("uname", uname);
            return "register"; // 返回 register.jsp
        }
    }
}

但参数多了这么传是不是很啰嗦。更牛逼了 直接传个对象

@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

这里介绍了几种常用的传参方式,还有很多,可以自行去学习。

返回json数据

可以使用fastjson

导入依赖

<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.68</version>
</dependency>
@RequestMapping(value = "getUsers",produces = {"text/json;charset=utf-8"})
@ResponseBody
public String getUsers(){
  List<User> users =  new ArrayList<User>(){{
    add(new User("wangwu","2222",23));
    add(new User("空巷","333",23));
 }};
  return JSONArray.toJSONString(users);
}

@ResponseBody能将处理的结果放在响应体中,直接返回,不走视图解析器

每次都需要自己处理数据麻烦 那么springMVC 封装了一个更好用的 只需要在配置文件中注入一个消息转换器

<mvc:annotation-driven >
  <mvc:message-converters>
    <bean id="fastjson"
          class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>text/html;charset=UTF-8</value>
          <value>application/json;charset=UTF-8</value>
        </list>
      </property>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>
@RequestMapping("set")
//直接将返回值以数据的形式返回到响应体
@ResponseBody
public User set(@Validated User user, BindingResult br) {
  List<ObjectError> allErrors = br.getAllErrors();
  for (ObjectError allError : allErrors) {
    System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
  }
  System.out.println(user.getId());
  return user;
}

数据转化

Spring MVC 提供了几个内置的格式化转换器,具体如下。

  • NumberFormatter:实现 Number 与 String 之间的解析与格式化。
  • CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
  • PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
  • DateFormatter:实现 Date 与 String 之间的解析与格式化。
@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date birthday;
@NumberFormat(pattern = "#,###,###.#")
private Double salary;

数据校验

@NotNull 对象不为空
@AssertTrue Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
@Size(min =, max =) 长度是否在给定的范围
@Min(value=) 验证 Number 对象是否大等于指定的值
@Max(value=) 验证 Number 对象是否小等于指定的值
@Pattern 验证 String 对象是否符合正则表达式的规则
@Past @Future验证 Date 和 Calendar 时间对象是否在当前时间之前/之后
@Email 是否是正确的邮箱地址

需要引入jar

<!-- 引入hibernate的校验器实现 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.1.Final</version>
</dependency>

在 springmvc.xml 中注册 hibernate 的校验器,并在原来的 mvc 注解驱动中设置 validator 属性:

<!--开启mvc注解驱动  -->
<mvc:annotation-driven validator="validator" />

<!-- 注册hibernate的校验器 -->
<bean id = "validator" class = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
	<property name="providerClass" value ="org.hibernate.validator.HibernateValidator"></property>  		
</bean>

java:

@Max(value = 20L,message = "id不能大于20")
private int id;
@NotNull(message = "名字不能为空")
private String username;
@Null
private String password;

@RequestMapping("set")
@ResponseBody
//用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
public User set(@Validated User user, BindingResult br) {
  //拿到错误,不会挡住请求
  List<ObjectError> allErrors = br.getAllErrors();
  for (ObjectError allError : allErrors) {
    System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
  }
  System.out.println(user.getId());
  return user;
}

注意点:

  • 在需要校验的对象前加 @Validated注解。

  • 在该对象后加一个参数:BindingResut 来接收校验信息。

  • 调用 BindingResult 对象的 hasErrors 方法判断前台传过来的参数是否符合校验规则,有一个不符合规则,hasErrors 方法就会返回 true。

  • 然后调用 getAllErrors() 方法获取 ObjectError 的 list 列表。

  • 遍历集合,调用每个对象的 getDefaultMessage() 方法,获取相应的错误提示信息(在实体类上配置的)

  • 获取到校验信息后再返回给前台或做一些其他的处理即可。

重定向和转发

请求转发:

    @RequestMapping("/annotation")
    public String testAnnotation(Model model) {
        model.addAttribute("annotation", "hello Springmvc");
        return "annotation";
    }

重定向:(只需要在return 语句中加入 redirect:+ 地址 即可访问)

    @RequestMapping(value = "/jump")
    public String jumpUser(HttpServletRequest request,HttpServletResponse response){
        //return "redirect:https://www.baidu.com/";
        return "redirect:add";
    }

restful

请求url请求方式操作
/admin/get/1get根据id查询用户
/admin/addPost添加一个用户
/admin/update/1Put根据id修改用户
/admin/delete/1Delete删除一个用户

Restful web service是一种常见的rest的应用,是遵守了rest风格的web服务;rest式的web服务是一种ROA(The Resource-Oriented Architecture)(面向资源的架构)

常用状态码 工具类

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@AllArgsConstructor
@Data
@NoArgsConstructor
public class R implements Serializable {
    private int code;
    private String msg;
    private Map<String,String> data;

    //返回成功后的信息
    public static R success(){
        return new R(200,"成功",null);
    }

    //返回失败后的信息
    public static R fail(){
        return new R(500,"fail",null);
    }

    //其他类型的返回
    public static R build(int code,String msg){
        return new R(500,msg,null);
    }

    public R put(String key,String msg){
        if (msg == null){
            this.getData().put(key,msg);
        }
        return this;
    }
}

controller类

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@Controller
@RequestMapping("/admin")
public class RestfulController {
    @GetMapping("/get/{id}")
    @ResponseBody
    public User getUser(@PathVariable int id){
        System.out.println(id);
        return new User(13,"123","123456",new Date(),12123.12);
    }

    @PostMapping("/add")
    @ResponseBody
    public R addUser(User user){
        System.out.println(user);
        if (user == null){
            return R.fail();
        }else{
            return R.success();
        }
    }

    @PutMapping("/update/{id}")
    @ResponseBody
    public R updateUser(@PathVariable int id){
        System.out.println(id);
        return R.success();
    }

    @DeleteMapping("/delete/{id}")
    @ResponseBody
    public R deleteUser(@PathVariable int id){
        System.out.println(id);
        return R.success();
    }
}

建立一个http文件或者在postman测试

GET http://localhost:8080/admin/get/1
Accept: application/json

###
POST http://localhost:8080/admin/add
Content-Type: text/html

###
PUT http://localhost:8080/admin/update/2
Content-Type: application/json
{
   "id":2,
   "username":"123",
   "password":2,
   "birthday":"2000-5-25",
   "salary":"1,111.14"
}

###
DELETE http://localhost:8080/admin/delete/1
Content-Type: text/html

update 测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81uCSZvO-1591533532699)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604172838216.png)]

delete 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WFpgcgf-1591533532700)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173022938.png)]

get 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HctHedNB-1591533532701)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173108784.png)]

add 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ovlp1zQ5-1591533532702)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173200239.png)]

拦截器

  1. SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的任意请求。
  2. 熟练掌握 SpringMVC 拦截器对于我们开发非常有帮助,在没使用权限框架( shiro,spring security )之前,一般使用拦截器进行认证和授权操作。
  3. SpringMVC拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。

拦截器规则

自定义的拦截器需要实现 Spring 的 HandlerInterceptor 接口,其中有三个方法。

  1. preHandle:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。
  2. postHandle:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方
    法都返回true时才会调用。
  3. afterCompletion:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true就会
    调用

登录拦截器

/**
 * @author 空巷
 * @Date 2020/6/4
 */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        if (user == null){
            response.sendRedirect("/admin/toLogin");
        }else {
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

还需要在 SpringMVC.xml 配置文件名中,配置编写的拦截器

<!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--
                mapping:拦截的路径
                /**:是指所有文件夹及其子孙文件夹
                /*:是指所有问你减价,单不包含子孙文件夹
                /:Web项目的根目录
            -->
            <mvc:mapping path="/**"/>
            <!--
                exclude-mapping:不拦截路径,不拦截登录路径
                /toLogin:跳转到登陆页面
                /login:登录操作
            -->
            <mvc:exclude-mapping path="/admin/toLogin"/>
            <mvc:exclude-mapping path="/admin/login"/>
            <!--class属性就是我们自定义的拦截器-->
            <bean id="loginInterceptor" class="com.xinzhi.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

controller

    @GetMapping("/toLogin")
    public String toLogin(User user){
        return "login";
    }

    @PostMapping("/login")
    @ResponseBody
    public R login(User user, HttpServletRequest request){
        request.getSession().setAttribute("user",user);
        return R.success();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6xjbSrg-1591533532703)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604184134261.png)]

文件上传

Spring MVC 框架的文件上传是基于 commons-fileupload 组件的文件上传,只不过 Spring MVC 框架在原有文件上传组件上做了进一步封装,简化了文件上传的代码实现,取消了不同上传组件上的编程差异。

commons-fileupload组件

由于 Spring MVC 框架的文件上传是基于 commons-fileupload 组件的文件上传,因此需要将 commons-fileupload 组件相关的 JAR(commons-fileupload-1.3.1.jar 和 commons-io-2.4.jar)复制到 Spring MVC 应用的 WEB-INF/lib 目录下。下面讲解如何下载相关 JAR 包。

Commons 是 Apache 开放源代码组织中的一个 Java 子项目,该项目包括文件上传、命令行处理、数据库连接池、XML 配置文件处理等模块。fileupload 就是其中用来处理基于表单的文件上传的子项目,commons-fileupload 组件性能优良,并支持任意大小文件的上传。文件上传

<form action="/admin/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file">
    <input type="submit">
</form>

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

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的CommonsFileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

首先需要导入文件上传的 jar 包

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>

在 SpringMVC 中配置bean

    <!--文件上传配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--请求的编码格式,必须和jsp的pageEncoding的属性一致,以便正确读取表单的内筒,默认为ISO-8850-1-->
        <property name="defaultEncoding" value="utf-8"/>
        <!--上传文件大小上线,单位为字节 10485760 = 10M-->
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>

CommonsMultipartFile 的 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中

controller

@PostMapping("/upload")
@ResponseBody
public R upload(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request){
    //getOriginalFilename 可以获得文件的名字
    String filename = file.getOriginalFilename();
    //文件路径
    String path = "E:\\upload";
    File relPath = new File(path);
    //判断路径是否存在,不存在则创建一个
    if (!relPath.exists()){
        relPath.mkdir();
    }
    try {
        file.transferTo(new File(path +"\\"+ filename));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return R.success();
}

文件下载

实现文件下载有以下两种方法:

  • 通过超链接实现下载。
  • 利用程序编码实现下载。

使用 ResponseEntity

    @GetMapping("/download")
    public ResponseEntity<byte[]> download() {
        String fileName = "6.png";
        try {
            //FileUtils 中有个方法 readFileToByteArray 可以读取文件
            byte[] bytes = FileUtils.readFileToByteArray(new File("E:\\upload" + "\\" + fileName));

            HttpHeaders handler = new HttpHeaders();
            //下载文件时需要设置响应头
            handler.set("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(fileName, "UTF-8"));
            handler.set("charsetEncoding", "utf-8");
            handler.set("content-type", "multipart/form-data");
            ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, handler, HttpStatus.OK);
            return responseEntity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsYxOaI3-1591533532704)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604194010851.png)]

异常捕获

当页面出现异常时,不能显示在页面,之前是通过在web.xml配置错误页面,但往往这种处理办法是不好的。因为不知道他出现了什么错误。因此,Spring MVC提供了一个HandlerExceptionResolver接口,可用于统一异常处理。

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@Component
public class MyException implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        //打印错误到控制台
        e.printStackTrace();
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        return mv;
    }
}

国际化

国际化是设计软件应用的过程中应用被使用与不同语言和地区

spring-mvc.xml配置文件

<!--配置拦截器-->
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <!--该拦截器通过名为”lang”的参数来拦截HTTP请求,使其重新设置页面的区域化信息 -->
    <bean id="localeChangeInterceptor"
    class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
    </bean>
</mvc:interceptor>
        
        
        
<!-- 存储区域设置信息 SessionLocaleResolver类通过一个预定义会话名将区域化信息存储在会话中 从session判断用户语言defaultLocale
    :默认语言 -->
    <bean id="localeResolver"
          class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="defaultLocale" value="zh_CN" />
    </bean>

    <!-- 国际化资源文件 messageSource配置的是国际化资源文件的路径, classpath:messages指的是classpath路径下的
        messages_zh_CN.properties文件和messages_en_US.properties文件 设置“useCodeAsDefaultMessage”,默认为false,这样当Spring在ResourceBundle中找不到messageKey的话,就抛出NoSuchMessageException,
        把它设置为True,则找不到不会抛出异常,而是使用messageKey作为返回值。 -->
    <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="defaultEncoding" value="UTF-8" />
        <property name="useCodeAsDefaultMessage" value="true" />
        <property name="basenames">
            <list>
                <value>classpath:message</value>
            </list>
        </property>
    </bean>

国际化语言属性文件:message_zh_CN.properties

TITLE = 开始冒险之旅
USERNAME = 账号:
PASSWORD = 密码:
LOGIN = 登录

message_en_US.properties

TITLE = BEGIN TO START
USERNAME = UserName
PASSWORD = PassWord
LOGIN = Login

chinese.jsp

<%--
  Created by IntelliJ IDEA.
  User: 空巷
  Date: 2020/6/4
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="/admin/getLogin?lang=zh_CN">中文</a>
<br />
<a href="/admin/getLogin?lang=en_US">英文</a>
</body>
</html>

controller

  @GetMapping("/getLogin")
    public String getLogin(){
        return "result";
    }

result.jsp

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%--
  Created by IntelliJ IDEA.
  User: 空巷
  Date: 2020/6/4
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<div class="login">
    <h1><spring:message code="TITLE" /> </h1>
    <form action="test.do" method="post">
        <input type="text" name="name" placeholder=<spring:message code="USERNAME" /> required="required" value="" />
        <input type="password" name="password" placeholder=<spring:message code="PASSWORD" /> required="required" value="" />
        <button type="submit" class="btn btn-primary btn-block btn-large"><spring:message code="LOGIN" /> </button>
    </form>
</div>
</body>
</html>

ssm整合

pom依赖:

    <dependencies>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- spring 相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.6.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!-- mybatis 相关 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- 数据库连接驱动 相关 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 提供了对JDBC操作的完整封装 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- 织入 相关 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <!-- spring,mybatis整合包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
    </dependencies>
    <!-- 资源文件过滤 -->
    <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>

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>

    <typeAliases>
        <!--为 com.xinzhi.entity.User 这个类设置别名user-->
        <!--<typeAlias type="com.xinzhi.entity.User"  alias="user"/>-->
        <!--为com.xinzhi.entity 包下的所有文件设置别名 -->
        <package name="com.xinzhi.entity"/>
    </typeAliases>

</configuration>

springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://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">

    <!--&lt;!&ndash; 让Spring MVC不处理静态资源 &ndash;&gt;-->
    <mvc:default-servlet-handler />

    <!--&lt;!&ndash;当把静态资源放入WEN-INF下时,需要添加访问静态资源的配置&ndash;&gt;
    <mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>-->


    <!--  让springmvc自带的注解生效  -->
    <mvc:annotation-driven >
        <mvc:message-converters>
            <bean id="fastjson" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>


    <!--文件上传配置-->
    <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>

    <!-- 处理映射器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- 处理器适配器 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器:DispatcherServlet给他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

application.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://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
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 加载外部的数据库信息 classpath:不叫会报错具体原因下边解释-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!-- 加入springmvc的配置 -->
    <import resource="classpath:springmvc-servlet.xml"/>
    <!--扫包-->
    <context:component-scan base-package="com.xinzhi"/>

    <!-- Mapper 扫描器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 扫描 cn.wmyskxz.mapper 包下的组件 -->
        <property name="basePackage" value="com.xinzhi.dao"/>
    </bean>

    <!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <property name = "filters" value = "${filters}" />
        <!-- 最大并发连接数 -->
        <property name = "maxActive" value = "${maxActive}" />
        <!-- 初始化连接数量 -->
        <property name = "initialSize" value = "${initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name = "maxWait" value = "${maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name = "minIdle" value = "${minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name = "timeBetweenEvictionRunsMillis" value ="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name = "minEvictableIdleTimeMillis" value ="${minEvictableIdleTimeMillis}" />
        <!--        <property name = "validationQuery" value = "${validationQuery}" />     -->
        <property name = "testWhileIdle" value = "${testWhileIdle}" />
        <property name = "testOnBorrow" value = "${testOnBorrow}" />
        <property name = "testOnReturn" value = "${testOnReturn}" />
        <property name = "maxOpenPreparedStatements" value ="${maxOpenPreparedStatements}" />
        <!-- 打开 removeAbandoned 功能 -->
        <property name = "removeAbandoned" value = "${removeAbandoned}" />
        <!-- 1800 秒,也就是 30 分钟 -->
        <property name = "removeAbandonedTimeout" value ="${removeAbandonedTimeout}" />
        <!-- 关闭 abanded 连接时输出错误日志 -->
        <property name = "logAbandoned" value = "${logAbandoned}" />
    </bean>

    <!--配置SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--关联Mybatis-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mappers/*.xml"/>
    </bean>

    <!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--利用构造器注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>


    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="search*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置aop织入事务-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.xinzhi.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
    <!--    &lt;!&ndash;开启事务注解,并配置一个事务管理器 &ndash;&gt;-->
<!--    <tx:annotation-driven transaction-manager="transactionManager" />-->

</beans>

logback.xml(日志配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 定义参数常量 -->
    <!-- 日志级别 TRACE<DEBUG<INFO<WARN<ERROR -->
    <!-- logger.trace("msg") logger.debug... -->
    <property name="log.level" value="debug" />
    <property name="log.maxHistory" value="0.5" />
    <property name="log.filePath" value="D:/log" />
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
    <!-- 控制台输出设置 -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- DEBUG级别文件记录 -->
    <appender name="debugAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径 -->
        <file>${log.filePath}/debug.log</file>
        <!-- 滚动日志文件类型,就是每天都会有一个日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 文件名称 -->
            <fileNamePattern>${log.filePath}/debug/debug.%d{yyyy-MM-dd}.log.gz
            </fileNamePattern>
            <!-- 文件最大保存历史数量 -->
            <maxHistory>${log.maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- INFO -->
    <appender name="infoAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径 -->
        <file>${log.filePath}/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 文件名称 -->
            <fileNamePattern>${log.filePath}/info/info.%d{yyyy-MM-dd}.log.gz
            </fileNamePattern>
            <!-- 文件最大保存历史数量 -->
            <maxHistory>${log.maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- com.xinzhi开头的日志对应形式 -->
    <logger name="com.xinzhi" level="${log.level}" additivity="true">
        <appender-ref ref="debugAppender"/>
        <appender-ref ref="infoAppender"/>
    </logger>

    <!-- <root> 是必选节点,用来指定最基础的日志输出级别,只有一个level属性 -->
    <root level="info">
        <appender-ref ref="consoleAppender"/>
    </root>

    <!-- 捕捉sql开头的日志 -->
    <appender name="MyBatis" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.filePath}/sql_log/mybatis-sql.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.filePath}/sql_log/mybatis-sql.log.%d{yyyy-MM-dd}</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%thread|%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger{36}|%m%n</pattern>
        </encoder>
    </appender>

    <logger name="sql" level="DEBUG">
        <appender-ref ref="MyBatis"/>
    </logger>

</configuration>

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/backlog?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root

filters=wall,stat
maxActive=20
initialSize=3
maxWait=5000
minIdle=3
maxIdle=15
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 'x'
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
maxOpenPreparedStatements=20
removeAbandoned=true
removeAbandonedTimeout=1800
logAbandoned=true

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>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 连接池 启用 Web 监控统计功能    start-->
    <filter>
        <filter-name>DruidWebStatFilter</filter-name>
        <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
        <init-param>
            <param-name>exclusions</param-name>
            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>DruidWebStatFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>DruidStatView </servlet-name>
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
        <init-param>
            <!-- 用户名 -->
            <param-name>loginUsername</param-name>
            <param-value>xinzhi</param-value>
        </init-param>
        <init-param>
            <!-- 密码 -->
            <param-name>loginPassword</param-name>
            <param-value>123</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DruidStatView</servlet-name>
        <url-pattern>/druid/*</url-pattern>
    </servlet-mapping>

</web-app>

xxxMapper.xml

<?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.xinzhi.dao.UserMapper">

</mapper>

静态资源处理问题

静态资源的访问问题

访问 jsp 页面时 首先通过访问controller,通过请求转发到jsp页面 需要跳转首页时 建立一个indexConrtoller 使用GetMapping直接转发到首页

当一些页面的css,js等静态资源 放在WEB-INF 下,通过相对路径的方式是访问不到的

解决安案一:(不被保护的,外界可以查看)

将静态资源放入 webapp 下 通过 相对路径 或绝对路径的方式访问

解決方案二:(受保护的)

当把静态资源放入 WEB-INF 下的时候:

需要在spring-mvc配置文件中加入:
<!--当把静态资源放入WEN-INF下时,需要添加访问静态资源的配置-->
<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>

**eg: ** 当你使用了拦截器时,你会发现css等静态资源加不进来,打开 F12 进行调试,看到 css等静态资源并没有报红,你会发现,莫名其妙的重定向了。

解决方案一:(针对静态资源放入 webapp 下的处理办法)

在拦截器中加入

<mvc:exclude-mapping path="/**/*.css" />
<mvc:exclude-mapping path="/**/*.js" />
<mvc:exclude-mapping path="/**/*.jsp" />....

解决方案二:(针对静态资源存入WEB-INF 下)

在拦截器中加入

<mvc:exclude-mapping path="/static/**" />
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值