一、配置文件
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;
......
}
-
欢迎页的处理规则
@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>
<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) -
编写拦截规则
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
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 {
}
}
@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 {
@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. 异常处理
-
定义基础接口
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;
}
}
-
自定义数据传输类
@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);
}
}
-
全局异常处理
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = MyDefinedException.class)
@ResponseBody
public ResultResponse myDefinedExceptionHandler(HttpServletRequest request, MyDefinedException e) {
log.error("业务异常:{}", e.getErrorMsg());
return ResultResponse.error(e.getErrorCode(), e.getErrorMsg());
}
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public ResultResponse exceptionHandler(HttpServletRequest req, NullPointerException e) {
log.error("空指针异常:{}", e.getMessage());
return ResultResponse.error("501", "空指针异常");
}
@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配置优先
-
配置
spring:
profiles:
active: prod
server:
port: 8080
-
通过命令行切换环境 java -jar [项目名称] --spring.profiles.active=[环境]
|