目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

SpringBoot开发规范

目录结构、静态资源访问

 1# 目录结构
 2* src/main/java:存放代码
 3* src/main/resources
 4	* static: 存放静态文件,比如 css、js、image, (访问方式 [http://localhost:8080/js/main.js](http://localhost:8080/js/main.js))
 5	* templates:存放静态页面jsp,html,tpl
 6	* config:存放配置文件,application.properties
 7
 8
 9# 静态资源访问(同一个文件的加载顺序)
10默认配置 spring.resources.static-locations = classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
11* META/resources >
12* resources >
13* static >
14* public

启动、部署

 1# Jar方式打包启动(pom文件新增maven插件)
 2<build>
 3            <plugins>
 4                <plugin>
 5                    <groupId>org.springframework.boot</groupId>
 6                    <artifactId>spring-boot-maven-plugin</artifactId>
 7                </plugin>
 8            </plugins>
 9</build>
10
11如果没Maven插件有加,则执行jar包 ,报错如下:
12	java -jar spring-boot-demo-0.0.1-SNAPSHOT.jar
13	no main manifest attribute, in spring-boot-demo-0.0.1-SNAPSHOT.jar
14
15
16# 打包
17构建:mvn install  (target目录下有对应的jar包就是打包后的项目)
18构建跳过测试类 mvn install -Dmaven.test.skip=true 
19
20# 启动
21进到对应的target目录启动 java -jar xxxxx.jar  即可
22想后台运行,就用守护进程 nohup java -jar xxx.jar &

打包后的 Jar 里面的目录结构

 1example.jar
 2 |
 3 +-META-INF    //指定main函数入口
 4 |  +-MANIFEST.MF
 5 |  +-maven
 6 |     +-pom.properties
 7 |     +-pom.xml
 8 +-org  //启动类目录
 9 |  +-springframework
10 |     +-boot
11 |        +-loader
12 |           +-<spring boot loader classes>
13 +-BOOT-INF 
14    +-classes	//项目的字节码文件目录、资源文件目录(resources)
15    |  +-mycompany
16    |     +-project
17    |        +-YourClasses.class
18    +-lib	//依赖的库文件
19       +-dependency1.jar
20       +-dependency2.jar

常用 HTTP 请求注解

1@GetMapping = @RequestMapping(method = RequestMethod.GET)
2@PostMapping = @RequestMapping(method = RequestMethod.POST)
3@PutMapping = @RequestMapping(method = RequestMethod.PUT)
4@DeleteMapping = @RequestMapping(method = RequestMethod.DELETE)
5
6@RequestBody  接收  raw (JSON)  复杂对象
7@PostMapping  接收 form(param)

定制 JSON 字段

 1# JavaBean序列化为Json
 2* 性能:Jackson > FastJson > Gson > Json-lib 同个结构
 3* Jackson、FastJson、Gson类库各有优点,各有自己的专长
 4* 空间换时间,时间换空间
 5
 6# jackson处理相关自动(一下注解作用于domain包下的bean的属性上)
 7* 指定字段不返回:@JsonIgnore
 8* 指定日期格式:@JsonFormat(pattern="yyyy-MM-dd hh🇲🇲ss",locale="zh",timezone="GMT+8")
 9* 空字段不返回:@JsonInclude(Include.NON_NULL)
10* 指定别名:@JsonProperty  例如: @JsonProperty("cover_img")  coverImg  (后端驼峰,返回给前端变为下划线)
11
12# 自定义JSON格式
13* 过滤用户敏感信息
14* 视频创建时间返回自定义格式
15
16# 序列化和反序列化操作
17        //序列化操作
18        ObjectMapper objectMapper = new ObjectMapper();
19        String jsonStr = objectMapper.writeValueAsString(list);
20        System.out.println(jsonStr);
21	  //反序列化操作
22        List<Video> temp = objectMapper.readValue(jsonStr,List.class);

热部署

 应用正在运行的时候升级功能, 不需要重新启动应用,对于 Java 应用程序来说, 热部署就是在运行时更新 Java 类文件。

1# 优点
2	不需要重新手工启动应用,提高本地开发效率
3
4# 常见实现热部署的方式
5Jrebel
6Spring Loaded
7spring-boot-devtools

添加依赖

 1 <dependency>  
 2         <groupId>org.springframework.boot</groupId>  
 3         <artifactId>spring-boot-devtools</artifactId>  
 4         <optional>true</optional>  
 5  </dependency>
 6  
 7  
 8  <build>
 9        <plugins>
10            <plugin>
11                <groupId>org.springframework.boot</groupId>
12                <artifactId>spring-boot-maven-plugin</artifactId>
13                <configuration>
14                    <fork>true</fork><!--必须添加这个配置-->
15                </configuration>
16            </plugin>
17        </plugins>
18    </build>

IDEA 开启自动编译

1	Preferences --> Build --> Compiler --> Build project automatically

使用快捷键打开,选择 Registry

1注意默认快捷键
2	window快捷键 Shift+Ctrl+Alt+/
3	mac快捷键 Shift+Command+Alt+/

选择 compiler.automake.allow.when.app.running
重启 idea 就行!!!

配置文件

Reference

1# Springboot里面常用xx.yml
2	YAML(Yet Another Markup Language)
3	写 YAML 要比写 XML 快得多(无需关注标签或引号) 使用空格 Space 缩进表示分层,不同层次之间的缩进可以使用不同的空格数目
4	注意:key后面的冒号,后面一定要跟一个空格,树状结构
5
6# Springboot里面常用 xx.properties(推荐)
7	Key=Value格式
8	语法简单,不容易出错

注解配置文件映射属性、实体类

 1# 方式一
 21. Controller上面配置 @PropertySource({"classpath:resource.properties"})
 32. 增加属性 @Value("${test.name}") private String name;
 4
 5@RestController
 6@RequestMapping("api/v1/test")
 7@PropertySource({"classpath:pay.properties"})
 8public class TestController {
 9    @Value("${wxpay.appid}")
10    private String payAppid;
11
12    @Value("${wxpay.secret}")
13    private String paySecret;
14
15    @Autowired
16    private WXConfig wxConfig;
17
18
19    @GetMapping("get_config")
20    public JsonData testConfig(){
21
22
23       Map<String,String> map = new HashMap<>();
24//        map.put("appid",payAppid);
25//        map.put("secret",paySecret);
26//
27//        return JsonData.buildSuccess(map);
28
29
30        map.put("appid",wxConfig.getPayAppid());
31        map.put("secret",wxConfig.getPaySecret());
32        map.put("mechid",wxConfig.getPayMechId());
33
34        return JsonData.buildSuccess(map);
35    }
36}
37
38# 方式二:实体类配置文件
391. 添加 @Configuration 注解;
402. 使用 @PropertySource 注解指定配置文件位置;
413. 使用 @Value注解,设置相关属性;
424. 必须 通过注入IOC对象Resource 进来 , 才能在类中使用获取的配置文件值。
43
44@Configuration
45@PropertySource(value="classpath:pay.properties")
46public class WXConfig implements Serializable {
47
48    @Value("${wxpay.appid}")
49    private String payAppid;
50
51    @Value("${wxpay.secret}")
52    private String paySecret;
53
54    @Value("${wxpay.mechid}")
55    private String payMechId;
56
57    public String getPayAppid() {
58        return payAppid;
59    }
60
61    public void setPayAppid(String payAppid) {
62        this.payAppid = payAppid;
63    }
64
65    public String getPaySecret() {
66        return paySecret;
67    }
68
69    public void setPaySecret(String paySecret) {
70        this.paySecret = paySecret;
71    }
72
73    public String getPayMechId() {
74        return payMechId;
75    }
76
77    public void setPayMechId(String payMechId) {
78        this.payMechId = payMechId;
79    }
80}

单元测试

 1# 软件开发流程
 2	需求分析->设计->开发->测试->上线
 3
 4# 测试里面的种类
 5* 单元测试
 6	完成最小的软件设计单元的验证工作,目标是确保模块被正确的编码
 7* 黑盒测试
 8	不考虑内部结构,主要测试功能十分满足需求
 9* 白盒测试
10	针对代码级别,测试开发工程师一般具备白盒测试能力,针对程序内部的逻辑结构进行代码级别的测试
11* 回归测试
12	对原先提出的缺陷进行二次验证,开发人员修复后进行二次的验证
13* 集成测试
14	测试模块和模块之间的整合,且测试主要的业务功能
15* 系统测试
16	针对整个产品系统进行的测试,验证系统是否满足产品业务需求

引入相关依赖:单元测试

 1<!--springboot程序测试依赖,如果是自动创建项目默认添加-->
 2 <dependency>
 3	<groupId>org.springframework.boot</groupId>
 4	<artifactId>spring-boot-starter-test</artifactId>
 5	<scope>test</scope>
 6</dependency>
 7
 8<dependency>
 9	<groupId>junit</groupId>
10	<artifactId>junit</artifactId>
11	<version>4.12</version>
12	<scope>test</scope>
13</dependency>

配置相关注解

1@RunWith(SpringRunner.class)  //底层用junit  SpringJUnit4ClassRunner
2@SpringBootTest(classes={XXXApplication.class})//启动整个springboot工程
3public class SpringBootTests { }

常用单元测试的注解

1* @before  在@Test方法执行之前执行
2* @Test
3* @After	  在@Test方法执行之后执行

断言

 1判断程序结果是否符合预期 TestCase.assertXXX()
 2
 3import junit.framework.TestCase;
 4import net.xdclass.demoproject.controller.UserController;
 5import net.xdclass.demoproject.domain.User;
 6import net.xdclass.demoproject.utils.JsonData;
 7import org.junit.Assert;
 8import org.junit.Test;
 9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.boot.test.context.SpringBootTest;
12import org.springframework.test.context.junit4.SpringRunner;
13
14@RunWith(SpringRunner.class)  //底层用junit  SpringJUnit4ClassRunner
15@SpringBootTest(classes={DemoProjectApplication.class})//启动整个springboot工程
16public class UserTest {
17
18    @Autowired
19    private UserController userController;
20
21    @Test
22    public void loginTest(){
23
24        User user = new User();
25        user.setUsername("jack");
26        user.setPwd("1234");
27
28        JsonData jsonData  = userController.login(user);
29
30        System.out.println(jsonData.toString());
31
32        TestCase.assertEquals(0,jsonData.getCode());
33    }
34}

MockMVC 调用 Controller 层 API 接口完成单元测试

 1# 如何测试Controller对外提供的接口
 2* 增加类注解 @AutoConfigureMockMvc
 3* 注入一个MockMvc类(此类相当于一个HTTP客户端,可以用于发起HTTP请求)
 4
 5# 相关API 
 6  * perform执行一个RequestBuilder请求
 7  * andExpect:添加ResultMatcher->MockMvcResultMatchers验证规则
 8  * andReturn:最后返回相应的MvcResult->Response
 9
10    @Test
11    public void testVideoListApi()throws Exception{
12	//发起HTTP请求到相应的API接口,并且添加验证规则针对状态码,如果成功才返回结果
13       MvcResult mvcResult =  mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/pub/video/list"))
14                .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
15
16	//从JSON数据中解析对应的状态码
17       int status = mvcResult.getResponse().getStatus();
18
19	//输出状态码
20       System.out.println(status);
21
22        //会乱码
23        //String result = mvcResult.getResponse().getContentAsString();
24
25        // 使用下面这个,增加 编码 说明,就不会乱码打印
26        String result = mvcResult.getResponse().getContentAsString(Charset.forName("utf-8"));
27
28       System.out.println(result);
29    }

全局异常处理

全局异常配置

 1# 为什么要配置全局异常?
 2	不配全局服务端报错场景 1/0、空指针等
 3
 4# 配置好处
 5	统一的错误页面或者错误码
 6	对用户更友好
 7
 8# Springboot2.X怎么在项目中配置全局异常
 9* 类添加注解
10	@ControllerAdvice,如果需要返回json数据,则方法需要加@ResponseBody
11	@RestControllerAdvice, 默认返回json数据,方法不需要加@ResponseBody
12* 方法添加处理器
13	捕获全局异常,处理所有不可知的异常
14	@ExceptionHandler(value=Exception.class)
 1import net.xdclass.demoproject.utils.JsonData;
 2import org.springframework.web.bind.annotation.ExceptionHandler;
 3import org.springframework.web.bind.annotation.RestControllerAdvice;
 4
 5import javax.servlet.http.HttpServletRequest;
 6
 7/**
 8 * 标记这个是一个异常处理类
 9 */
10@RestControllerAdvice
11public class CustomExtHandler {
12	//捕获全局异常,处理所有不可知的异常:Exception是父类,能捕获所有子类异常
13    @ExceptionHandler(value = Exception.class)
14    JsonData handlerException(Exception e, HttpServletRequest request){
15	//使用JsonData工具类封装异常时的响应数据
16        return JsonData.buildError("服务端出问题了", -2);
17    }
18}

自定义异常和错误页面跳转(技术比较老,单体项目)

 1# 返回自定义异常界面,需要引入thymeleaf依赖(非必须,如果是简单的html界面则不用)
 2<dependency>
 3	   <groupId>org.springframework.boot</groupId>
 4	   <artifactId>spring-boot-starter-thymeleaf</artifactId>
 5</dependency>
 6
 7# 步骤如下
 8* 由于springboot家在静态资源文件的是有顺序的,可以添加templates目录用于存放自定义异常页面,修改application.properties,在value最后追加路径 classpath:/templates/
 9	spring.resources.static-locations = classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/
10
11* resource目录下新建templates,并新建error.html
12<!DOCTYPE html>
13<html xmlns:th="http://www/thymeleaf.org">
14<head>
15    <meta charset="UTF-8">
16    <title>Title</title>
17</head>
18<body>
19欢迎光临!!
20<p th:text="${msg}"> </p>
21</body>
22</html>
23
24* 自定义异常处理类
25import org.springframework.web.bind.annotation.ControllerAdvice;
26import org.springframework.web.bind.annotation.ExceptionHandler;
27import org.springframework.web.bind.annotation.RestControllerAdvice;
28import org.springframework.web.servlet.ModelAndView;
29import javax.servlet.http.HttpServletRequest;
30/**
31 * 标记这个是一个异常处理类
32 */
33//@RestControllerAdvice  //默认返回JSON格式
34@ControllerAdvice
35public class CustomExtHandler {
36
37    @ExceptionHandler(value = Exception.class)
38    Object handlerException(Exception e, HttpServletRequest request){
39
40        ModelAndView modelAndView = new ModelAndView();
41        modelAndView.setViewName("error.html");
42        //System.out.println(e.getMessage());
43	//将捕获异常的getMessage的值作为错误页面跳转的传递参数msg的值
44        modelAndView.addObject("msg",e.getMessage());
45        return modelAndView;
46    }
47}

过滤器

 1# 场景
 2	权限控制,用户登录状态控制,也可以交给拦截器处理等 
 3
 4# springboot2.x 里面的过滤器
 5	ApplicationContextHeaderFilter
 6	OrderdCharacterEncodingFilter
 7	OrderedFormContentFilter
 8	OrderedRequestContextFilter
 9
10# 过滤器的优先级
11	低位值意味着更高的优先级
12	Ordered.HIGHEST_PRECEDENCE 
13	Ordered.LOWEST_PRECEDENCE
14
15# 注册 Filter 配置两种方式
16	bean FilterRegistrantionBean
17	Servlet3.0 webFilter 注解的方式
18
19# Servlet3.0的注解进行配置步骤
20* 启动类里面增加 @ServletComponentScan,进行扫描
21* 新建一个Filter类,implements Filter,并实现对应的接口
22* @WebFilter 标记一个类为filter,被spring进行扫描
23* urlPatterns:拦截规则,支持正则
24* 控制chain.doFilter的方法的调用,来实现是否通过放行
25* 不放行,web应用resp.sendRedirect("/index.html") 或者 返回json字符串

过滤器:用户登录状态控制,权限控制。

 1import com.fasterxml.jackson.databind.ObjectMapper;
 2import net.xdclass.demoproject.domain.User;
 3import net.xdclass.demoproject.service.impl.UserServiceImpl;
 4import net.xdclass.demoproject.utils.JsonData;
 5import org.springframework.util.StringUtils;
 6
 7import javax.servlet.*;
 8import javax.servlet.annotation.WebFilter;
 9import javax.servlet.http.HttpServletRequest;
10import javax.servlet.http.HttpServletResponse;
11import java.io.IOException;
12import java.io.PrintWriter;
13
14@WebFilter(urlPatterns = "/api/v1/pri/*", filterName = "loginFilter")
15public class LoginFilter implements Filter {
16    //ObjectMapper 是jackson中帮忙序列化的类
17    private static final ObjectMapper objectMapper = new ObjectMapper();
18
19    /**
20     * 容器加载的时候
21     * @param filterConfig
22     * @throws ServletException
23     */
24    @Override
25    public void init(FilterConfig filterConfig) throws ServletException {
26
27        System.out.println("init LoginFilter======");
28    }
29
30    /**
31     * 核心业务逻辑
32     */
33    @Override
34    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
35
36        System.out.println("doFilter LoginFilter======");
37
38        HttpServletRequest req = (HttpServletRequest) servletRequest;
39
40        HttpServletResponse resp = (HttpServletResponse) servletResponse;
41
42        String token = req.getHeader("token");
43        if(StringUtils.isEmpty(token)){
44            token = req.getParameter("token");
45        }
46
47        if(!StringUtils.isEmpty(token)){
48
49            //判断token是否合法
50            User user = UserServiceImpl.sessionMap.get(token);
51            if(user!=null){
52		//token存在,并且合法,放行
53                filterChain.doFilter(servletRequest,servletResponse);
54            }else {//有token,但token失效
55
56               JsonData jsonData =  JsonData.buildError("登录失败,token无效",-2);
57                String jsonStr = objectMapper.writeValueAsString(jsonData);
58                renderJson(resp,jsonStr);
59            }
60
61        }else {//token为空
62            JsonData jsonData =  JsonData.buildError("未登录",-3);
63            String jsonStr = objectMapper.writeValueAsString(jsonData);
64            renderJson(resp,jsonStr);
65        }
66    }
67
68    /* 回写给前端JSON数据 */
69    private void renderJson(HttpServletResponse response,String json){
70        response.setCharacterEncoding("UTF-8");
71        response.setContentType("application/json");
72
73        try(PrintWriter writer = response.getWriter()){
74            writer.print(json);
75        }catch (Exception e){
76            e.printStackTrace();
77        }
78    }
79
80    /**
81     * 容器销毁的时候
82     */
83    @Override
84    public void destroy() {
85        System.out.println("destroy LoginFilter======");
86    }
87}

Servlet3.0 的注解原生 Servlet
 javaweb 的使用 doPost 和 doGet 方法使用,Servlet3.0 替代更轻量级

 1import javax.servlet.ServletException;
 2import javax.servlet.annotation.WebServlet;
 3import javax.servlet.http.HttpServlet;
 4import javax.servlet.http.HttpServletRequest;
 5import javax.servlet.http.HttpServletResponse;
 6import java.io.IOException;
 7import java.io.PrintWriter;
 8
 9/**
10 * 使用servlet3.0 开发原生的接口 (不需要在xml文件中配置路径了,使用注解直接代替)
11 */
12@WebServlet(name = "userServlet", urlPatterns = "/api/v1/test/customs" )
13class UserServlet extends HttpServlet {
14
15    @Override
16    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
17
18        PrintWriter writer = resp.getWriter();
19        writer.write("this is my custom servlet");
20        writer.flush();
21        writer.close();
22    }
23
24    @Override
25    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
26        this.doGet(req,resp);
27    }
28}

