springboot-shang

SpringBoot

一、spring boot的入门

1、主程序类

Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@SpringBootConfiguration:spring boot的配置类;

  1. @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    
  2. @Configuration:使用它来标注配置类;

    1. @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Component
      public @interface Configuration {
      
    2. 使用了@Component来注解,说明配置类也是容器中的一个组件;

@EnableAutoConfiguration:开启自动配置功能;

  1. @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
    1. @AutoConfigurationPackage:自动配置包;

    2. @Import(AutoConfigurationPackages.Registrar.class)
      public @interface AutoConfigurationPackage {
      
    3. @Import(AutoConfigurationPackages.Registrar.class):

      1. @Import是spring的底层注解,给容器导入组件,导入的组件由AutoConfigurationPackages.Registrar.class来决定;

      2. 实际上是将主配置类(@SpringBootApplication标注的类)所在的包及其所有子包中的Bean组件扫描到Spring容器中;

    4. @Import(AutoConfigurationImportSelector.class)

      1. 给spring容器导入需要的自动配置类(XXXAutoConfiguaration);就是给容器导入这个场景所需的组件,并配置好这些组件;
      2. springBoot启动的时候从spring-boot-autoconfigure.jar路径下的META-INF/spring.factories中获取org.springframework.boot.autoconfigure.EnableAutoConfiguration指定的值,将这些自动配置类导入到容器中,自动配置类就生效了,帮我们进行自动配置;
      3. J2EE的整体配置解决方案和自动配置都在spring-boot-autoconfigure.jar中;

2.项目的目录

  1. resources文件夹中的文件目录:

    1. static:保存所有的静态文件:js css images;

    2. templates:所有的模板页面;(默认不支持jsp页面)使用模板引擎(freemarker,thymeleaf);

    3. application.properties:配置文件;

    4. application.yml:配置文件;

    5. #对象
      User:
      	name: 张三
      	age: 18
      #行内写法
      User: {name: 张三,age: 18}
      #数组
      pets:
      	- cat
      	- dog
      	- pig
      #行内写法
      pets: [cat,dog,pig]
      
  2. 使用@ConfigurationProperties(prefix = “user”)来从配置文件中配置对象属性;

    @Component
    @ConfigurationProperties(prefix = "user")
    public class User {
        private String name;
        private String gender;
        private int age;
        private List<Integer> list;
    
    user:
      name: 张胜男
      age: 18
      gender:list:
        - 1
        - 2
        - 3
    

    需要导入依赖;

    <!--        配置文件处理器-->
     <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-configuration-processor</artifactId>
           <optional>true</optional>
     </dependency>
    

    @Value和@ConfigurationProperties获取值的区别

    @ConfigurationProperties@Value
    功能批量注入配置文件中的属性一个个注入
    松散绑定支持不支持
    SpEL(Spring表达式)不支持支持
    JSR303数据校验支持不支持
    复杂类型封装支持不支持
    1. 只在业务逻辑中使用配置文件中的某项值,使用@Value;
    2. 使用配置文件来对Bean进行属性配置就使用@ConfigurationProperties;
  3. 配置文件注入值的数据校验

    @Component
    @ConfigurationProperties(prefix = "employee")
    @Validated//数据校验
    public class Employee {
        @Email
    //    @Value("${employee.name}")
        private String name;
        private String gender;
        private int age;
    //    @Value("${employee.list}")//不能使用@Value来注入List、Array、Map等
        private List<Integer> list;
    
  4. @PropertySource()加载指定的配置文件;

    @Component
    @PropertySource(value = {"classpath:employee.properties"})//导入指定的.properties文件
    @ConfigurationProperties(prefix = "employee")
    public class Employee {
        private String name;
        private String gender;
        private int age;
        private List<Integer> list;
    
  5. @ImportSource()加载xml配置文件,加载Bean;

    1. 导入xml的Bean配置文件;
    2. Springboot不推荐使用@ImportSource();
    3. 一般使用@Configuration注解的配置类来实现,使用@Bean来注解方法;
      1. 使用@Bean注入的bean的name默认是方法名;
  6. 配置文件占位符

    1. 配置文件中使用随机数;

    2. 引用前面配置的属性;

      #使用随机数
      user1:
        name: 张胜男_${random.int(12,100)}
        age: ${random.int}
        gender: ${random.value}
      
      employee:
        age: ${user1.age1:18} #使用占位符和默认值
        gender: ${random.long}
        list:
          - 1
          - 2
          - 3
        name: ${user1.name}_1139656340@qq.com
      
  7. profile的使用

    1. 在.properties文件中的使用;

      1. 建立多个.properties文件,文件名使用application-{name}.properties;
      2. 在主配置文件中使用 spring.profiles.active=name 来激活其中一个配置文件;
    2. 在yml文件中使用;

      1. 使用文档块;

        spring:
          profiles:
            active: dev
        ---
        server:
          port: 8082
        spring:
          profiles: dev
        ---
        server:
          port: 8083
        spring:
          profiles: prod  #指定属于哪个环境
        
      2. 使用命令行来激活:–spring.profiles.active=prod来覆盖配置;

      3. 使用虚拟机参数:-Dspring.profiles.active=prod来覆盖配置;

  8. 配置文件的加载位置的优先级

    1. -file:config/

    2. -file:/

    3. classpath:config/

    4. classpath:

      优先级从高到低,高的会覆盖低的配置文件,形成互补配置;

    5. -jar运行jar包时,可以通过 spring.config.location= 来指定配置文件形成互补配置;

  9. 外部配置的加载顺序

    1. 命令行参数
      java -jar spring- boot-02-config-02-0.0.1-SNAPSHOT.jar -server.port-8087

    2. 来自java:comp/env的JNDI属性

    3. Java系统属性( System.getProperties() )

    4. 操作系统环境变量

    5. RandomValuePropertySource配置的random.*属性值

      重要的四种:

    6. jar包外部的application-{profile}.properties或application.yml(带spring,profile)配置文件

    7. jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

    8. jar包外部的application.properties或application.ym(不带spring.profile)配置文件

    9. jar包内部的application.properties或application.yml(不带spring.profile)配置文件

    10. @Configuration注解类上的@PropertySource

    11. 通过SpringApplication.setDefaultProperties指定的默认属性

二、自动配置原理

1.自动配置原理

  1. Springboot启动的时候,开启了自动配置功能@EnableAutoConfiguration

  2. @EnableAutoConfiguration的作用:

    1. @Import(AutoConfigurationImportSelector.class)
      
    2. AutoConfigurationImportSelector类中;

    3. 通过selectImports方法来导入所需的自动配置类;

    4. getAutoConfigurationEntry -> getCandidateConfigurations -> SpringFactoriesLoader.loadFactoryNames -> loadSpringFactories ;

      1. 最终从所有jar包的类路径下 “META-INF/spring.factories” 把扫描到的文件内容包装成properties对象;

      2. 从properties对象中获取 EnableAutoConfiguration.class 的类名作为Key的Value;

      3. 就是把spring.factories中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的value通过@Import中实现接口ImportSelector的类来加载到容器中;

      @Override
      public String[] selectImports(AnnotationMetadata annotationMetadata) {
         if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
         }
         AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
         return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
      }
      
      //再看getAutoConfigurationEntry方法
      protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
      		if (!isEnabled(annotationMetadata)) {
      			return EMPTY_ENTRY;
      		}
      		AnnotationAttributes attributes = getAttributes(annotationMetadata);
      		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//获取候选的配置
      		configurations = removeDuplicates(configurations);
      		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
      		checkExcludedClasses(configurations, exclusions);
      		configurations.removeAll(exclusions);
      		configurations = getConfigurationClassFilter().filter(configurations);
      		fireAutoConfigurationImportEvents(configurations, exclusions);
      		return new AutoConfigurationEntry(configurations, exclusions);
      	}
      
      protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
      				getBeanClassLoader());//
      		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
      				+ "are using a custom packaging, make sure that file is correct.");
      		return configurations;
      	}
      
      public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
      		String factoryTypeName = factoryType.getName();
      		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
      	}
      
      private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
      		MultiValueMap<String, String> result = cache.get(classLoader);
      		if (result != null) {
      			return result;
      		}
      
      		try {
                  //"META-INF/spring.factories"
      			Enumeration<URL> urls = (classLoader != null ?
      		  classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
      					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      			result = new LinkedMultiValueMap<>();
      			while (urls.hasMoreElements()) {
      				URL url = urls.nextElement();
      				UrlResource resource = new UrlResource(url);
      				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
      				for (Map.Entry<?, ?> entry : properties.entrySet()) {
      					String factoryTypeName = ((String) entry.getKey()).trim();
      					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
      						result.add(factoryTypeName, factoryImplementationName.trim());
      					}
      				}
      			}
      			cache.put(classLoader, result);
      			return result;
      		}
      		catch (IOException ex) {
      			throw new IllegalArgumentException("Unable to load factories from location [" +
      					FACTORIES_RESOURCE_LOCATION + "]", ex);
      		}
      	}
      
  3. 每一个xxxAutoConfiguration类都有@Configuration注解,通过它向spring容器中注册Bean;以一个HttpEncodingAutoConfiguration来说明;

    //lite模式,这是一个配置类
    @Configuration(proxyBeanMethods = false)
    //启动指定类的ConfigurationProperties功能,将ServerProperties加载到spring容器中
    @EnableConfigurationProperties(ServerProperties.class)
    //通过条件判断是否加载这个配置类
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
    public class HttpEncodingAutoConfiguration {
        //通过properties来给xxxAutoConfiguration中的属性赋值,它已经和配置文件形成了映射
    	private final Encoding properties;
        //只有一个有参构造器时,参数的值会自动从Spring容器中获取
    	public HttpEncodingAutoConfiguration(ServerProperties properties) {
    		this.properties = properties.getServlet().getEncoding();
    	}
    
    //XXXProperties类中的属性通过配置文件来配置,形成绑定
    @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
    public class ServerProperties {
    
    1. 根据不同的条件判断这个配置类是否生效;
    2. 一旦生效;就会把这个配置类中@Bean注册到spring容器中;而这些Bean的属性从对应的properties类中获取;这些properties类的每一个属性又和配置文件@ConfigurationProperties(prefix = “server”)绑定到一起;这些配置文件的值可以通过我们自己的配置文件来覆盖配置,形成互补配置;

2.run方法的启动过程

  1. 一般使用SpringApplication的静态run方法;这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

    • 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

    • 推断并设置main方法的定义类。

    • public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
         this.resourceLoader = resourceLoader;
         Assert.notNull(primarySources, "PrimarySources must not be null");
         this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
          //根据classpath中存在的某些特征类,来决定返回的ApplicationContext的类型
         this.webApplicationType = WebApplicationType.deduceFromClasspath();
         //使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer
         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
          //使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          //推断并设置main方法的定义类
         this.mainApplicationClass = deduceMainApplicationClass();
      }
      
  2. SpringApplication的run方法;

    public ConfigurableApplicationContext run(String... args) {
       StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       ConfigurableApplicationContext context = null;
       Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
       configureHeadlessProperty();
       //首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”
       SpringApplicationRunListeners listeners = getRunListeners(args);
       listeners.starting();
       try {
          ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          //创建并配置当前Spring Boot应用将要使用的Environment;
          //遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”
          ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
          configureIgnoreBeanInfo(environment);
          //如果SpringApplication的showBanner属性被设置为true,则打印banner。
          Banner printedBanner = printBanner(environment);
          //根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并       创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,       将之前准备好的Environment设置给创建好的ApplicationContext使用。
          context = createApplicationContext();
          exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
          prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          refreshContext(context);
          afterRefresh(context, applicationArguments);
          stopWatch.stop();
          if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
          }
          listeners.started(context);
          callRunners(context, applicationArguments);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, listeners);
          throw new IllegalStateException(ex);
       }
    
       try {
          listeners.running(context);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, null);
          throw new IllegalStateException(ex);
       }
       return context;
    }
    

