SpringBoot2
SpringBoot2 入门
ref:
要求
Java 8 & 兼容java14
Maven 3.3+
idea 2019.1.2
配置maven
- 默认jdk版本
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
- 阿里云镜像
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
HelloWorld
配置pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- MainApplication
@SpringBootApplication // 这是一个应用
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
- HelloController
@RestController
public class HelloController {
@RequestMapping("/")
public String hello(){
return "hello SpringBoot2";
}
}
main -> run
关于application.properties
可以修改配置 如 修改端口号
aserver.port = 8888
其他配置可选项详见官方文档
打包成jar
pom配置,无需配置xml,简化部署
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
运行jar
执行
java -jar xxx.jar
即可访问
pom依赖图解
Ctrl + Alt + Shift + U
@ComponentScan
默认扫描主程序所在层级的包,可以修改自定义包
@ComponentScan("com.xxx.boot")
@Configuration
示例
- 原来的配置方式
<?xml ... ?>
<beans ...>
<bean id="user01" class=" com.xxx.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
</beans>
-
使用注解 @Configuration
-
创建两个实体类,User ,Pet
-
MyConfig
@Configuration // 这是一个配置类 相当于 配置了一个xml文件 public class MyConfig { /* * @Bean => 往容器中加组件 * 组件类型 => User, 组件id => user01 (函数名 或 自定义@Bean("xxx")) * */ @Bean("userXiaoMing") public User user01(){ return new User("小明", 18); } @Bean public Pet pet01(){ return new Pet("小猫咪"); } }
-
MainApplication
@SpringBootApplication // 这是一个应用 public class MainApplication { public static void main(String[] args) { // IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); String[] names = run.getBeanDefinitionNames(); // 查看组件 for (String name : names) { System.out.println(name); } } }
-
-
运行结果
... org.springframework.context.event.internalEventListenerFactory mainApplication org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory myConfig helloController userXiaoMing // 《- pet01 // 《- org.springframework.boot.autoconfigure.AutoConfigurationPackages org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration ...
-
可以看到MyConfig本身也是一个组件
组件是单实例的
示例
- 示例一
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 获取实例
User bean1 = run.getBean("userXiaoMing", User.class);
User bean2 = run.getBean("userXiaoMing", User.class);
System.out.println(bean1 == bean2);
System.out.println(bean1.equals(bean2));
}
}
输出
true
true
- 示例二
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 获取MyConfig组件
MyConfig beans = run.getBean(MyConfig.class);
// 获取子组件
User user = beans.user01();
User user1 = beans.user01();
// 输出
System.out.println(user == user1);
System.out.println(user.equals(user1));
}
}
输出
true
true
为什么
Ctrl + 鼠标左键 @Configuration 注解 可以看到
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
proxyBeanMethods 默认为 true ,也就是说使用代理方法,每次请求组件检查是否已经存在该组件,进行重用
修改
@Configuration(proxyBeanMethods = false)
再次运行
结果
false
false
同样,
User有Pet变量时,
return User实例,
User实例拿到的Pet实例
与 MyConfig的Pet实例
不相同
总结
Full -> 全模式 proxyBeanMethods = true 【需要检查,慢】
Lite -> 轻量级模式 proxyBeanMethods = false 【不需要检查,快】
@Import
MyConfig添加注解
@Import({DBAppender.class, User.class})
MainApplication
DBHelper bean = run.getBean(DBHelper.class);
String[] beans = run.getBeanNamesForType(User.class);
System.out.println(bean);
for (String s : beans) {
System.out.println(s);
}
输出
ch.qos.logback.classic.db.DBHelper@36453307
com.abc.boot.bean.User
userXiaoMing
@Conditional
条件装配,满足条件时才进行组件注入
示例
@ConditionalOnBean(name = "dog")
@Bean("userXiaoMing")
public User user01(){
return new User("小明", 18);
}
@Bean("cat")
public Pet pet01(){
return new Pet("小猫咪");
}
存在cat组件时才往容器注入userXiaoMing
run.containsBean("userXiaoMing") // false
@ImportResource
导入Spring配置文件
MyConfig添加注解
@ImportResource("classpath:beans.xml")
beans.xml下的bean会被注册到该容器
@ConfigurationProperties
如果使用myCar会报错,
application.properties
mycar.brand = YADEA
mycar.price = 2000
HelloController
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
...
}
HelloController
@RestController
public class HelloController {
@Autowired // 自动注入
Car car;
@RequestMapping("/car")
public Car car(){
return car;
}
@RequestMapping("/")
public String hello(){
return "hello SpringBoot2";
}
}
启动程序并访问/car
{"brand":"YADEA","price":2000}
@EnableConfigurationProperties
springboot.properties
mycar.brand = YADEA
mycar.price = 2000
MyConfig
@EnableConfigurationProperties(Car.class) // 开启配置绑定功能 自动注册到容器
Car
@ConfigurationProperties(prefix = "mycar") // 此时只需 @ConfigurationProperties
小技巧
Lombok
安装
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
file -> settings -> plugins 搜索并安装lombok插件
使用
标注了@Data的类自动生成setter、getter方法,
标注了@toString的类自动生成toString方法
@NoArgsConstructor -> 无参构造器
@AllArgsConstructor -> 全参构造器
@EqualsAndHashCode -> 按照属性重写哈希值
@Slf4j 的使用
@Slf4j // <<
@RestController
public class HelloController {
@RequestMapping("/")
public String hello() {
log.info("收到请求..."); // <<
return "hello SpringBoot2";
}
}
dev-tools
Restart
安装
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
使用
代码更新后,Ctrl + F9 或 Buid -> Build Project
Spring Initailizr
安装
idea商业版自带
使用
file -> new project -> Spring Initailizr
可选配置
YAML
格式
k: v
k: (k1: v1,k2: v2)
或
k:
k1: v1
k2: v2
数组
k: [v1, v2, v3]
或
k:
- v1
- v2
- v3
单双引号有区别,特殊字符处理
单引号下转义(行为改变)
双引号下不转义(行为不改变)
示例
java
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private String gender;
private Integer age;
private Integer height;
private String[] interests;
private Map<String,Object> score;
private Map<String, List<Pet>> allPets;
}
yml
person:
name: 张三
gender: 男
age: 18
height: 180
interests: [游泳,篮球]
score:
java: 80
c++: 81
html: 82
allPets:
pet1:
- dog
- cat
yml提示-processor
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
排除-processor
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
目录结构
Web开发
静态资源路径默认配置
- ResourceProperties
//21
private static final String[] CLASSPATH_RESOURCE_LOCATIONS =
new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
区分静态资源
静态资源与web请求区分开
增加前缀/res
spring:
mvc:
static-path-pattern: /res/**
或者
# 修改默认
spring:
resources:
static-locations: classpath:/static
多个
resources:
static-locations: [classpath:/static, classpath:/public]
webjars
引入jar
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
引用
http://localhost:8080/webjars/jquery/3.5.1/jquery.js
welcome与favicon
-
静态资源路径下index.html
-
可以配置静态资源路径
-
但是不可以配置静态资源的访问前缀。否则导致index.html不能被默认访问
spring: # mvC: # static-path-pattern: /res/** 这个会 导致welcome page 和 favicon 功能失效 resources: static- locations: [classpath: /haha/]
-
-
controller能处理/index
源码分析
静态资源管理
配置文件相关属性绑定
ResourceProperties => spring.resources
WebMvcProperties => spring.mvc
...
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
...
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
...
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
}
...
}
...
}
静态资源访问处理代码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
Rest映射
OrderedHiddenHttpMethodFilter
HiddenHttpMethodFilter
页面表单的PUT、DELETE请求等需要开启过滤(不开启表单只需要携带_method参数过去)
spring:
mvc:
hiddenmethod:
filter:
enabled: true
# 开启页面表单的Rest功能
默认的_method可修改,代码如下
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_custom");
return methodFilter;
}
}
请求映射x
请求处理
常用注解
普通参数与基本注解
- 注解:
@PathVariable、@RequestHeader、 @ModelAttribute、 @RequestParam、 @MatrixVariable、 @CookieValue、
@RequestBody - Servlet API:
WebRequest、ServletRequest、 MultipartRequest、 HttpSession、 javax.servlet.http.PushBuilder、 Principal、
InputStream、Reader、 HttpMethod、 Locald、 TimeZone、 Zoneld - 复杂参数:
Map、Errors/BindingResult、 Model、 RedirectAttributes、 ServletResponse、 SessionStatus、
UriComponentsBuilder、ServletUriComponentsBuilder - 自定义对象参数:
可以自动类型转换与格式化,可以级联封装。
Thymeleaf
引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置
自动配置
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration {}
SpringTemplateEngine...
路径
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
...
}
简单体验
模板代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}">默认H1内容</h1>
<h2>
<a href="http://www.bilibili.com" th:href="${link}">$去B站</a><br>
<a href="http://www.bilibili.com" th:href="@{link}">@去B站</a><br>
</h2>
</body>
</html>
结果
<h1>hello Thymeleaf</h1>
<a href="http://www.bilibili.com">$去B站</a><br>
<a href="link">@去B站</a><br>
拦截器
登录拦截与静态资源放行
拦截示例
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")// 拦截所有
.excludePathPatterns("/", "/login");
}
}
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");
System.out.println("loginUser == null ?" + (loginUser == null));
if (loginUser != null){
return true;
}
response.sendRedirect("/");
return false;
}
...
}
放行静态资源示例
.excludePathPatterns("/", "/login","/css/**","/js/**","/img/**","/font/**");
postHandle 处理完preHandle执行
afterCompletion 渲染完页面后执行