监听器

 监听器:应用启动监听器,会话监听器,请求监听器。

1# 作用
2* ServletContextListener 应用启动监听
3* HttpSessionLisener 会话监听
4* ServletRequestListener 请求监听
5
6# 常用的监听器 
7* ServletContextListener
8* HttpSessionListener
9* ServletRequestListener
1# 作用
2	ServletContextListener 应用启动监听:用于资源的初始化加载,读取配置文件、建立连接、垃圾清理、初始化
3	HttpSessionLisener 会话监听:分布式环境下很少用,几乎不用
4	ServletRequestListener 请求监听:用于日志打印
5

ServletContextListener

 1import javax.servlet.ServletContextEvent;
 2import javax.servlet.ServletContextListener;
 3import javax.servlet.annotation.WebListener;
 4
 5/**
 6 * 应用上下文监听器
 7 */
 8@WebListener
 9class ApplicationListener implements ServletContextListener {
10
11    @Override
12    public void contextInitialized(ServletContextEvent sce) {
13        System.out.println("contextInitialized====");
14    }
15
16    @Override
17    public void contextDestroyed(ServletContextEvent sce) {
18        System.out.println("contextDestroyed====");
19    }
20}

HttpSessionLisener

 1import javax.servlet.annotation.WebListener;
 2import javax.servlet.http.HttpSessionEvent;
 3import javax.servlet.http.HttpSessionListener;
 4
 5@WebListener
 6class CustomSessionListener implements HttpSessionListener {
 7
 8    @Override
 9    public void sessionCreated(HttpSessionEvent se) {
10        System.out.println("sessionCreated====");
11    }
12
13    @Override
14    public void sessionDestroyed(HttpSessionEvent se) {
15        System.out.println("sessionDestroyed====");
16    }
17}

