SpringBoot(三) :最佳实践

x33g5p2x  于2021-10-25 转载在 Spring  
字(12.1k)|赞(0)|评价(0)|浏览(840)

1、最佳实践-SpringBoot应用如何编写

  • 引入场景依赖

  • 官方文档

  • 查看自动配置了哪些(选做)

  • 自己分析,引入场景对应的自动配置一般都生效了

  • 配置文件中debug=true开启自动配置报告。

  • Negative(不生效)

  • Positive(生效)

  • 是否需要修改

  • 参照文档修改配置项

  • 官方文档

  • 自己分析。xxxxProperties绑定了配置文件的哪些。

  • 自定义加入或者替换组件

  • @Bean、@Component…

  • 自定义器 XXXXXCustomizer;

配置文件中debug=true开启自动配置报告:

  • Positive matches    生效的
  • Negative matches   不生效的
  • Unconditional classes。 

============================
CONDITIONS EVALUATION REPORT

Positive matches:

AopAutoConfiguration matched:
      - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
 

Negative matches:

 ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)

Unconditional classes:
----------------------    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

修改启动时的图像

  •  spring.banner.image.location = classpath.2.jpg

2、最佳实践-Lombok简化开发

Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。

spring boot已经管理Lombok。引入依赖:

<dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
</dependency>

IDEA中File->Settings->Plugins,搜索安装Lombok插件。

@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {

    private String name;
    private Integer age;

    private Pet pet;

    public User(String name,Integer age){
        this.name = name;
        this.age = age;
    }
}

简化日志开发

@Slf4j
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(@RequestParam("name") String name){
        log.info("请求进来了....");
        return "Hello, Spring Boot 2!"+"你好:"+name;
    }
}

3、最佳实践-dev-tools

热部署,热更新,以后更新完代码后使用command + f9即可

在IDEA中,项目或者页面修改以后:comand +F9(Build Project)。

Restart:实际上这是自动重启,类似于手动点击重启按钮。

JRebel:付费,重新编译。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

4、最佳实践-Spring Initailizr

Spring Initailizr是创建Spring Boot工程向导。

在IDEA中,菜单栏New -> Project -> Spring Initailizr。

5、配置文件-yaml的用法

同以前的properties用法

YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

非常适合用来做以数据为中心的配置文件。

基本语法

  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '/#'表示注释
  • 字符串无需加引号,如果要加,单引号’’表示字符串内容会被 转义  、双引号""表示字符串内容不转义

数据类型

  • 字面量:单个的、不可再分的值。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

实例

@Data
public class Person {
    private String userName;
    private Boolean boss;
    private Date birth;
    private Integer age;
    private Pet pet;
    private String[] interests;
    private List<String> animal;
    private Map<String, Object> score;
    private Set<Double> salarys;
    private Map<String, List<Pet>> allPets;
}

@Data
public class Pet {
    private String name;
    private Double weight;
}

用yaml表示以上对象

person:
  userName: zhangsan
  boss: false
  birth: 2019/12/12 20:12:33
  age: 18
  pet: 
    name: tomcat
    weight: 23.4
  interests: [篮球,游泳]
  animal: 
    - jerry
    - mario
  score:
    english: 
      first: 30
      second: 40
      third: 50
    math: [131,140,148]
    chinese: {first: 128,second: 136}
  salarys: [3999,4999.98,5999.99]
  allPets:
    sick:
      - {name: tom}
      - {name: jerry,weight: 47}
    health: [{name: mario,weight: 47}]

6、配置文件-自定义类绑定的配置提示

自定义的类和配置文件绑定一般没有提示。若要提示,添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

<!-- 下面插件作用是工程打包时,不将spring-boot-configuration-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>

7、web场景-web开发简介

  • 大多场景我们都无需自定义配置

  • 内容协商视图解析器和BeanName视图解析器

  • 静态资源(包括webjars)

  • 自动注册 Converter,GenericConverter,Formatter

  • 支持 HttpMessageConverters (后来我们配合内容协商理解原理)

  • 自动注册 MessageCodesResolver (国际化用)

  • 静态index.html 页支持

  • 自定义 Favicon

  • 自动使用 ConfigurableWebBindingInitializer (DataBinder负责将请求数据绑定到JavaBean上)
    不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则

声明 WebMvcRegistrations 改变默认底层组件

使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

8、web场景-静态资源规则与定制化

