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 就行!!!
配置文件
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 * postHandle:Controller之后调用,视图渲染之前,如果控制器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* Filter和Interceptor二者都是AOP编程思想的体现,功能基本都可以实现
26* 拦截器功能更强大些,Filter能做的事情它都能做
27* Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
28* filter依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
29* 在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
30
31# Filter和Interceptor的执行顺序
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}