ServletRequestListener

 1import javax.servlet.annotation.WebListener;
 2import javax.servlet.http.HttpSessionEvent;
 3import javax.servlet.http.HttpSessionListener;
 4
 5@WebListener
 6class CustomSessionListener implements HttpSessionListener {
 7
 8    @Override
 9    public void sessionCreated(HttpSessionEvent se) {
10        System.out.println("sessionCreated====");
11    }
12
13    @Override
14    public void sessionDestroyed(HttpSessionEvent se) {
15        System.out.println("sessionDestroyed====");
16    }
17}

拦截器

 拦截器和过滤器用途基本类似。
 适用场景:权限控制、用户登录状态控制等。

 1# 配置拦截器
 2* SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer
 3
 4* 自定义拦截器 HandlerInterceptor
 5  * preHandle:调用Controller某个方法之前
 6  * postHandleController之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
 7  * afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理
 8
 9* 按照注册顺序进行拦截:流入方向,先注册,先被拦截,流出方向,后注册,先执行。
10	LoginIntercepter preHandle =====
11	TwoIntercepter preHandle =====
12
13	TwoIntercepter postHandle =====
14	LoginIntercepter postHandle =====
15
16	TwoIntercepter afterCompletion =====
17	LoginIntercepter afterCompletion =====
18
19# 拦截器不生效常见问题
20	- 是否有加@Configuration
21	- 拦截路径是否有问题 **   * 
22	- 拦截器最后路径一定要 /**  如果是目录的话则是 /*/
23
24# Filter过滤器的区别
25* FilterInterceptor二者都是AOP编程思想的体现,功能基本都可以实现
26* 拦截器功能更强大些,Filter能做的事情它都能做
27* Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
28* filter依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
29* 在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
30
31# FilterInterceptor的执行顺序
32	过滤前->拦截前->action执行->拦截后->过滤后
33
34# 补充知识
35* 如何配置不拦截某些路径?
36	registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api/v1/pri/**")
37* 配置不拦截某些路径,比如静态资源
38	.excludePathPatterns("/**/*.html","/**/*.js"); 