7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。

12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)

三、日志

1.日志的简绍

  1. 日志的抽象层:日志的门面:统一的接口层;
  2. 日志框架一般实现的日志的抽象层;filter,adapter
  3. 选择一个日志门面+一个具体实现;
  4. 实现不同日志门面的具体日志框架可以通过一个适配层来连接统一的日志门面;
  5. slf4j(simple logging facade for java) logback

2.统一使用slf4j

  1. 将系统中其它日志框架先排除;
  2. 用中间包来替换原有的日志框架;
  3. 在导入slf4j的其它实现;

3.slf4j的使用

  1. 开发时,日志方法的调用,应该直接使用日志抽象层中的方法;不直接使用具体实现框架中的方法;
  2. 给系统中导入统一的接口层:slf4j 和具体实现:logback;

4.Springboot日志

  1. springboot的底层也是使用slf4j+logback的方式进行日志的记录;
  2. springboot使用中间包把其它日志替换成slf4j;
  3. 如果引入其它框架,要把它的日志替换成slf4j;

四、Web开发

自动配置原理:

xxxAutoConfiguration:给容器中自动配置组件
@EnableConfigurationProperties({WebMvcProperties.class})//注入配置类
xxxproperties类来封装配置文件的内容

1.资源文件的位置;

public class WebMvcAutoConfiguration {
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));
   }
}
  1. 所有的 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 下去找资源;

  2. webjars:以jar包的方法引入静态资源;

    <!--在访问的时候只需要引入webjars下面的资源名称就行了-->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>4.5.2</version>
    </dependency>
    

