文章目录
一、配置文件
1. properties文件
properties文件的用法比较熟悉,这里就不过多介绍
2. yaml文件
2.1 简介
- YAML是“YAML Ain`t Markup Language”(是一种标记语言)的递归缩写。
- 适合用来做以数据为中心的配置文件
2.2 基本语法
- key: value;k v之间有一个空格
- 大小写敏感
- 使用缩进进行层级关系
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
#
表示注释
2.3 数据类型
-
字面量:当个、不可分的值。date、Boolean、String、number、null
K: v
-
对象:键值对的集合。map、hash、set、Object
# 行内写法 k: {k1:v1,k2:v2,k3:v3} k: k1: v1 k2: v2 k3: v3
-
数组:一组按次序排列的值。array、list、queue
# 行内写法 k: [v1,v2,v3] k: - v1 - v2 - v3
二、Web开发
1. 简单功能
1.1 静态资源访问
-
类路径下:called:
/static
(or/public
or/resources
or/META-INF/resources
) -
静态资源放到以上目录下,可以通过根目录加静态资源名的方式直接访问
-
静态资源会映射/**,当请求进来时,先去Controller看是否能够处理,若不能处理则将请求交给静态资源处理。当静态资源也不能处理时则会报404。
-
静态资源访问前缀和静态资源路径配置
spring: mvc: # 静态资源的访问前缀路径 static-path-pattern: /res/** web: resources: # 静态资源的文件夹位置 static-locations: classpath:/test
1.2 欢迎页和Favicon
- 欢迎页:默认访问静态资源路径下的index.html文件
- Favicon:favicon.ico放在静态资源路径下即可
1.3 静态资源配置原理
-
SpringBoot启动默认加载xxxAutoConfiguration类(自动配置类)
-
SpringMVC功能的自动配置类WebMvcAutoConfiguration生效
@Configuration( proxyBeanMethods = false ) @ConditionalOnWebApplication( type = Type.SERVLET ) @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}) @ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) @AutoConfigureOrder(-2147483638) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}) public class WebMvcAutoConfiguration {......}
-
给容器中配置的内容
@Configuration( proxyBeanMethods = false ) @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class}) @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class}) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {......}
-
配置文件相关属性和xxx进行了绑定,WebMvcProperties,ResourceProperties,WebProperties
@ConfigurationProperties( prefix = "spring.mvc" ) public class WebMvcProperties {......} @Deprecated @ConfigurationProperties( prefix = "spring.resources", ignoreUnknownFields = false ) public class ResourceProperties extends Resources {......} @ConfigurationProperties("spring.web") public class WebProperties {......}
-
资源处理的默认规则
public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); } else { this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { ServletContextResource resource = new ServletContextResource(this.servletContext, "/"); registration.addResourceLocations(new Resource[]{resource}); } }); } } private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) { this.addResourceHandler(registry, pattern, (registration) -> { registration.addResourceLocations(locations); }); } private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) { if (!registry.hasMappingForPattern(pattern)) { ResourceHandlerRegistration registration = registry.addResourceHandler(new String[]{pattern}); customizer.accept(registration); registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod())); registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl()); registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()); this.customizeResourceHandlerRegistration(registration); } }
-
资源配置的默认值
public static class Resources { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"}; private String[] staticLocations; ...... }
-
欢迎页的处理规则
//HandlerMapping:处理器映射,保存了每一个Handler能处理哪些请求 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations()); return welcomePageHandlerMapping; } //欢迎页的处理逻辑 final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping { private static final Log logger = LogFactory.getLog(WelcomePageHandlerMapping.class); private static final List<MediaType> MEDIA_TYPES_ALL = Collections.singletonList(MediaType.ALL); WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) { if (welcomePage != null && "/**".equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage); setRootViewName("forward:index.html"); } else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { logger.info("Adding welcome page template: index"); setRootViewName("index"); } } ...... }
2. 请求参数处理
2.1 常用注解
@PathVariable 路径变量(取路径中的值)
@RequestHeader 获取请求头
@RequestParam 获取请求参数(指问号后的参数,url?a=1&b=2)
@CookieValue 获取Cookie值
@RequestAttribute 获取request域属性
@RequestBody 获取请求体[POST]
@MatrixVariable 矩阵变量
@ModelAttribute
3. 响应数据
- 响应页面
- 响应数据
- json
- xml
- 音视频
- 自定义协议数据等
3.1 响应JSON
-
Jackson.jar+@ResponseBody
-
导入web模块依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--web starter中包含了JSON相关依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.5.3</version> <scope>compile</scope> </dependency>
-
测试
@Controller @Slf4j public class HelloController { @ResponseBody @GetMapping(value = "/user") public User getUser() { User user = new User(); user.setAge(18); user.setName("Tom"); log.info(user.toString()); return user; } }
4. 拦截器
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
-
编写拦截器实现HandlerInterceptor接口
-
拦截器注册到容器中(实现WebMvcConfigurer的addInterceptor)
-
编写拦截规则
/** * @className: LoginInterceptor * @description: TODO * @version: 1.0 * 1. 配置好拦截器需要拦截的请求 * 2,将这些配置放在容器中 */ public class LoginInterceptor implements HandlerInterceptor { /** * @return boolean * @description 目标方法执行之前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser"); //返回true表示放行 if (!Objects.isNull(loginUser)) { return true; } request.setAttribute("msg", "请先登录"); request.getRequestDispatcher("/").forward(request, response); return false; } @Override //目标方法执行之后 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } @Override //页面渲染之后 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } } //配置拦截器 /** * @className: AdminWebConfig * @description: TODO * @version: 1.0 */ @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) //拦截的路径 .addPathPatterns("/**") //放行的路径 .excludePathPatterns("/", "/login"); } }
6. 文件上传
- 代码测试
@RestController
@Slf4j
public class FileController {
/**
* @return java.util.Map<java.lang.String, java.lang.String>
* @description 文件上传
* [email, username, image, photos]
*/
@PostMapping(value = "/upload")
public Map<String, String> upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestParam("image") MultipartFile image,
@RequestParam("photos") MultipartFile[] photos) throws IOException {
log.info("上传的数据:email={},username={},image={},photos={}", email, username, image.getSize(), photos.length);
if (!image.isEmpty()) {
String originalFilename = image.getOriginalFilename();
image.transferTo(new File("F:\\test\\" + originalFilename));
}
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("F:\\test\\" + originalFilename));
}
}
}
HashMap<String, String> res = new HashMap<>();
res.put("msg", "ok");
res.put("code", "200");
return res;
}
}
-
配置文件
spring: servlet: multipart: # 文件上传的大小 max-file-size: 10MB # 整体文件上传的大小 max-request-size: 100MB
7. 异常处理
-
定义基础接口
/** * @interfaceName: BaseErrorInfoInterface * @description: TODO * @date: 2021/10/15 16:30 * @version: 1.0 */ public interface BaseErrorInfoInterface { /** * 错误码 */ String getResultCode(); /** * 错误信息 */ String getMsg(); }
-
定义异常枚举类
public enum ExceptionEnum implements BaseErrorInfoInterface { SUCCESS("200", "成功"), NOT_FOUND("404", "未找资源"), SERVER_ERROR("500", "服务器内部异常"); /** * 错误码 */ private final String code; /** * 错误信息 */ private final String msg; ExceptionEnum(String code, String msg) { this.code = code; this.msg = msg; } @Override public String getResultCode() { return code; } @Override public String getMsg() { return msg; } }
-
自定义异常
@Data public class MyDefinedException extends RuntimeException { private static final long serialVersionUID = 2L; protected String errorCode; protected String errorMsg; public MyDefinedException() { super(); } public MyDefinedException(BaseErrorInfoInterface baseErrorInfoInterface) { super(baseErrorInfoInterface.getResultCode()); this.errorCode = baseErrorInfoInterface.getResultCode(); this.errorMsg = baseErrorInfoInterface.getMsg(); } public MyDefinedException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) { super(errorInfoInterface.getResultCode(), cause); this.errorCode = errorInfoInterface.getResultCode(); this.errorMsg = errorInfoInterface.getMsg(); } public MyDefinedException(String errorMsg) { super(errorMsg); this.errorMsg = errorMsg; } public MyDefinedException(String errorCode, String errorMsg) { super(errorCode); this.errorCode = errorCode; this.errorMsg = errorMsg; } public MyDefinedException(String errorCode, String errorMsg, Throwable cause) { super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } @Override public Throwable fillInStackTrace() { return this; } }
-
自定义数据传输类
/** * @className: ResultResponse * @description: 传输数据 * @date: 2021/10/15 16:53 * @version: 1.0 */ @Getter @Setter public class ResultResponse { private String code; private String msg; private Object result; public ResultResponse() { } public ResultResponse(BaseErrorInfoInterface errorInfoInterface) { this.code = errorInfoInterface.getResultCode(); this.msg = errorInfoInterface.getMsg(); } public static ResultResponse success() { return success(null); } public static ResultResponse success(Object data) { ResultResponse res = new ResultResponse(); res.setCode(ExceptionEnum.SUCCESS.getResultCode()); res.setMsg(ExceptionEnum.SUCCESS.getMsg()); res.setResult(data); return res; } public static ResultResponse error(BaseErrorInfoInterface errorInfo) { ResultResponse res = new ResultResponse(); res.setMsg(errorInfo.getMsg()); res.setCode(errorInfo.getResultCode()); res.setResult(null); return res; } public static ResultResponse error(String code, String msg) { ResultResponse res = new ResultResponse(); res.setCode(code); res.setMsg(msg); res.setResult(null); return res; } @Override public String toString() { return JSON.toJSONString(this); } }
-
全局异常处理
/** * @className: GlobalExceptionHandler * @description: 全局异常处理 * @date: 2021/10/15 17:05 * @version: 1.0 */ @ControllerAdvice @Slf4j public class GlobalExceptionHandler { /** * @return com.gridsum.springboot.common.ResultResponse * @description 自定义业务异常 * @date 17:15 2021/10/15 */ @ExceptionHandler(value = MyDefinedException.class) @ResponseBody public ResultResponse myDefinedExceptionHandler(HttpServletRequest request, MyDefinedException e) { log.error("业务异常:{}", e.getErrorMsg()); return ResultResponse.error(e.getErrorCode(), e.getErrorMsg()); } /** * @return com.gridsum.springboot.common.ResultResponse * @description 空指针 * @date 17:15 2021/10/15 */ @ExceptionHandler(value = NullPointerException.class) @ResponseBody public ResultResponse exceptionHandler(HttpServletRequest req, NullPointerException e) { log.error("空指针异常:{}", e.getMessage()); return ResultResponse.error("501", "空指针异常"); } /** * @return com.gridsum.springboot.common.ResultResponse * @description 其他异常 * @date 17:15 2021/10/15 */ @ExceptionHandler(value = Exception.class) @ResponseBody public ResultResponse exceptionHandler(HttpServletRequest req, Exception e) { log.error("服务器内部异常:{}", e.getMessage()); return ResultResponse.error(ExceptionEnum.SERVER_ERROR); } }
三、数据访问
1. SQL
1.1 配置数据源
-
导入jdbc场景
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
-
导入数据库驱动
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency>
-
修改配置项
datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db_test username: root password: xxxxxx
2. NoSQL
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置
spring: redis: host: xxx port: 6379 password: xxxxx client-type: jedis
四、单元测试
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
-
常用注解
- @Test:表示方法是测试方法。与JUnit 4的@Test注释不同,这个注释不声明任何属性,因为JUnit Jupiter中的测试扩展基于它们自己的专用注释进行操作。
- @ParameterizedTest:表示方法是参数化测试。
- @RepeatedTest:表示方法是重复测试的测试模板
- @TestFactory:表示方法是动态测试的测试工厂。
- @TestInstance:用于为带注释的测试类配置测试实例生命周期。
- @TestTemplate:表示方法是为测试用例设计的模板,根据注册提供程序返回的调用上下文的数量进行多次调用。
- @DisplayName:声明测试类或测试方法的自定义显示名称。
- @BeforeEach:表示在当前类中每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之前执行注释的方法;类似于JUnit 4的@Before。
- @AfterEach:表示在当前类中的每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之后,都应该执行带注释的方法;类似于JUnit 4的@After。
- @BeforeAll:表示应在当前类中的所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法之前执行带注释的方法;类似于JUnit 4的@BeforeClass。
- @AfterAll:表示在当前类中,所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法都应该执行注释的方法;类似于JUnit 4的@AfterClass。
- @Nested:表示带注释的类是一个嵌套的、非静态的测试类。@BeforeAll和@AfterAll方法不能直接在 @Nested 测试类中使用,除非使用“每个类”测试实例生命周期。
- @Tag:用于在类或方法级别声明过滤测试的标记;类似于TestNG中的测试组或JUnit 4中的类别。
- @Disabled:用于禁用测试类或测试方法;类似于JUnit 4的@Ignore。
- @ExtendWith:用于注册自定义扩展。
五、其他特性
1. 环境切换
-
application-profile功能
- 默认配置文件:application.yaml;任何时候都会加载
- 指定环境配置文件 application-{env}.yaml
- 激活指定环境
- 配置文件激活
- 命令行激活
- 默认配置和环境配置同时生效
- 同名配置项,profile配置优先
-
配置
- application.yaml文件
spring: profiles: # 指定激活的环境 active: prod
- application-prod.yaml文件
# 配置端口号 server: port: 8080
-
通过命令行切换环境
java -jar [项目名称] --spring.profiles.active=[环境]