拦截器配置类

 1import org.springframework.context.annotation.Bean;
 2import org.springframework.context.annotation.Configuration;
 3import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 4import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 5
 6/**
 7 * 拦截器配置类
 8 */
 9@Configuration
10class CustomWebMvcConfigurer implements WebMvcConfigurer {
11
12    @Override
13    public void addInterceptors(InterceptorRegistry registry) {
14	//注册拦截器
15        registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/v1/pri/**");
16        registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/v1/pri/**");
17        WebMvcConfigurer.super.addInterceptors(registry);
18    }
19
20
21//    补充知识点
22//    @Override
23//    public void addInterceptors(InterceptorRegistry registry) {
24//
25//        registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/v1/pri/**","/api/v1/pri/user/**")
26//        .excludePathPatterns("/**/*.html","/**/*.js"); //配置不拦截某些路径;
27//
28//        registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/v1/pri/**")
29//
30//
31//        WebMvcConfigurer.super.addInterceptors(registry);
32//
33//
34//    }
35
36    @Bean
37    public LoginIntercepter getLoginInterceptor(){
38        return new LoginIntercepter();
39    }
40}

拦截器类

 1import com.fasterxml.jackson.databind.ObjectMapper;
 2import net.xdclass.demoproject.domain.User;
 3import net.xdclass.demoproject.service.impl.UserServiceImpl;
 4import net.xdclass.demoproject.utils.JsonData;
 5import org.springframework.util.StringUtils;
 6import org.springframework.web.servlet.HandlerInterceptor;
 7import org.springframework.web.servlet.ModelAndView;
 8
 9import javax.servlet.http.HttpServletRequest;
10import javax.servlet.http.HttpServletResponse;
11import java.io.PrintWriter;
12
13class LoginIntercepter implements HandlerInterceptor {
14  
15    private static final ObjectMapper objectMapper = new ObjectMapper();
16
17    @Override
18    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
19
20        System.out.println("LoginIntercepter preHandle =====");
21
22        String token = request.getHeader("token");
23        if(StringUtils.isEmpty(token)){
24            token = request.getParameter("token");
25        }
26
27        if(!StringUtils.isEmpty(token)){
28            //判断token是否合法
29            User user = UserServiceImpl.sessionMap.get(token);
30            if(user!=null){
31                return true;
32            }else {
33                JsonData jsonData =  JsonData.buildError("登录失败,token无效",-2);
34                String jsonStr = objectMapper.writeValueAsString(jsonData);
35                renderJson(response,jsonStr);
36                return false;
37            }
38
39        }else {
40
41            JsonData jsonData =  JsonData.buildError("未登录",-3);
42            String jsonStr = objectMapper.writeValueAsString(jsonData);
43            renderJson(response,jsonStr);
44            return false;
45        }
46        //return HandlerInterceptor.super.preHandle(request,response,handler);
47    }
48
49    private void renderJson(HttpServletResponse response,String json){
50
51        response.setContentType("application/json");
52        response.setCharacterEncoding("UTF-8");
53
54        try(PrintWriter writer = response.getWriter()){
55            writer.print(json);
56        }catch (Exception e){
57            e.printStackTrace();
58        }
59    }
60
61    @Override
62    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
63        System.out.println("LoginIntercepter postHandle =====");
64        HandlerInterceptor.super.postHandle(request,response,handler,modelAndView);
65    }
66
67    @Override
68    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
69        System.out.println("LoginIntercepter afterCompletion =====");
70        HandlerInterceptor.super.afterCompletion(request,response,handler,ex);
71    }
72}

