• SpringBoot Features


    . SpringApplication

      1. Startup Failure 启动失败

          注入FailureAnalyzers 获得一个机会去提供一个详细的错误信息

          SpringBoot提供了很多FailureAnalyzer 的实现类,也可以添加自定义的

          如果没有failure analyzers 可以开启debug模式 java -jar xxx.jar --debug

          FailureAnalyzer实现必须在META-INF/spring.factories中注册,如: 

    org.springframework.boot.diagnostics.FailureAnalyzer=
    com.example.ProjectConstraintViolationFailureAnalyzer

      2. Customizing the Banner 自定义横幅

          https://blog.csdn.net/u011447164/article/details/86009262

      3. Customizing SpringApplication

    @SpringBootApplication
    public class SpringBootJarApplication {
        public static void main(String[] args) {
            SpringApplication app = new SpringApplication(SpringBootJarApplication.class);
            app.setBanner((e, c, o) -> o.println("BINGJJFLY") );
            app.run(args);
        }
    }
    

      4. Fluent Builder API 流畅的Builder API

    @SpringBootApplication
    public class SpringBootJarApplication {
        public static void main(String[] args) {
            new SpringApplicationBuilder()
                    .sources(SpringBootJarApplication.class)
                    .bannerMode(Banner.Mode.CONSOLE)
                    .banner((e, c, o) -> o.println("BINGJJFLY"))
                    .run(args);
        }
    }
    

      5. Application Events and Listeners 应用事件和监听器

          除了常见的spring框架事件(如ContextRefreshedEvent)之外,SpringApplication还发送一些额外的应用程序事件

          添加监听器方式:

            (1)SpringApplication.addListeners(…)

            (2)SpringApplicationBuilder.listeners(…)

            (3)META-INF/spring.factories 文件中添加:

    org.springframework.context.ApplicationListener=com.example.project.MyListener
    

          事件发送顺序:

            (1)ApplicationStartingEvent:在运行开始时但在任何处理之前发送,除非用于注册侦听器和初始化器

            (2)ApplicationEnvironmentPreparedEvent:在已知上下文中要使用的Environment,但在创建上下文之前发送

            (3)ApplicationPreparedEvent:在刷新开始之前但在加载bean定义之后发送

            (4)ApplicationStartedEvent:在刷新上下文之后,但在调用任何应用程序和命令行运行程序之前发送

            (5)ApplicationReadyEvent:在调用任何应用程序和命令行运行程序后发送。它表示应用程序已准备好为请求提供服务

            (6)ApplicationFailedEvent:启动出现异常时发送

            https://baijiahao.baidu.com/s?id=1617119527751442850&wfr=spider&for=pc

      6. Web Environment  

           web环境设置,test环境可以指定为非web

    @SpringBootApplication
    public class SpringBootJarApplication {
        public static void main(String[] args) {
            new SpringApplicationBuilder(SpringBootJarApplication.class)
                    .web(WebApplicationType.NONE)
                    .run(args);
        }
    }
    

      7. Accessing Application Arguments 获得系统运行参数

    @Controller
    @RequestMapping("/args")
    public class ArgsController {
        @Autowired
        private ApplicationArguments args;
        @Value("profile")
        private String profile;
        @RequestMapping("/application")
        public void application() {
            List<String> nonOptionArgs = args.getNonOptionArgs();
            nonOptionArgs.forEach(o -> System.out.println("运行参数(ApplicationArguments方式):" + o));
            System.out.println("运行参数(@Value方式):" + profile);
        }
    }
    

      8. Using the ApplicationRunner or CommandLineRunner 

          SpringApplication完全启动之前(run方法执行完之前、发布ApplicationReadyEvent事件之前) 进行特殊操作

    @Slf4j
    @Order(1) // 两个都存在时可以加顺序
    @Component
    public class ArgsCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            log.info("CommandLineRunner方式获得运行参数:{}", args);
        }
    }
    
    @Slf4j
    @Order(2)
    @Component
    public class ArgsApplicationRunner implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            log.info("ApplicationRunner方式获得运行参数:{}", args.getNonOptionArgs());
        }
    }
    

      9. Application Exit 优雅的退出

        每个Spring应用都会向JVM注册一个关机钩子以优雅的退出,Spring标准回调方式:DisposableBean、@PreDestroy

        SpringBoot额外提供ExitCodeGenerator

    @Bean
    public ExitCodeGenerator ecg() {
        return () -> 17;
    }
    

      10. Admin Features

          https://www.jianshu.com/p/a9e35674c530

    Ⅱ. Externalized Configuration 外部化配置

      外部化配置文件方式:properties、yaml,获取配置信息方式:@Value、Environment、@ConfigurationProperties   

      1. Configuring Random Values 

    my.secret=${random.value}
    my.number=${random.int}
    my.bignumber=${random.long}
    my.uuid=${random.uuid}
    my.number.less.than.ten=${random.int(10)}
    my.number.in.range=${random.int[1024,65536]}
    

      2. Accessing Command Line Properties

          SpringApplication可以转换所有命令行参数(--arg=val)到Environment,如果不想映射到其中可以:

    SpringApplication.setAddCommandLineProperties(false)
    

      3. Application Property Files 

           加载application.properties的顺序(First Match Wins策略):

            根目录下/config目录及子目录

            根目录

            类路径下/config目录及子目录 

          重定义配置文件信息

    java -jar myproject.jar --spring.config.name=myproject
    java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/
    override.properties

          spring.config.location如果是目录必须以'/'结尾, 且在spring.config.name加载,加载顺序正好和First Match Wins相反(Last Match Wins),且覆盖上述的加载顺序
          spring.config.additional-location扩展加载范围,且在上述加载顺序之前

      4. Profile-specific Properties

          加后缀配置文件总是覆盖非特定配置文件

          application.properties(公共配置信息)

    spring.profiles.active=@{spring.active}
    

          application-dev.properties(开发环境配置信息)

    server.port=8081
    

          application-prod.properties(生产环境配置信息)  

    server.port=8082
    

        重定义resources插件的分隔符,SpringBoot将分隔符修改成了 '@'(@spring.active@)

    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    		<plugin>
    			<groupId>org.apache.maven.plugins</groupId>
    			<artifactId>maven-resources-plugin</artifactId>
    			<configuration>
    				<delimiters>
    					<delimiter>@{*}</delimiter>
    				</delimiters>
    				<useDefaultDelimiters>false</useDefaultDelimiters>
    			</configuration>
    		</plugin>
    	</plugins>
    	<resources>
    		<resource>
    			<directory>src/main/webapp</directory>
    		</resource>
    		<resource>
    			<directory>src/main/resources</directory>
    			<filtering>true</filtering>
    		</resource>
    	</resources>
    </build>
    <profiles>
    	<profile>
    		<id>dev</id>
    		<properties>
    			<spring.active>dev</spring.active>
    		</properties>
    		<activation>
    			<activeByDefault>true</activeByDefault>
    		</activation>
    	</profile>
    	<profile>
    		<id>prod</id>
    		<properties>
    			<spring.active>prod</spring.active>
    		</properties>
    	</profile>
    </profiles>
    

        打包命令

    mvn clean package -Dmaven.test.skip=true -P prod
    

      5. Placeholders in Properties 占位符  

    app.name=MyApp
    app.description=${app.name} is a Spring Boot application
    # --port=7797 代替 --server.port=7797
    server.port=${port:8080}
    

      6. Encrypting Properties 加密属性  

          SpringBoot不支持直接对属性加密,但是提供EnvironmentPostProcessor(自定义参考SpringApplicationJsonEnvironmentPostProcessor)对Environment进行操作在应用启动之前 
          resources下添加META-INF/spring.factories文件      

    org.springframework.boot.env.EnvironmentPostProcessor=
    com.wjz.config.processor.EnvironmentPostProcessorExample
    

          创建一个包含敏感信息的配置文件encrypting.properties

    db.password=encrypt_password
    

          自定义EnvironmentPostProcessor,order大则优先级低

    package com.wjz.config.processor;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.env.EnvironmentPostProcessor;
    import org.springframework.boot.env.OriginTrackedMapPropertySource;
    import org.springframework.boot.env.PropertiesPropertySourceLoader;
    import org.springframework.core.Ordered;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.MapPropertySource;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    
    import java.io.IOException;
    import java.util.Map;
    
    public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor, Ordered {
    
        public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 100;
        private int order = DEFAULT_ORDER;
        private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();
    
        @Override
        public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
            Resource path = new ClassPathResource("encrypting.properties");
            PropertySource<Map<String, Object>> propertySource = loadProperties(path);
            environment.getPropertySources().addLast(propertySource);
        }
    
        private PropertySource<Map<String, Object>> loadProperties(Resource path) {
            if (!path.exists()) {
                throw new IllegalArgumentException("Resource "+ path + "does not exist");
            }
            try {
                return new DecryptedMapPropertySource((OriginTrackedMapPropertySource) loader.load("custom-resource", path).get(0));
            } catch (IOException e) {
                throw new IllegalStateException("Failed to load properties configuration from " + path, e);
            }
        }
    
        private static class DecryptedMapPropertySource extends MapPropertySource {
    
            public DecryptedMapPropertySource(OriginTrackedMapPropertySource propertySource) {
                super(propertySource.getName(), propertySource.getSource());
            }
    
            @Override
            public Object getProperty(String name) {
                Object val = super.getProperty(name);
                if ("db.password".equals(name)) {
                    return "DecryptedValue";
                }
                return val;
            }
        }
    
        @Override
        public int getOrder() {
            return order;
        }
    }
    

      7. Using YAML Instead of Properties 

          YamlPropertySourceLoader可以加载yaml文件为PropertySource,这样就可以使用@Value获得属性值 

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    
    PropertySource<?> propertySource = loadYaml(path);
    
    private PropertySource<?> loadYaml(Resource path) {
    	if (!path.exists()) {
    		throw new IllegalArgumentException("Resource " + path + " does not exist");
    	}
    	try {
    		return this.loader.load("custom-resource", path).get(0);
    	}catch (IOException ex) {
    		throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
    	}
    }
    

          spring.profiles既支持字面量(prod)也支持表达式(prod & prev),表达式运算符(‘!’、‘&’、‘|’)

          production & us-east | eu-central 无效,production & (us-east | eu-central) 有效

          Yaml缺点:

            1. 不能使用@PropertySource获得属性值

            2. 特定profile文件和多文档不能结合使用,如 application-dev.yml

    server:
    	port: 8000
    ---
    spring:
    	profiles: !test
    	security:
    		user:
    			password: weak
    

      8. Type-safe Configuration Properties 属性映射为对象  

          1. Map初始化后可省略setter

          2. Collection或Array推荐添加setter,即使初始化不能添加final

          3. POJO一般可省略setter

          4. 如果使用Lombok则确保没有特殊的构造器

          5. 静态变量不能被映射 

          配置类中使用配置信息参考git:spring-boot-jar.EmailAutoConfiguration

    @Configuration
    @EnableConfigurationProperties(AcmeProperties.class)
    public class MyConfiguration {
    }
    

          这种形式下,AcmeProperties的BeanName为<profile>-<fqn>(前缀-类的全限定名),形如:acme-com.example.AcmeProperties
          除了上述这种形式还可以将配置信息类作为Bean组件(即添加@Component)

    @Component
    @ConfigurationProperties(prefix="acme")
    public class AcmeProperties {
    
    }
    

          使用@configurationproperties还可以生成元数据文件(spring-configuration-metadata.json),IDE可以根据该文件在配置文件中进行提示

    {
      "groups": [
        {
          "name": "boot.email",
          "type": "com.wjz.config.EmailProperties",
          "sourceType": "com.wjz.config.EmailProperties"
        }
      ],
      "properties": [
        {
          "name": "boot.email.power",
          "type": "java.lang.String",
          "sourceType": "com.wjz.config.EmailProperties"
        }
      ],
      "hints": []
    }

          前提条件是添加依赖

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

        Third-party Configuration  第三方依赖配置

            类似于web有service的依赖,但是web想为service的某个类映射属性值

    @ConfigurationProperties(prefix = "service")
    @Bean
    public AnotherComponent anotherComponent() {	
        return new AnotherComponent();
    }
    

        Relaxed Binding 松散绑定

            连接号形式(驼峰格式):context-path == contextPath

            大写形式(系统变量):PORT == port

            松散绑定方式

    acme.my-project.person.first-name		Kebab case格式,推荐使用在.properties和.yml文件中
    acme.myProject.person.firstName			标准驼峰格式
    acme.my_project.person.first_name		下划线格式
    ACME_MYPROJECT_PERSON_FIRSTNAME			全大写格式,为系统变量时推荐使用
    

            @ConfigurationProperties的prefix属性必须为kebab case格式

        Merging Complex Types 映射复杂类型

            当在多个位置配置集合时,重写通过替换整个集合来工作

            List在多个profile中配置时,只有一个profile起作用

            Map在多个文件中配置时,只有一个文件(优先级高的)起作用

        Properties Conversion 属性转换

             如果想自定义属性类型转换,提供一个ConversionService(BeanName为conversionService) 、自定义属性编辑器(通过CustomEditorConfigurer)、自定义Converters(通过@ConfigurationPropertiesBinding)

            Converting durations jdk8新特性日期类型

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);
    

              映射类型:秒 30,PT30s,30s、毫秒 500, PT0.5S and 500ms,默认为毫秒除非指定了@DurationUnit
            Converting Data Sizes 数据大小

    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);
    

              映射类型:M 10,10MB、B 256,256B,默认为B除非指定了@DataSizeUnit
            @ConfigurationProperties Validation JSR-303数据校验 

    @ConfigurationProperties(prefix="acme")
    @Validated
    public class AcmeProperties {
    	@NotNull
    	private InetAddress remoteAddress;
    }
    

              默认的@Validated也可以用在@Bean修饰的方法上

    @Configuration
    public class AcmeConfiguration {
    	@Bean
    	@Validated
    	private InetAddress remoteAddress(){
    		return new InetAddress();
    	}
    }
    

              尽管内嵌属性在绑定时也将被验证,但最好同时注释关联字段为@Valid

    @ConfigurationProperties(prefix="acme")
    @Validated
    public class AcmeProperties {
    	@NotNull
    	private InetAddress remoteAddress;
    	@Valid
    	private final Security security = new Security();
    
    	public static class Security {
    		@NotEmpty
    		public String username;
    	}
    }
    

              还可以通过创建一个名为configurationPropertiesValidator的bean定义来添加一个自定义的Spring验证器,@Bean修饰的方法必须为static的

              spring-boot-sample-property-validation

            @ConfigurationProperties vs. @Value

                功能                  批量映射                    逐一映射
                松散绑定 支持 不支持
                SpEL 不支持 支持
                JSP303数据校验 支持 不支持
                复杂类型 支持 不支持

    Ⅲ. Profiles

        任意一个@Component或@Configuration都可以注释一个@Profile以适应不同的环境,不符合条件的组件不会被注入到容器中

    @Configuration
    @Profile("production")
    public class ProductionConfiguration {
    }    

              application.properties中

    spring.profiles.active=dev,hsqldb
    

              命令行中

    java -jar xxx.jar --spring.profiles.active=dev,hsqldb    

        1. Adding Active Profiles

            即使指定了spring.profiles.active=prod,profiles也包含prev

            application.properties   

    spring.profiles.include=prev
    

            API

    new SpringApplicationBuilder(SpringBootJarApplication.class)
    		.profiles("prev")
    		.run(args);
    
    SpringApplication application = new SpringApplication(SpringBootJarApplication.class);
            application.setAdditionalProfiles("prev");
            application.run(args);
    

    Ⅳ. Logging

      1. File Output 输出到文件   

         默认的Spring Boot只输出日志到控制台,修改则设置logging.file或logging.path

         logging.file 指定的日志文件,可以是确切的位置或相对于当前目录

         logging.path 指定日志存放目录,可以使确切目录或相对于当前目录

         默认的日志文件大小为10M,超过限制则创建新的日志文件,且无数量限制

    logging.file.max-size=5KB
    logging.file.max-history=3
    

      2. Log Levels       

    logging.level.root=WARN
    logging.level.org.springframework.web=DEBUG
    logging.level.org.hibernate=ERROR 

      3. Log Groups

    logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat
    logging.level.tomcat=TRACE
    
    # 两个开箱即用的日志组web,sql
    # logging.group.web=org.springframework.core.codec, org.springframework.http, org.springframework.web
    # logging.group.sql=org.springframework.jdbc.core, org.hibernate.SQL
    logging.level.web=TRACE
    logging.level.sql=TRACE
    

      4. Custom Log Configuration

         可以自定义日志配置文件,在类路径下或在“logging.config=classpath:logback-spring.xml”(Springboot推荐加‘-spring’)指定的路径下

            可以通过系统属性指定日志系统,日志部分源码分析

    System.setProperty("org.springframework.boot.logging.LoggingSystem", LoggingSystem.NONE);
    System.setProperty("org.springframework.boot.logging.LoggingSystem", LogbackLoggingSystem.class.getName());
    

         参考LoggingApplicationListener源码可知,日志组件先于ApplicationContext创建,所以不可能通过配置文件修改(@PropertySources或@Configuration),唯一修改或禁用的方式就是通过设置系统属性

         如果自定义日志系统的话,“logging.file”、“logging.path”等在配置文件中的配置则不起作用,Springboot提供了一些Environment转为System Properteis的配置项

    logging.file --> LOG_FILE
    logging.path --> LOG_PATH
    ...

      5. Logback Extensions 扩展功能

         文件名为“logback-spring.xml”才可以使用扩展功能 

         指定Profile,不同的环境使用不同的配置信息

    <springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
    </springProfile>
    <springProfile name="dev | staging">
    <!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
    </springProfile>
    

            获取环境变量,获得application.properties中配置的信息

         scope为变量作用域、source为配置文件key(必须是kebab case),defaultValue为默认值

    <springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
    defaultValue="localhost"/>
    <appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
      <remoteHost>${fluentHost}</remoteHost>
    </appender>
    

    Ⅴ. Internationalization 国际化

       国际化资源配置信息

    spring.messages.basename=messages,config.i18n.messages
    spring.messages.fallback-to-system-locale=false
    

       自动化配置类:org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration 

       国际化资源可配置信息及作用:org.springframework.boot.autoconfigure.context.MessageSourceProperties

    Ⅵ. JSON

       SpringBoot提供三种方式:Gson、Jackson、JSON-B,推荐使用的是Jackson

       支持Jackson的自动化配置,并且提供了启动器spring-boot-starter-json

        自定义ObjectMapper

         Spring MVC使用HttpMessageConverters进行服务器端和客户端的数据类型转换,Jackson2ObjectMapperBuilder会通过自动化配置注入到容器中  

           默认的自定义属性,参见JackAutoConfiguration、Jackson2ObjectMapperBuilder(customizeDefaultFeatures方法)

    MapperFeature.DEFAULT_VIEW_INCLUSION is disabled
    DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled
    SerializationFeature.WRITE_DATES_AS_TIMESTAMPS is disabled
    

        Springboot提供了大量的开关配置以自定义特性

         Jackson2ObjectMapperBuilder 可以通过一个或多个 Jackson2ObjectMapperBuilderCustomizer 进行自定义 ObjectMapper,支持排序可在默认之前或之后(实现Ordered

    接口,重写getOrder方法或使用@Order注解)

         com.fasterxml.jackson.databind.Module 的Bean会自动注册到 Jackson2ObjectMapperBuilder 

         如果想替换掉默认的ObjectMapper 使用 @Bean 注释且标记 @Primary

         JacksonAutoConfiguration 注入 Jackson2ObjectMapperBuilder、StandardJackson2ObjectMapperBuilderCustomizer、ObjectMapper 

         HttpMessageConvertersAutoConfiguration(数据转换器自动配置器)注入 StringHttpMessageConverter

         JacksonHttpMessageConvertersConfiguration 注入 MappingJackson2HttpMessageConverter 和 MappingJackson2XmlHttpMessageConverter

         添加自定义的转换器,与MVC不同的是,这种方法是添加额外的而不是替换默认的

    @Configuration
    public class JacksonConfiguration implements WebMvcConfigurer {
    
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                    .indentOutput(true)
                    .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                    .modulesToInstall(new ParameterNamesModule());
            converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        }
    }
    

    Ⅶ. Developing Web Applications 

      Spring MVC框架

        Spring MVC Auto-configuration  

            自动配置提供的特性(WebMvcAutoConfiguration):

              1.  InternalResourceViewResolver、ContentNegotiatingViewResolver 和 BeanNameViewResolver  

              2. 支持依赖方式引入静态资源,支持WebJars 

              3. 自动注册 Converter、GenericConverter 和 Formatter 

              4. 支持 HttpMessageConverters 消息转换器

              5. 自动注册 MessageCodesResolver

              6. 支持静态 index.html 文件,欢迎页

              7. 自定义 Favicon 网站图标

              8. 自动使用 ConfigurableWebBindingInitializer 

            如果想保持Spring Boot MVC的特性并且想添加自己的特性(拦截器、视图解析器等),创建一个类实现 WebMvcConfigurer,

    注释@Configuration且不能注释@EnableWebMvc

          HttpMessageConverters

             Spring MVC使用HttpMessageConverter进行信息转换,Spring Boot提供 HttpMessageConverters 自定义 HttpMessageConverter,添加了这个默认的HttpMessageConverters 就失效了

    @Configuration
    public class HttpMessageConvertersConfiguration {
    
        @Bean
        public HttpMessageConverters messageConverters() {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                    .indentOutput(true)
                    .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                    .modulesToInstall(new ParameterNamesModule());
            return new HttpMessageConverters(new MappingJackson2HttpMessageConverter(builder.build()));
        }
    }
    

          Custom JSON Serializers and Deserializers

             覆盖默认ObjectMapper  

    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    	ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    	SerializerProvider serializerProvider = objectMapper.getSerializerProvider();
    	serializerProvider.setNullValueSerializer(new NullValueSerializer());
    	return objectMapper;
    }
    

               创建NullValueSerializer

    public class NullValueSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            try {
                jgen.writeString("");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

          MessageCodesResolver

             当指定一个“spring.mvc.message-codes-resolver-format”时,Spring Boot会创建一个MessageCodesResolver

            Static Content 静态资源

             Spring Boot默认四个静态文件读取路径:/static、/public、/resources、/META-INF/resources,是因为使用了ResourceHttpRequestHandler (xml配置方式:<mvc:resources mapping="/static/**" location="/static/" />),我们可以通过实现 WebMvcConfigurer 接口覆盖 addResourceHandlers 方法

    @Component
    public class AdditionalResourceHandler implements WebMvcConfigurer {
        private final ResourceProperties resourceProperties;
    
        public AdditionalResourceHandler(ResourceProperties resourceProperties) {
            this.resourceProperties = resourceProperties;
        }
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            Duration cachePeriod = resourceProperties.getCache().getPeriod();
            CacheControl cacheControl = resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
            registry.addResourceHandler("/additional/**")
                    .addResourceLocations("classpath:/additional/")
                    .setCachePeriod(getSeconds(cachePeriod))
                    .setCacheControl(cacheControl);
        }
    
        private Integer getSeconds(Duration cachePeriod) {
            return cachePeriod != null ? (int) cachePeriod.getSeconds() : null;
        }
    }

            修改默认读取路径

    # 默认的spring.mvc.static-path-pattern=/,访问地址:http://127.0.0.1:8080/1.js
    # 替换后访问地址:http://127.0.0.1:8080/static/1.js
    spring.mvc.static-path-pattern=/static/**
    
    # 替换默认的静态资源读取路径
    spring.resources.static-locations=classpath:/static,classpath:/public
    

             webJars 方式引入静态文件,参考 WebMvcAutoConfiguration.addResourceHandlers,访问地址:http://127.0.0.1:8080/webjars/jquery/3.4.1/jquery.js

    <dependency>
    	<groupId>org.webjars</groupId>
    	<artifactId>jquery</artifactId>
    	<version>3.4.1</version>
    </dependency>                
    

             静态资源缓存破坏,参考:https://www.iteye.com/blog/412887952-qq-com-2342354

               默认的Thymeleaf和FreeMarker可以直接配置ResourceUrlEncodingFilter

    @Bean
    public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
    	return new ResourceUrlEncodingFilter();
    }
    

               如果是JSP则可以使用ResourceUrlProvider 

    @ControllerAdvice
    public class UrlProviderController {
        @Autowired
        private ResourceUrlProvider provider;
        @ModelAttribute("urls")
        public ResourceUrlProvider urls() {
            return provider;
        }
    }
    

              MD5方式

    spring.resources.chain.strategy.content.enabled=true
    spring.resources.chain.strategy.content.paths=/**

              版本方式

    spring.resources.chain.strategy.fixed.enabled=true
    spring.resources.chain.strategy.fixed.paths=/js/,/css/,/img/
    spring.resources.chain.strategy.fixed.version=1.0.0
    

              版本方式添加依赖

    <dependency>
    	<groupId>org.webjars</groupId>
    	<artifactId>webjars-locator-core</artifactId>
    	<version>0.40</version>
    </dependency>

              版本方式目录结构

              页面配置(必须 '/' 开头)

    <link th:href="@{/1.css}" rel="stylesheet"/>
    <script type="text/javascript" th:src="@{/webjars/js/1.js}"></script>
    <script type="text/javascript" th:src="@{/webjars/jquery/jquery.js}"></script>
    

          Welcome Page              

              SpringBoot 提供静态的和模板形式的欢迎页,首先会找静态资源路径下的index.html文件,如果没有再找index模板页

              WebMvcAutoConfiguration中的WelcomePageHandlerMapping

    WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
    	if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
    		logger.info("Adding welcome page: " + welcomePage.get());
    		this.setRootViewName("forward:index.html");
    	} else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
    		logger.info("Adding welcome page template: index");
    		this.setRootViewName("index");
    	}
    }
    
    private boolean welcomeTemplateExists(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext) {
        return templateAvailabilityProviders.getProvider("index", applicationContext) != null;
    }

              TemplateAvailabilityProviders中会读取spring.factories配置文件(SpringFactoriesLoader.loadFactories(TemplateAvailabilityProvider.class, classLoader);)

    # Template availability providers
    org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,
    org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,
    org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
    

              以ThymeleafTemplateAvailabilityProvider为例

    public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) {
    	if (ClassUtils.isPresent("org.thymeleaf.spring5.SpringTemplateEngine", classLoader)) {
    		String prefix = environment.getProperty("spring.thymeleaf.prefix", "classpath:/templates/");
    		String suffix = environment.getProperty("spring.thymeleaf.suffix", ".html");
    		return resourceLoader.getResource(prefix + view + suffix).exists();
    	} else {
    		return false;
    	}
    }
    

          Custom Favicon 自定义标签小图标

             SpringBoot会在静态资源路径和根路径下寻找favicon.ico,参考:WebMvcAutoConfiguration.FaviconConfiguration

          Path Matching and Content Negotiation 路径匹配和内容协商

             Springboot默认的不开启后缀方式(GET /projects/spring-boot.json" 不能匹配 @GetMapping("/projects/spring-boot"))

                除了后缀方式还可以不用发送带“Accept”请求头信息的方式,参数方式(GET /projects/spring-boot?format=json)   

    spring.mvc.contentnegotiation.favor-parameter=true
    spring.mvc.contentnegotiation.parameter-name=myparam
    spring.mvc.contentnegotiation.media-types.markdown=text/markdown
    

             仍然想使用后缀方式

    spring.mvc.contentnegotiation.favor-path-extension=true
    spring.mvc.pathmatch.use-suffix-pattern=true
    

             与其开放所有后缀方式,不如只支持注册了的

    spring.mvc.contentnegotiation.favor-path-extension=true
    spring.mvc.pathmatch.use-registered-suffix-pattern=true
    

          ConfigurableWebBindingInitializer  

             SpringBoot使用WebBindingInitializer初始化WebDataBinder应对特殊的请求,覆盖方式

    // 继承ConfigurableWebBindingInitializer并将组件加入容器中,参考WebMvcAutoConfiguration和WebMvcConfigurationSupport
    protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
    	try {
    		return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
    	} catch (NoSuchBeanDefinitionException var2) {
    		return super.getConfigurableWebBindingInitializer();
    	}
    }
    

          Error Handling

            默认的SpringBoot提供了 /error 的映射来处理所有的错误请求

            参考ErrorMvcAutoConfiguration,注入了DefaultErrorAttributes(包含错误信息,可以覆盖以添加更丰富的内容)、BasicErrorController(映射 ${server.error.path:${error.path:/error}} 路径的Controller)、ErrorPageCustomizer(错误跳转地址)、DefaultErrorViewResolver(错误页面解析器)

            当发生错误时,发起 ‘/error’请求,先通过错误页面解析获得视图,没获得则返回 error 视图

    @RequestMapping(
    	produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    	HttpStatus status = this.getStatus(request);
    	Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
    	response.setStatus(status.value());
    	ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
    	return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }
    

            可以使用@ControllerAdvice和@ExceptionHandler将错误请求转发至 ‘/error’

    package com.wjz.error;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.HashMap;
    import java.util.Map;
    
    @ControllerAdvice
    public class ErrorControllerAdvice {
    
        @ExceptionHandler(ErrorException.class)
        public String handleException(Exception e, HttpServletRequest req) {
            Map<String, Object> messages = new HashMap<>();
            messages.put("code", "000");
            messages.put("message", e.getMessage());
            req.setAttribute("messages", messages);
            req.setAttribute("javax.servlet.error.status_code", HttpStatus.INTERNAL_SERVER_ERROR.value());
            return "forward:/error";
        }
    }
    

           重写默认的ErrorAttributes添加额外的信息

    package com.wjz.error;
    
    import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.ServletWebRequest;
    import org.springframework.web.context.request.WebRequest;
    
    import java.util.Map;
    
    import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
    
    @Component
    public class AdditionalErrorAttributes extends DefaultErrorAttributes {
    
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
            Map<String, Object> attributes = super.getErrorAttributes(webRequest, includeStackTrace);
            attributes.put("messages", ((ServletWebRequest) webRequest).getAttribute("messages", SCOPE_REQUEST));
            return attributes;
        }
    }

          Custom Error Pages

            可以以静态文件方式放在静态资源路径下的 /error 目录下(404.html,500.html)

            可以以模板文件方式放在 classpath:templates路劲下的 /error 目录下

            4xx.html可以匹配所有客户端错误,5xx.html可以匹配所有服务端错误

            还可以自定义错误页面解析器,参考AbstractErrorController.resolveErrorView()方法、DefaultErrorViewResolver

          CORS Support 跨域请求

            单一请求跨域支持,Controller层方法上添加@CrossOrigin注解

            全局请求跨域支持

    @Configuration
    public class CorsConfiguration {
        public WebMvcConfigurer corsFilter() {
            return new WebMvcConfigurer() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                            .allowedOrigins("*")
                            .allowedMethods("*")
                            .allowedHeaders("*");
                }
            };
        }
    }
    

        JAX-RS and Jersey RESTFul方式和集成Jersey框架

            参考:https://www.jianshu.com/p/c14a9028e6e7

        Embedded Servlet Container Support 嵌入式Servlet容器支持

            Servlets, Filters, and listeners

               默认的SpringBoot会提供一些Filter,如字符集Filter(OrderedCharacterEncodingFilter,参考HttpEncodingAutoConfiguration)

               @ServletComponentScan扫描@WebServlet、@WebFilter、@WebListener注释的类

               ServletRegistrationBean、FilterRegistrationBean和ServletListenerRegistrationBean形式注入

    @Configuration
    @ServletComponentScan({"com.wjz.filter", "com.wjz.servlet", "com.wjz.listener"})
    public class FilterConfiguration {
        @Bean
        public FilterRegistrationBean filter_1() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            registrationBean.setFilter(new Filter_1());
            registrationBean.addInitParameter("target", "true");
            registrationBean.addUrlPatterns("/*");
            return registrationBean;
        }
    }
    

                 注解形式注入

    @WebFilter(filterName = "filter_2", initParams = {@WebInitParam(name = "target", value = "true")}, urlPatterns = "/filter_2/*")
    public class Filter_2 extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
    

            Servlet Context Initialization 

              web容器启动时,为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能,SpringBoot为避免风险提供了ServletContextInitializer,其onStartup方法提供了javax.servlet.ServletContext,可以利用后者添加servlet、filter

              具体可参考ServletRegistrationBean、FilterRegistrationBean和ServletListenerRegistrationBean

     

             Customizing Embedded Servlet Containers

              可以通过application.properties配置文件自定义嵌入式Servlet容器,如端口(server.port)、项目缺省路径(server.servlet.context-path)等,具体可参考ServerProperties

    Ⅷ. Working With SQL Databases     

        SpringBoot整合Mybatis

            参考:https://blog.csdn.net/qq_35206261/article/details/81778224

            自定义数据源

              @ConfigurationProperties可以使用在被@Bean修饰的public方法上,利用ConfigurationPropertiesBindingPostProcessor后置处理为实例设置属性值

    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource dataSource() {
    	return new HikariDataSource();
    }
    

              配置文件内容

    spring.datasource.username=root
    spring.datasource.password=root
    

              其他组件注入DataSource时会为其设置用户名、密码信息,因此指定DataSource类型时,需要提前指定

    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource dataSource(DataSourceProperties properties) {
    	return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

        SpringBoot内置数据库

            参考https://blog.csdn.net/zyhlwzy/article/details/78733644

    Ⅸ. Working with NoSQL Technologies 

          SpringBoot提供两种连接API,Lettuce和Jedis,推荐使用前者,参考RedisAutoConfiguration

    spring.redis.database=7
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.lettuce.pool.max-idle=8
    spring.redis.lettuce.pool.min-idle=0
    spring.redis.lettuce.pool.max-active=8
    

     

  • 相关阅读:
    传智博客.NET培训第13季 Ajax教程(共十三季) 学习资源
    一些sql语句的常用总结(重要)
    处理oracle的死锁
    Adroid 总结--android ListView美化,个性化更改的属性
    如何远程备份sql server数据库
    VSS (Visual Source Safe 2005) 用法详解
    php插入代码数据库
    PHP之PHP文件引用详解
    需要引入库:vue-resource
    axios调用详解
  • 原文地址:https://www.cnblogs.com/BINGJJFLY/p/11523528.html
Copyright © 2020-2023  润新知