2.Springboot对静态资源的映射规则

//设置与静态资源相关的参数,比喻缓存时间
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
  1. "/**"访问项目的任何资源;从下面文件夹中找;

    "classpath:/META-INF/resources/",
    "classpath:/resources/", 
    "classpath:/static/", 
    "classpath:/public/" ,
    "/":servlet-context:"/"的路径 //https://www.cnblogs.com/hujunzheng/p/9682960.html 
                                 //https://www.cnblogs.com/cq0143/p/10668331.html  下有说明
    
  2. 欢迎页;静态资源文件夹下的所有index.html页面;被"/**"映射;

  3. 配置静态资源文件夹覆盖自动配置;

    spring.resources.static-locations=classpath:templates
    

3.模板引擎

  1. Thymeleaf的使用和语法;

    1. 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";
          //默认的前缀和后缀
      
    2. 导入thymeleaf的命名空间;

      <html lang="en" xmlns:th="http://www.thymeleaf.org"/>
      

4.thymeleaf语法

  1. OrderFeatureAttributes
    1Fragment inclusionth:insert th:replace片断包含:jsp:include
    2Fragment iterationth:each遍历
    3Conditional evaluationth:if th:unless th:switch th:case判断
    4Local variable definitionth:object th:with声明变量
    5General attribute modificationth:attr th:attrprepend th:attrappend任意属性修改
    6Specific attribute modificationth:value th:href th:src ...修改指定属性默认值
    7Text (tag body modification)th:text th:utext修改标签体内容
    8Fragment specificationth:fragment声明片断
    9Fragment removalth:remove
  2. 表达式;

    • 简单表达式:
      • 变量表达式: ${...}
      • 选择变量表达式: *{...}
      • 消息表达: #{...}
      • 链接URL表达式: @{...}
      • 片段表达式: ~{...}
    • 文字
      • 文本文字:'one text''Another one!',…
      • 号码文字:0343.012.3,…
      • 布尔文字:truefalse
      • 空文字: null
      • 文字标记:onesometextmain,…
    • 文字操作:
      • 字符串串联: +
      • 文字替换: |The name is ${name}|
    • 算术运算:
      • 二元运算符:+-*/%
      • 减号(一元运算符): -
    • 布尔运算:
      • 二元运算符:andor
      • 布尔否定(一元运算符): !not
    • 比较和平等:
      • 比较:><>=<=gtltgele
      • 等号运算符:==!=eqne
    • 条件运算符:
      • 如果-则: (if) ? (then)
      • 如果-则-否则: (if) ? (then) : (else)
      • 默认: (value) ?: (defaultvalue)
    • 特殊令牌:
      • 无操作: _