TwoIntercepter

 1import org.springframework.web.servlet.HandlerInterceptor;
 2import org.springframework.web.servlet.ModelAndView;
 3
 4import javax.servlet.http.HttpServletRequest;
 5import javax.servlet.http.HttpServletResponse;
 6
 7class TwoIntercepter implements HandlerInterceptor {
 8
 9    @Override
10    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
11        System.out.println("TwoIntercepter preHandle =====");
12        return HandlerInterceptor.super.preHandle(request,response,handler);
13    }
14
15    @Override
16    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
17        System.out.println("TwoIntercepter postHandle =====");
18        HandlerInterceptor.super.postHandle(request,response,handler,modelAndView);
19    }
20
21    @Override
22    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
23        System.out.println("TwoIntercepter afterCompletion =====");
24        HandlerInterceptor.super.afterCompletion(request,response,handler,ex);
25    }
26}

常见模版引擎

JSP(后端渲染,消耗性能)

1Java Server Pages 动态网页技术,由应用服务器中的JSP引擎来编译和执行,再将生成的整个页面返回给客户端
2可以写java代码
3持表达式语言(el、jstl)
4内建函数
5JSP->Servlet(占用JVM内存)permSize
6javaweb官方推荐
7springboot官方不推荐 ,因为JSP极消耗性能

