Springboot 学习笔记,核心自动配置
社区版的 Idea 少了一些配置,从网上下下来的 initializr 直接倒入的还一些配置可能失效,可以看文件前缀判断
HelloWorld web mode
访问 Initializr 定制项目, dependencies 选 Spring Web 即可
下载项目 jar 文件并解压,使用 Idea import,构建项目
Springboot 的项目结构和 Springmvc 基本一样,在 HelloWorldApplication 同级目录下创建 controller 包并添加 controller 类
Springboot 项目默认集成 tomcat,直接运行 application class 即可启动服务器
该 tomcat 应该是优化过的,启动速度飞起,访问 http://localhost:8080/hello
可以看到返回 hello 字符串
查看 idea 右边的 maven tab, 在 Lifecycle 下双击执行 package 打包项目
可以看到打包好的项目 Building jar: ...\helloworld\target\helloworld-0.0.1-SNAPSHOT.jar
到对应的路径下,cmd 窗口输入 java -jar helloworld-0.0.1-SNAPSHOT.jar
可以直接启动
1 2 3 4 5 6 7 @RestController public class TestController { @GetMapping("/hello") public String hello () { return "hello" ; } }
HelloWorld idea mode
Idea -> file -> new -> project -> Spring Initialzr
填入必要信息,可以修改 package 简化路径
其他步骤和上面的练习一样
彩蛋:banner 替换,在 resources 下新建 banner.txt 文件,替换终端启动图标
Autowired 替代方案 方案一 可以通过把注解放到对应的 setter 方法上绕过
1 2 3 4 5 6 InitBean initBean; @Autowired public void setInitBean (InitBean initBean) { this .initBean = initBean; }
方案二 放入构造函数中自动识别
1 2 3 4 5 6 7 public class InitTraceSourceEventListener implements ApplicationListener <ApplicationReadyEvent > { InitBean bean; public InitTraceSourceEventListener (InitBean bean) { this .bean = bean; } }
方案三 用 @Resource
代替
1 2 @Resource InitBean bean;
或者最粗暴的: Settings -> Editor -> Code Style -> Inspections -> Spring Core -> Code -> Field injection warning 选项 disable 掉
自动配置原理初探
核心依赖都在父工程中
写入依赖时不需要指定版本,父类 pom 已经管理了
启动器 , 即 Springboot 的启动场景
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency >
各种场景有对应的启动器,比如 spring-boot-starter-web
, 开发时只需要找到对应的启动器即可
主程序
这部分需要 实操+完善 好几遍才行,流程有点长
1 2 3 4 5 6 7 8 9 @SpringBootApplication public class Hello02Application { public static void main (String[] args) { SpringApplication.run(Hello02Application.class, args); } }
主要注解关系
1 2 3 4 5 6 7 @SpringBootApplication @SpringBootConfiguration - springboot 配置 @Configuration - spring 配置类 @Component - 说明时一个 spring 组件 @EnableAutoConfiguration @AutoConfigurationPackage @Import(AutoConfigurationPackages.Registrar.class)
结论:Springboot 所有自动配置都是在启动的时候扫描并加载(spring.factories). 所有的自动配置配都在里面,但不一定生效。要判断条件是否成立,只有导入了对应的启动器(starter), 才会生效。
spring-boot-autoconfiguration.jar 包含所有的配置
SpringApplication.run() 完了可以深入了解一下,不过,前面的自动装备更重要
YAML 给属性赋值
yaml 和 properties 是可以共存的
共存时 properties 的优先级要高于 yaml
yaml 的后缀可以是 yaml 或 yml 都可以生效
yaml 格式:
可以直接给对象赋值
基本赋值用法 通过添加 @Value 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Component @Data @NoArgsConstructor @AllArgsConstructor public class Dog { @Value("旺财") private String name; @Value("3") private int age; } @SpringBootTest class DogTest { @Autowired private Dog dog; @Test public void test () { System.out.println(dog); } }
yaml 配置属性 配置 pom 依赖
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency >
类添加 @ConfigurationProperties
注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component @Data @NoArgsConstructor @AllArgsConstructor @ConfigurationProperties(prefix = "person") public class Person { private String name; private int age; private boolean isHappy; private Date birth; private Map<String, Object> maps; private List<Object> lists; private Dog dog; }
添加 application.yaml
文件并设置属性
1 2 3 4 5 6 7 8 9 10 person: name: jack age: 30 isHappy: true birth: 2020 /01/01 maps: {k1: v1 , K2: v2 } lists: [1 , 2 , 3 ] dog: name: 旺财 age: 2
1 2 3 4 5 6 7 8 9 10 11 @SpringBootTest class PersonTest { @Autowired private Person person; @Test public void test () { System.out.println(person); } }
PS: yaml 还支持各种随机占位符,一元表达式等,可扩展性要更强
通过 properties 配置 缺点:表示起来比较冗余
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component @Data @NoArgsConstructor @AllArgsConstructor @PropertySource(value="classpath:application.properties") public class Person { @Value("${name}") private String name; private int age; private boolean isHappy; private Date birth; private Map<String, Object> maps; private List<Object> lists; private Dog dog; }
JSR 303 校验 spring 自带的验证注解,添加之后可以再给 bean 赋值的时候带上校验效果
添加依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-validation</artifactId > </dependency >
数据配置
添加 @Validated
, @Email
等注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import org.springframework.validation.annotation.Validated;import javax.validation.constraints.Email;@Component @Data @NoArgsConstructor @AllArgsConstructor @ConfigurationProperties(prefix = "mailbox") @Validated public class MailBox { @Email(message = "Email format is incorrect!!!") private String email; } @SpringBootTest class MailBoxTest { @Autowired private MailBox box; @Test public void test () { System.out.println(box); } }
默认配置文件优先级 方式一:root/config > root/. > classpath:/config > classpath:/.
方式二: 新建 application-xx.properties
, 再 default 中的配置文件中通过 spring.profile.active=xx
指定激活的配置
方式三:yaml + —
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server: port: 8081 spring: profiles: active: dev --- server: port: 8082 spring: profiles: dev --- server: port: 8083 spring: profiles: test
application.properties 中支持的属性源码中在哪里写的
SpringBoot 启动会加载大量的配置类
我们看需要的功能有没有在 SpringBoot 默认写好的自动配置类当中
再看这个配置类中到底配置了哪些组件
给容器中自动皮欸之类添加组件的时候,会从 properties 类中获取某些属性
xxxAutoConfiguration: 自动配置类,给容器添加组件
xxxProperties:封装配置文件中相关属性
debug=true 可以查看配置详情
静态资源加载原理 分析一波 WebMvcAutoConfiuration.java -> webjars, web 相关的包封装成 Java 模式,但是不建议这么做
优先级: resources > script > public
首页定制 getIndexHtml()
template 文件夹下的内容需要使用模板引擎,添加 dependency + 注解
thymeleaf 导入 starter
1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
在 template 下新建页面文件 test.html,新建 controller 文件夹并创建 controller
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <div th:text ="${msg}" > </div > </body > </html >
1 2 3 4 5 6 7 8 @Controller public class TestController { @RequestMapping("/hello") public String test (Model model) { model.addAttribute("msg" , "hello, springboot" ); return "test" ; } }
启动服务,访问 localhost:8080/hello 可以看到新建的页面
MVC 配置原理 https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-developing-web-applications.html
自定义视图解析器 @Configuration + implement WebMvcConfigurer 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Bean public ViewResolver myViewResolver () { return new MyViewResolver(); } public static class MyViewResolver implements ViewResolver { @Override public View resolveViewName (String viewName, Locale locale) throws Exception { return null ; } } }
在 DispatcherServlet 的 doDispatch 方法打上断点,在 this 下的 viewResolver 变量中可以看到自定义的解析器
@Configuration 修饰的类可以帮你扩展功能
员工管理模块案例 1 2 3 4 5 6 7 8 9 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers (ViewControllerRegistry registry) { registry.addViewController("/" ).setViewName("index" ); registry.addViewController("/index.html" ).setViewName("index" ); } }
关闭 thymeleaf cache
1 2 3 spring: thymeleaf: cache: false
i18n 国际化实现
resource 下新建 i18n 文件夹,添加 properties(默认 login.properties + 语言支持版本 login_en_US.properties, login_zh_CN.properties)
自定义 LocaleResolver 做切换
自定义组件配置到 Spring 容器中 @Bean
用 #{} 替换模板
源码中在 WebMvcAutoConfiguration 类中有配置 localeResolver 方法,通过这个引出自定义的类实现
1 2 <a class ="btn btn-sm" th:href ="@{/index.html(l='zh_CN')}" > 中文</a > <a class ="btn btn-sm" th:href ="@{/index.html(l='en_US')}" > English</a >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale (HttpServletRequest request) { String language = request.getParameter("l" ); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(language)) { String[] split = language.split("_" ); locale = new Locale(split[0 ], split[1 ]); } return locale; } @Override public void setLocale (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } public class MyMvcConfig implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver () { return new MyLocaleResolver(); } }
通过 session + 拦截器实现强制登录 LoginController 在登录成功的时候 set 一下 session
1 2 3 4 5 6 7 8 9 10 @RequestMapping("/user/login") public String login (...Model model, HttpSession session) { if ( !StringUtils.isEmpty(username) && "123456" .equals(password)) { session.setAttribute("loginUser" , username); return "redirect:/main.html" ; } }
定制拦截器实现 HandlerInterceptor 接口
1 2 3 4 5 6 7 8 9 10 11 12 public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("loginUser" ); if (loginUser == null ) { request.setAttribute("msg" , "Please login first..." ); request.getRequestDispatcher("/index.html" ).forward(request, response); return false ; } return true ; } }
在 config 类中注册
1 2 3 4 5 6 7 8 9 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**" ) .excludePathPatterns("/index.html" , "/" , "/user/login" , "/css/**" , "/js/**" , "/img/**" ); } }
thymeleaf 标签修改方式 1 2 3 4 5 原来: <script type ="text/javascript" src ="asserts/js/jquery-3.2.1.slim.min.js" > </script > 修改: <script type ="text/javascript" th:src ="@{/js/Chart.min.js}" > </script > <link th:href ="@{/css/bootstrap.min.css}" rel ="stylesheet" >