5.spring mvc自动配置

Spring MVC Auto-configuration

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器:根据返回值得到视图对象(view),视图对象决定如何渲染(转发?重定向,,,))
    • ContentNegotiatingViewResolver组合所有的视图解析器;
    • 如何定制:我们可以自己给容器中添加一个视图解析器,ContentNegotiatingViewResolver 会自动将其组合起来;
  • Support for serving static resources, including support for WebJars (covered later in this document)).

  • Static index.html support. //静态首页访问的;

  • Custom Favicon support (covered later in this document). //favicon.ico 图标;

  • Automatic registration of Converter, GenericConverter, and Formatter beans.

    • Converter:转换器;类型转换;比如把数据转换成java的对象;
    • Formatter:格式化器:把日期转换成Date
    • 自己添加的转换器和格式化器,只需放到容器中就可以了;
  • Support for HttpMessageConverters (covered later in this document).

    • HttpMessageConverter:SpringMVC用来转换Http请求和响应的;比如,接受User对象,把它转换成json输出;
    • HttpMessageConverters 是从容器中获取所有的HttpMessageConverter
    • 自定义 HttpMessageConverter,只需将自己的组件注册到容器中;
  • Automatic registration of MessageCodesResolver (covered later in this document).

    • 定义错误代码生成规则;
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

    • 初始化WebDataBinder;
      请求数据绑定到JavaBean中;
      