FreeMarker

1FreeMarker Template Language(FTL) 文件一般保存为 xxx.ftl
2严格依赖MVC模式,不依赖Servlet容器(不占用JVM内存)
3内建函数

Thymeleaf (主推)

1轻量级的模板引擎(复杂逻辑业务的不推荐,解析DOM或者XML会占用多的内存)
2可以直接在浏览器中打开且正确显示模板页面
3直接是html结尾,直接编辑xdlcass.net/user/userinfo.html
4社会工程学伪装

定时任务

 1# 使用场景
 2* 某个时间定时处理某个任务
 3* 发邮件、短信等
 4* 消息提醒
 5* 订单通知
 6* 统计报表系统
 7* …
 8
 9# 常见定时任务
10* Java自带的java.util.Timer类配置比较麻烦,时间延后问题
11* Quartz框架: 配置更简单,xml或者注解适合分布式或者大型调度作业(复杂、功能强大)
12* SpringBoot框架自带(便捷)
13
14# SpringBoot使用注解方式开启定时任务
15* 启动类里面 @EnableScheduling开启定时任务,自动扫描
16* 定时任务业务类 加注解 @Component被容器扫描
17* 定时执行的方法加上注解 @Scheduled(fixedRate=2000) 定期执行一次
 1/**
 2 * 启动类里面 @EnableScheduling开启定时任务,自动扫描
 3 * 定时统计订单,金额
 4 */
 5
 6@Component
 7class VideoOrderTask {
 8    //每2秒执行一次
 9    @Scheduled(fixedRate = 2000)
10    public void sum(){
11        //正常的是从数据库中查询
12        System.out.println(LocalDateTime.now() + " 当前交易额="+ Math.random());
13    }
14}

