SpringBoot2

SpringBoot2 入门

ref:

https://www.bilibili.com/video/BV19K4y1L7MT/

要求

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 + F9Buid -> 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>

目录结构

image-20220905215606582

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

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 渲染完页面后执行

posted @   夏末秋初~  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
点击右上角即可分享
微信分享提示