学习过一个项目,记录一些基本知识点。
程序仓库:https://gitee.com/juncaoit/basic-springboot
目录:
1.springboot 的官网
2.@Controller与@RestController的区别
3.springboot的目录结构
4.同个⽂件的加载顺序
5.解压后的jar包的目录
6.对外接口使用统一的json格式
7.JackSon处理字段
8.添加配置文件
9.单元测试
10.MockMvc
11.全局异常处理
12.拦截器
13.监听器
14.拦截器
15.FreeMarker
1.springboot 的官网
https://spring.io/projects/spring-boot#overview
2.@Controller与@RestController的区别
@Controller 作⽤:⽤于标记这个类是⼀个控制器,返回⻚⾯的时候使⽤;如果要返回JSON,则需要在接⼝上使⽤@ResponseBody才可以
@RestController 作⽤:⽤于标记这个类是⼀个控制器,返回JSON数据的时候使⽤,如果使⽤这个注解,则接⼝返回数据会被序列化为JSON
所以:@RestController = @Controller+@ResponseBody
3.springboot的目录结构
src/main/java:存放代码
src/main/resources
static: 存放静态⽂件,⽐如 css、js、image, (访问⽅式 http://localhost:8080/js/main.js)
templates:存放静态⻚⾯jsp,html,tpl
config:存放配置⽂件,application.properties
resources:
4.同个⽂件的加载顺序
静态资源⽂件 Spring Boot 默认会挨个从:
META/resources > resources > static > public ⾥⾯找是否存在相应的资源
默认配置 spring.resources.static-locations = classpath:/METAINF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
如何添加新的目录,则修改appliction.properties
#文件加载内容
spring.resources.static-locations=classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/templates/
启动之后进行访问页面:
http://localhost:8080/success.html
5.解压后的jar包的目录
org:spring使用的
META-INF:指定main函数的位置,告诉虚拟机入口位置
BOOT-INF:自己的项目下的class与lib
6.对外接口使用统一的json格式
对外转换:
package com.jun.xiaod.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor @Data public class JsonData<T> { private int code; private T data; private String msg; public JsonData(int code, T data) { this.code = code; this.data = data; } public static <T> JsonData<T> buildSuccess(T data) { return new JsonData<T>(0, data, ""); } public static <T> JsonData<T> buildFail(String msg) { return new JsonData<T>(-1, null, msg); } public static <T> JsonData<T> buildFail(int code, String msg) { return new JsonData<T>(code, null, msg); } }
同时,序列化与反序列化工具:
package com.jun.xiaod.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtils {
/**
* 序列化
*/
public static <T> String toJsonString(T t) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(t);
}
/**
* 反序列化
*/
public static <T> T read(String jsonStr) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(jsonStr, new TypeReference<T>(){});
}
}
7.JackSon处理字段
在返回的VO中添加注解:
jackson处理相关⾃动:
指定字段不返回:@JsonIgnore
指定⽇期格式:@JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8")
空字段不返回:@JsonInclude(Include.NON_NULL)
指定别名:@JsonProperty
举例:
package com.jun.xiaod.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Video {
private int id;
/**
* 视频标题
*/
private String title;
/**
* 概述
*/
private String summary;
/**
* 封面图
*/
private String coverImg;
/**
* 价格,分
*/
private int price;
/**
* 创建时间
*/
@JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8")
@JsonProperty("create_time")
private Date createTime;
/**
* 默认8.7,最高10分
*/
private Double point;
}
效果:
其中,video.setCreateTime(new Date());
{
"code": 0,
"data": [
{
"id": 1,
"title": "java",
"summary": null,
"coverImg": null,
"price": 0,
"point": null,
"create_time": "2022-05-06 09:57:54"
}
],
"msg": ""
}
8.添加配置文件
其中,配置文件如下:
#微信支付配置
wxpay.appid=w123456
wxpay.secret=safemn
wx.mechid=121212
其他地方引用使用:
@Resource
private WxConfig wxConfig;
9.单元测试
<!-- spring test框架 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
使用:
在controller的测试类中进行继承。
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {VideoApplication.class})
public class BaseTest {
}
10.MockMvc
增加类注解 @AutoConfigureMockMvc
注⼊⼀个MockMvc类
相关API :
perform执⾏⼀个RequestBuilder请求
andExpect:添加ResultMatcher->MockMvcResultMatchers验证规则
andReturn:最后返回相应的MvcResult->Response
package com.jun.xioad.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.jun.xiaod.controller.VideoController; import com.jun.xiaod.domain.Video; import com.jun.xiaod.utils.JsonData; import com.jun.xiaod.utils.JsonUtils; import com.jun.xioad.BaseTest; import junit.framework.TestCase; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import javax.annotation.Resource; import java.sql.*; import java.util.List; @Slf4j @AutoConfigureMockMvc public class VideoControllerTest extends BaseTest { @Resource private VideoController videoController; @Resource private MockMvc mockMvc; @Test public void testList() throws JsonProcessingException { JsonData<List<Video>> videoList = videoController.getVideoList(); log.info("videoList=={}", JsonUtils.toJsonString(videoList)); TestCase.assertEquals(5, videoList.getData().size()); TestCase.assertTrue(videoList.getData().size() > 0); } /** * mockMvc的使用 */ @Test public void testListMvc() throws Exception { MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/app/v1/test/get/config")) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); int status = mvcResult.getResponse().getStatus(); log.info("ststua={}", status); } }
11.全局异常处理
统⼀的错误⻚⾯或者错误码
对⽤户更友好
package com.jun.xiaod.handler; import com.jun.xiaod.utils.JsonData; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; @RestControllerAdvice public class CustomExtHandler { @ExceptionHandler(value = Exception.class) JsonData<String> handle(Exception e, HttpServletRequest request) { return JsonData.buildFail(-2, "网络正忙,请稍后重试"); } }
思路:
类添加注解
@ControllerAdvice,如果需要返回json数据,则⽅法需要加@ResponseBody
@RestControllerAdvice, 默认返回json数据,⽅法不需要加@ResponseBody
⽅法添加处理器 捕获全局异常,处理所有不可知的异常
@ExceptionHandler(value=Exception.class)
12.拦截器
package com.jun.xiaod.filter; import com.jun.xiaod.domain.User; import com.jun.xiaod.service.impl.UserServiceImpl; import com.jun.xiaod.utils.JsonData; import com.jun.xiaod.utils.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; @Slf4j @WebFilter(urlPatterns = "/api/v1/pri/*", filterName = "loginFilter") public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info("doFilter"); HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 先从请求头中进行获取,再从参数中进行获取 String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { token = request.getParameter("token"); } // 存在token if (!StringUtils.isEmpty(token)) { // 为了验证这里,需要先进行登陆,然后使用登陆之后的token获取user User user = UserServiceImpl.sessionMap.get(token); if (Objects.nonNull(user)) { filterChain.doFilter(servletRequest, servletResponse); } else { renderJson(response, JsonUtils.toJsonString(JsonData.buildFail(-2, "登陆失败,没有登陆过"))); } } else { renderJson(response, JsonUtils.toJsonString(JsonData.buildFail(-3, "登陆失败,无token"))); } } /** * 输出流输出 */ private void renderJson(HttpServletResponse response, String json) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); try (PrintWriter writer = response.getWriter()) { writer.println(json); } catch (Exception e) { e.printStackTrace(); } } }
使用地方:
@Slf4j @RestController @RequestMapping("/api/v1/pri/order") public class VideoOrderController { @Resource private VideoServiceImpl videoService; @PostMapping("/save") public JsonData<String> saveVideo() { log.info("/save"); return JsonData.buildSuccess("下单成功"); } }
思路:
启动类⾥⾯增加 @ServletComponentScan,进⾏扫描
新建⼀个Filter类,implements Filter,并实现对应的接⼝
@WebFilter 标记⼀个类为filter,被spring进⾏扫描。urlPatterns:拦截规则,⽀持正则,控制chain.doFilter的⽅法的调⽤,来实现是否通过放⾏。不放⾏,web应⽤resp.sendRedirect("/index.html") 或者 返回json字符串
效果:
13.监听器
常见的监听器
ServletContextListener 应⽤启动监听
HttpSessionLisener 会话监听
ServletRequestListener 请求监听
/** * 应用启动监听器 */ @Slf4j @WebListener public class ApplicationListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("ApplicationListener init"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("ApplicationListener destory"); } }
14.拦截器
注册拦截器
package com.jun.xiaod.config; import com.jun.xiaod.interceptor.LoginInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 拦截器啥的 * 不依赖容器 */ @Configuration public class CustomerWebMvcConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/v1/**"); WebMvcConfigurer.super.addInterceptors(registry); } public LoginInterceptor getLoginInterceptor() { return new LoginInterceptor(); } }
定义拦截器:
package com.jun.xiaod.interceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("LoginInterceptor preHandle "); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("LoginInterceptor postHandle "); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("LoginInterceptor afterCompletion "); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
15.FreeMarker
pom
<!--freemarker--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
配置文件
#freemarker spring.freemarker.cache=false spring.freemarker.charset=utf-8 spring.freemarker.allow-request-override=false spring.freemarker.check-template-location=true spring.freemarker.content-type=text/html spring.freemarker.expose-request-attributes=true spring.freemarker.expose-session-attributes=true spring.freemarker.suffix=.ftl spring.freemarker.template-loader-path=classpath:/templates/
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>payId:${setting.payId}</h2> </body> </html>
后端
@Controller @RequestMapping("/api/v1/freemarker") public class FreemarkerController { @Resource private WxConfig wxConfig; @GetMapping("/get/config") public String getConfig(ModelMap modelMap) { modelMap.addAttribute("setting", wxConfig); // 不用添加后缀 return "fm/user"; } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~