6.修改springboot的默认配置

  1. springboot在自动配置很多组件时,会通过@Conditional判断容器中是否有已经配置好的(@Bean,@Component),如果没有才自动配置;
  2. 有的组件可以有多个(ViewResolver);则将用户添加的和springboot 默认的组合起来;

7.扩展Springmvc

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

  1. 通过实现WebMvcConfigurer接口来扩展配置;

  2. @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override//添加了一个视图控制器,把"/hello",
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/hello").setViewName("success");
        }
    }
    
  3. 在WebMvcAutoConfiguration中会有以下这个类;

    @Configuration(proxyBeanMethods = false)
    @Import(EnableWebMvcConfiguration.class)//实现的关键
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    //它也是通过实现WebMvcConfigurer来向springboot中添加组件的;
    
  4. WebMvcConfigurer则通过EnableWebMvcConfiguration.class来实现向spring容器中添加组件;

    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        
    @Configuration(proxyBeanMethods = false)
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
       private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
       @Autowired(required = false)//从容器中获取所有WebMvcConfigurer
       public void setConfigurers(List<WebMvcConfigurer> configurers) {
          if (!CollectionUtils.isEmpty(configurers)) {
             this.configurers.addWebMvcConfigurers(configurers);
          }
       }
       //以添加视图控制器来举例
       @Override
       protected void addViewControllers(ViewControllerRegistry registry) {
    	  this.configurers.addViewControllers(registry);//调用这个方法
       }
       @Override//把每个WebMvcConfigurer的方法都调用一遍
       public void addViewControllers(ViewControllerRegistry registry) {
    	   for (WebMvcConfigurer delegate : this.delegates) {
    		    delegate.addViewControllers(registry);
    	   }
       }
    
  5. 这样容器中的所有WebMvcConfigurer都会一起作用;自定义的配置类也会被调用;

8.全面接管Springmvc

在配置类上添加@EnableWebMvc就可以了;

  1. 原理:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)//导入了这个类
    public @interface EnableWebMvc {
    }
    
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    	@Autowired(required = false)
    	public void setConfigurers(List<WebMvcConfigurer> configurers) {
    		if (!CollectionUtils.isEmpty(configurers)) {
    			this.configurers.addWebMvcConfigurers(configurers);
    		}
    	}
    //在WebMvcAutoConfiguration中有以下注解,其中 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)会使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 {
    
  2. 导入的DelegatingWebMvcConfiguration.class会实现springmvc的最基本功能;

9.修改springboot的自动配置

  1. 在springboot中会有非常多XXXConfigurer帮助我们进行扩展配置;

  2. 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置;

五、RestfulCRUD