8.1、静态资源访问

  • **存放位置:**只要静态资源放在类路径下: /static ( /public 、 /resources 、 /META-INF/resources
  • 访问 : 当前项目根路径/ + 静态资源名
  • 原理: 静态映射///

访问:http://localhost:8888/4.jpeg    http://localhost:8888/1.jpeghttp://localhost:8888/2.jpeg

http://localhost:8888/3.jpeg

**处理原理:**请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器,到静态资源路径下找。静态资源也找不到则响应404页面。

静态资源访问前缀

application.yml 

spring:
  mvc:
    static-path-pattern: /res/**
  • /*  代表一层目录或目录下的一个文件
  • // 任意层目录任意文件

访问路径:当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找

      http://localhost:8888/res/1.jpeg

也可以改变默认的静态资源路径,/static、``/public/resources/META-INF/resources失效。只有/haha/下的资源有效

spring:
  resources:
    static-locations: [classpath:/haha/]

8.2、web场景-welcome与favicon功能

官方文档

欢迎页支持

静态资源路径(resources路径)下 index.html。 

  • 可以配置静态资源路径
  • 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
  • controller能处理/index。
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效
  resources:
    static-locations: [classpath:/haha/]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎你</title>
</head>
<body>

    <h1>欢迎你</h1>
</body>
</html>

自定义Favicon

指网页标签上的小图标。

favicon.ico 放在静态资源目录下即可。

spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致 Favicon 功能失效

9、web场景-【源码分析】-静态资源原理

9.1、

  • SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
  • SpringMVC功能的自动配置类WebMvcAutoConfiguration,生效

@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 {
    ...
}
  • 给容器中配置的内容:

  • 注意@EnableConfigurationProperties 代表绑定配置文件

  • 配置文件的相关属性的绑定:WebMvcProperties==spring.mvc、ResourceProperties==spring.resources

  • WebMvcProperties和ResourceProperties注册到容器中

public class WebMvcAutoConfiguration {
        
        @Configuration(proxyBeanMethods = false)
        @Import(EnableWebMvcConfiguration.class)
        @EnableConfigurationProperties({ WebMvcProperties.class,         ResourceProperties.class })
        @Order(0)
public static class WebMvcAutoConfigurationAdapter implements         WebMvcConfigurer {

        有参构造器所有参数的值都会从容器中确定
        public WebMvcAutoConfigurationAdapter(WebProperties webProperties,         WebMvcProperties mvcProperties,
        ListableBeanFactory beanFactory,         ObjectProvider<HttpMessageConverters> messageConvertersProvider,
        ObjectProvider<ResourceHandlerRegistrationCustomizer>         resourceHandlerRegistrationCustomizerProvider,
        ObjectProvider<DispatcherServletPath> dispatcherServletPath,
        ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
    this.mvcProperties = mvcProperties;
    this.beanFactory = beanFactory;
    this.messageConvertersProvider = messageConvertersProvider;
    this.resourceHandlerRegistrationCustomizer =          resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
    this.dispatcherServletPath = dispatcherServletPath;
    this.servletRegistrations = servletRegistrations;
    this.mvcProperties.checkConfiguration();
    }

  }
}
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
}
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
}

9.2、注意:

一个配置类只有一个有参构造器,那有参构造器所有参数的值都会默认从容器中确定。

则WebMvcAutoConfigurationAdapter会从容器中获取以下参数

  • ResourceProperties resourceProperties:获取和spring.resources绑定的所有的值的对象
  • WebMvcProperties mvcProperties :获取和spring.mvc绑定的所有的值的对象
  • ListableBeanFactory beanFactory :Spring的beanFactory
  • HttpMessageConverters :找到所有的HttpMessageConverters
  • ResourceHandlerRegistrationCustomizer :找到 资源处理器的自定义器
  • DispatcherServletPath 
  • ServletRegistrationBean 给应用注册Servlet、Filter…

9.3、资源处理的默认规则:

public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
            
        @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));
			}
		}

}

WebMvcAutoConfigurationAdapterl里的addResourceHandlers:

代表加载资源的路径。

  • 拿到ResourceProperties配置文件里绑定的所有内容,即能获取addMappings属性,默认true。
  • 如果addMappings属性设置为false,则直接返回,则不会走下面获取静态资源的代码,则静态资源无论放哪都不会被访问到。
  •  当Cache 和 Cachecontrol 返回304给客户端,让客户端缓存该页面内容一段时间
  • 当访问/webjars//*/*下面的所有请求时,她会到 classpath:/META-INF/resources/webjars/ 路径下找资源。

9.3、数据缓存设置:

@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();

}

resources:
  cache:
    period: 332536000

9.4、访问 /webjars

会自动到classpath:/META-INF/resources/webjars中找资源

http://localhost:8888/webjars/jquery/3.5.1/jquery.min.js


9.5、访问 ///

staticLoscations就是

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
      "classpath:/resources/", "classpath:/static/", "classpath:/public/" };

9.6、欢迎页的处理规则:

handlerMapping:处理器映射器,保存了每一个handler能处理那些请求。

...
public class WebMvcAutoConfiguration {
    ...
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        ...
		@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
				FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
			WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
			welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
			welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
			return welcomePageHandlerMapping;
		}

WelcomePageHandlerMapping的构造方法如下:

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)) {
        //调用Controller /index
        logger.info("Adding welcome page template: index");
        setRootViewName("index");
    }
}

要用欢迎页功能,必须将index.xml放到 /// 下。

这构造方法内的代码也解释了web场景-welcome与favicon功能中配置static-path-pattern了,welcome页面和小图标失效的问题。
 

9.7、根据上述代码,我们可以同过配置禁止所有静态资源规则:

application.yaml中
spring:
  resources:
    add-mappings: false   /#禁用所有静态资源规则

9.8、静态资源规则:

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
 public class ResourceProperties {

  private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {                       "classpath:/META-INF/resources/",
               "classpath:/resources/", "classpath:/static/", "classpath:/public/"

 };
     private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
     ...
    }

}

相关文章