常用定时任务表达式配置和在线生成器

  • cron 定时任务表达式 @Scheduled(cron="*/1 * * * * *") 表示每秒;crontab 表达式生成工具
  • fixedRate: 定时多久执行一次(上一次开始执行时间点后 xx 秒再次执行;)
  • fixedDelay: 上一次执行结束时间点后 xx 秒再次执行 (任务 sleep 也算在内,适合用于任务执行之后再等待若干时间再执行。)
 1import org.springframework.scheduling.annotation.Scheduled;
 2import org.springframework.stereotype.Component;
 3import java.time.LocalDate;
 4import java.time.LocalDateTime;
 5import java.util.Random;
 6
 7/**
 8 * 定时统计订单,金额
 9 */
10
11@Component
12class VideoOrderTask {
13
14    //每2秒执行一次
15    @Scheduled(fixedDelay = 4000)
16    //@Scheduled(fixedRate = 4000)
17    //@Scheduled(cron = "*/1 * * * * *")
18    public void sum(){
19
20        //正常的是从数据库中查询
21        System.out.println(LocalDateTime.now() + " 当前交易额="+ Math.random());
22
23        try {
24            Thread.sleep(2000L);
25        } catch (InterruptedException e) {
26            e.printStackTrace();
27        }
28    }
29}