1.首页的设置

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        WebMvcConfigurer configurer = new WebMvcConfigurer(){
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/first").setViewName("index");
            }
        };
        return configurer;
    }
}

2.国际化

  1. 编写国际化配置文件;[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uTTISEcP-1602861135206)(file://C:/Users/110/Downloads/Spring%20Boot%20%E7%AC%94%E8%AE%B0+%E8%AF%BE%E4%BB%B6/images/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20180211130721.png?lastModify=1601990494)]

  2. SpringBoot自动配置好了管理国际化资源的组件;

    public class MessageSourceAutoConfiguration {
       private static final Resource[] NO_RESOURCES = {};
       @Bean
       @ConfigurationProperties(prefix = "spring.messages")
       public MessageSourceProperties messageSourceProperties() {
          return new MessageSourceProperties();
       }
       //在MessageSourceProperties中private String basename = "messages",它从类路径中寻找,一般要进行修改
       @Bean
       public MessageSource messageSource(MessageSourceProperties properties) {
          ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
          if (StringUtils.hasText(properties.getBasename())) {
             //设置国际化资源文件的基础名(除去语言国家后的)
              messageSource.setBasenames(StringUtils        .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
          }
          if (properties.getEncoding() != null) {
             messageSource.setDefaultEncoding(properties.getEncoding().name());
          }
          messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
          Duration cacheDuration = properties.getCacheDuration();
          if (cacheDuration != null) {
             messageSource.setCacheMillis(cacheDuration.toMillis());
          }
          messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
          messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
          return messageSource;
       }
    

    效果:根据浏览器语言显示界面语言;

  3. //国际化对象Local;通过LocalResolver来获取国际化对象Local
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    public LocaleResolver localeResolver() {
       if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
          return new FixedLocaleResolver(this.mvcProperties.getLocale());
       }
       //默认是从请求头中获取区域信息
       AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
       localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
       return localeResolver;
    }
    
  4. 点击链接实现国际化;

    //通过自己定义LocaleResolver,从请求参数中获取语言信息
    public class MyLocaleResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            Locale locale=Locale.getDefault();
            String l = request.getParameter("l");
            if (!StringUtils.isEmpty(l)){
                String[] split = l.split("_");
                locale=new Locale(split[0],split[1]);
            }
            return locale;
        }
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale){
        }
    }
    //最后通过配置类,把上面的LocaleResolver注入到spring容器中
    

3.登录

  1. 禁用模板引擎

    spring.thymeleaf.cache=false 
    
  2. 使用拦截器进行过滤检查

    public class MyLoginHandlerInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            Object user = session.getAttribute("loginUser");//如果session中没有用户信息,就拦截
            if (StringUtils.isEmpty(user)) {
                request.setAttribute("msg", "没有权限,请登录");
                request.getRequestDispatcher("/").forward(request, response);
                return false;
            } else {
                return true;
            }
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        }
    }
    //注册interceptor,在实现了WebMvcConfigurter的类中添加
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
            //不用处理静态资源,springboot已经做好了静态资源映射了;
            registry.addInterceptor(new MyLoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/", "index.html", "/user/login");
        }
    
  3. 发送POST求后,带_method参数,将其修改为PUT,或者DELETE请求

    1. //HiddenHttpMethodFilter这个类会把pust会把请求转变为指定的请求方式
      @Override
      	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {
      		HttpServletRequest requestToUse = request;
      		if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
                  //this.methodParam的值为"_method";
      			String paramValue = request.getParameter(this.methodParam);
      			if (StringUtils.hasLength(paramValue)) {
      				String method = paramValue.toUpperCase(Locale.ENGLISH);
      				if (ALLOWED_METHODS.contains(method)) {
      					requestToUse = new HttpMethodRequestWrapper(request, method);
      				}
      			}
      		}
      		filterChain.doFilter(requestToUse, response);
      	}
      //在WebMvcAutoConfiguration中自动配置了该类
      	@Bean
      	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
      	@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
      //要在.properties中配置spring.mvc.hiddenmethod.filter.enabled=true
      	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
      		return new OrderedHiddenHttpMethodFilter();
      	}
      

七、启动配置原理

启动流程:

几个重要的事件回调机制:

前两个是配置在META-INF/Spring.factories

  • ApplicationContextInitializer

  • SpringApplicationRunListener

    以下两个只需要放在ioc容器中

  • ApplicationRunner

  • CommandLineRuner

  1. 在启动类中调用了SpringApplication的静态run方法;

  2. 创建SpringApplication对象;

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
       this.resourceLoader = resourceLoader;
       Assert.notNull(primarySources, "PrimarySources must not be null");
        //启动类添加到列表中
       this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //判断是NONE(非web应用),SERVLET(基于serlvet的web),REACTIVE(响应式的web);
       this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //从类路径的META-INF/spring.factories中的ApplicationContextInitializer.class,保存起来;
       setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //从类路径的META-INF/spring.factories中的ApplicationListener.class,保存起来;
       setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //从多个配置类中找到有main方法的主配置类
       this.mainApplicationClass = deduceMainApplicationClass();
    }
    

3.run方法;

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();//和AWT相关的;
    //getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    //看到上面这个方法,就证明从类路径的META-INF/spring.factories中的SpringApplicationRunListener.class获取;
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的SpringApplicationRunListener的starting方法
   listeners.starting();
   try {
       //封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       //准备环境,这个方法会回调SpringApplicationRunListener的environmentPrepared方法,表示环境准备完成
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
       //打印图标
      Banner printedBanner = printBanner(environment);
       //创建NONE(非web应用),SERVLET(基于serlvet的web),REACTIVE(响应式的web)(根据环境创建其中一个)ConfigurationApplicationContext容器
      context = createApplicationContext();
       //从类路径的META-INF/spring.factories中的SpringBootExceptionReporter.class获取所有的SpringBootExceptionReporter;
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
       
       //准备上下文;将environment保存到ApplicationContext中,并且里面有个applyInitializers()方法,它回调之前保存在Initializers中的所有ApplicationContextInitializer的initialize(context)方法;
       //还会回调listeners中所有SpringApplicationRunListener的contextPrepared(context)方法;
       //perpareContext运行结束之前调用所有SpringApplicationRunListener的contextLoaded(context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 刷新容器;spring ioc容器的初始化(如果是web应用会加载嵌入式的Tomcat);
      //扫描,加载,创建所有组件的地方
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
       //回调listeners中所有SpringApplicationRunListener的started(context)方法
      listeners.started(context);
       //从ApplicationContext中获取所有的ApplicationRunner和CommandLineRunnrer,分别回调它们的run方法;
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
       //回调listeners中所有SpringApplicationRunListener的running(context)方法
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
    //springBoot应用启动完成以后返回启动的ApplicationContext
   return context;
}
SpringBoot整合RabbitMQ中进行手动签收的方法如下所示: 1. 首先,你可以创建一个消费者类,并在该类上添加`@RabbitListener`注解,指定监听的队列。例如,可以创建一个名为`FanoutReceiverB`的消费者类,使用`@RabbitListener(queues = "fanout.B")`指定监听队列为"fanout.B"。 2. 在消费者类中,可以使用`@RabbitHandler`注解标注一个处理消息的方法,该方法接受一个消息参数。例如,在`FanoutReceiverB`类中,可以定义一个名为`process`的方法,参数为`Map testMessage`,用来处理接收到的消息。 3. 在处理方法中,你可以根据业务逻辑进行相应的处理,并手动确认消息的签收。你可以使用`channel.basicAck`方法来手动确认消息的签收。例如,可以在`process`方法中调用`channel.basicAck`方法来手动确认消息的签收。 4. 最后,你需要在pom.xml文件中添加RabbitMQ的相关依赖。可以添加`spring-boot-starter-amqp`和`spring-boot-starter`依赖。例如,在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> ``` 通过以上步骤,你就可以在SpringBoot中实现手动签收RabbitMQ消息了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [RabbitMQ—SpringBoot中实现手动签收重试三次进入死信队列](https://blog.csdn.net/shang_0122/article/details/120617954)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Springboot 整合RabbitMq ,原来这么简单](https://blog.csdn.net/biglow/article/details/119633573)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值