异步任务

 异步任务相当于多开一个线程去执行,主程序会被立刻返回执行后续的动作,可以减少用户等待时间,提高用户体验。

1# 适用场景
2	什么是异步任务和使用场景:适用于处理log、发送邮件、短信……等
3* 下单接口->查库存 1000
4* 余额校验 1500
5* 风控用户1000
6
7# 创建异步任务的步骤
8* 启动类里面使用@EnableAsync注解开启功能,自动扫描
9* 定义异步任务类并使用@Component标记组件被容器扫描,异步方法加上@Async
 1import org.springframework.scheduling.annotation.Async;
 2import org.springframework.stereotype.Component;
 3
 4@Component
 5@Async
 6public class AsyncTask {
 7    public void task1(){
 8        try {
 9            Thread.sleep(4000L);
10        } catch (InterruptedException e) {
11            e.printStackTrace();
12        }
13        System.out.println(" task 1 ");
14    }
15
16    public void task2(){
17        try {
18            Thread.sleep(4000L);
19        } catch (InterruptedException e) {
20            e.printStackTrace();
21        }
22        System.out.println(" task 2 ");
23    }
24
25    public void task3(){
26        try {
27            Thread.sleep(4000L);
28        } catch (InterruptedException e) {
29            e.printStackTrace();
30        }
31        System.out.println(" task 3 ");
32    }
33}

作者:Soulboy