@SpringBootConfiguration:spring boot的配置类;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration {@Configuration:使用它来标注配置类;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {使用了@Component来注解,说明配置类也是容器中的一个组件;
@EnableAutoConfiguration:开启自动配置功能;
@Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {@AutoConfigurationPackage:自动配置包;
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {@Import(AutoConfigurationPackages.Registrar.class):
@Import是spring的底层注解,给容器导入组件,导入的组件由AutoConfigurationPackages.Registrar.class来决定;
实际上是将主配置类(@SpringBootApplication标注的类)所在的包及其所有子包中的Bean组件扫描到Spring容器中;
@Import(AutoConfigurationImportSelector.class)
给spring容器导入需要的自动配置类(XXXAutoConfiguaration);就是给容器导入这个场景所需的组件,并配置好这些组件;springBoot启动的时候从spring-boot-autoconfigure.jar路径下的META-INF/spring.factories中获取org.springframework.boot.autoconfigure.EnableAutoConfiguration指定的值,将这些自动配置类导入到容器中,自动配置类就生效了,帮我们进行自动配置;J2EE的整体配置解决方案和自动配置都在spring-boot-autoconfigure.jar中;resources文件夹中的文件目录:
static:保存所有的静态文件:js css images;
templates:所有的模板页面;(默认不支持jsp页面)使用模板引擎(freemarker,thymeleaf);
application.properties:配置文件;
application.yml:配置文件;
#对象 User: name: 张三 age: 18 #行内写法 User: {name: 张三,age: 18} #数组 pets: - cat - dog - pig #行内写法 pets: [cat,dog,pig]使用@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数据校验支持不支持复杂类型封装支持不支持 只在业务逻辑中使用配置文件中的某项值,使用@Value;使用配置文件来对Bean进行属性配置就使用@ConfigurationProperties;配置文件注入值的数据校验
@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;@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;@ImportSource()加载xml配置文件,加载Bean;
导入xml的Bean配置文件;Springboot不推荐使用@ImportSource();一般使用@Configuration注解的配置类来实现,使用@Bean来注解方法; 使用@Bean注入的bean的name默认是方法名;配置文件占位符
配置文件中使用随机数;
引用前面配置的属性;
#使用随机数 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.comprofile的使用
在.properties文件中的使用;
建立多个.properties文件,文件名使用application-{name}.properties;在主配置文件中使用 spring.profiles.active=name 来激活其中一个配置文件;在yml文件中使用;
使用文档块;
spring: profiles: active: dev --- server: port: 8082 spring: profiles: dev --- server: port: 8083 spring: profiles: prod #指定属于哪个环境使用命令行来激活:–spring.profiles.active=prod来覆盖配置;
使用虚拟机参数:-Dspring.profiles.active=prod来覆盖配置;
配置文件的加载位置的优先级
-file:config/
-file:/
classpath:config/
classpath:
优先级从高到低,高的会覆盖低的配置文件,形成互补配置;
-jar运行jar包时,可以通过 spring.config.location= 来指定配置文件形成互补配置;
外部配置的加载顺序
命令行参数 java -jar spring- boot-02-config-02-0.0.1-SNAPSHOT.jar -server.port-8087
来自java:comp/env的JNDI属性
Java系统属性( System.getProperties() )
操作系统环境变量
RandomValuePropertySource配置的random.*属性值
重要的四种:
jar包外部的application-{profile}.properties或application.yml(带spring,profile)配置文件
jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
jar包外部的application.properties或application.ym(不带spring.profile)配置文件
jar包内部的application.properties或application.yml(不带spring.profile)配置文件
@Configuration注解类上的@PropertySource
通过SpringApplication.setDefaultProperties指定的默认属性
Springboot启动的时候,开启了自动配置功能@EnableAutoConfiguration
@EnableAutoConfiguration的作用:
@Import(AutoConfigurationImportSelector.class)AutoConfigurationImportSelector类中;
通过selectImports方法来导入所需的自动配置类;
getAutoConfigurationEntry -> getCandidateConfigurations -> SpringFactoriesLoader.loadFactoryNames -> loadSpringFactories ;
最终从所有jar包的类路径下 “META-INF/spring.factories” 把扫描到的文件内容包装成properties对象;
从properties对象中获取 EnableAutoConfiguration.class 的类名作为Key的Value;
就是把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); } }每一个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 { 根据不同的条件判断这个配置类是否生效;一旦生效;就会把这个配置类中@Bean注册到spring容器中;而这些Bean的属性从对应的properties类中获取;这些properties类的每一个属性又和配置文件@ConfigurationProperties(prefix = “server”)绑定到一起;这些配置文件的值可以通过我们自己的配置文件来覆盖配置,形成互补配置;一般使用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(); }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()方法,只不过这种情况下会将异常信息一并传入处理)
自动配置原理:
xxxAutoConfiguration:给容器中自动配置组件 @EnableConfigurationProperties({WebMvcProperties.class})//注入配置类 xxxproperties类来封装配置文件的内容所有的 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 下去找资源;
webjars:以jar包的方法引入静态资源;
<!--在访问的时候只需要引入webjars下面的资源名称就行了--> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>4.5.2</version> </dependency>"/**"访问项目的任何资源;从下面文件夹中找;
"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 下有说明欢迎页;静态资源文件夹下的所有index.html页面;被"/**"映射;
配置静态资源文件夹覆盖自动配置;
spring.resources.static-locations=classpath:templatesThymeleaf的使用和语法;
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"; //默认的前缀和后缀导入thymeleaf的命名空间;
<html lang="en" xmlns:th="http://www.thymeleaf.org"/>表达式;
简单表达式: 变量表达式: ${...}选择变量表达式: *{...}消息表达: #{...}链接URL表达式: @{...}片段表达式: ~{...} 文字 文本文字:'one text','Another one!',…号码文字:0,34,3.0,12.3,…布尔文字:true,false空文字: null文字标记:one,sometext,main,… 文字操作: 字符串串联: +文字替换: |The name is ${name}| 算术运算: 二元运算符:+,-,*,/,%减号(一元运算符): - 布尔运算: 二元运算符:and,or布尔否定(一元运算符): !,not 比较和平等: 比较:>,<,>=,<=(gt,lt,ge,le)等号运算符:==,!=(eq,ne) 条件运算符: 如果-则: (if) ? (then)如果-则-否则: (if) ? (then) : (else)默认: (value) ?: (defaultvalue) 特殊令牌: 无操作: _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中;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.
通过实现WebMvcConfigurer接口来扩展配置;
@Configuration public class WebConfig implements WebMvcConfigurer { @Override//添加了一个视图控制器,把"/hello", public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/hello").setViewName("success"); } }在WebMvcAutoConfiguration中会有以下这个类;
@Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class)//实现的关键 @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer { //它也是通过实现WebMvcConfigurer来向springboot中添加组件的;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); } }这样容器中的所有WebMvcConfigurer都会一起作用;自定义的配置类也会被调用;
在配置类上添加@EnableWebMvc就可以了;
原理:
@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 {导入的DelegatingWebMvcConfiguration.class会实现springmvc的最基本功能;
在springboot中会有非常多XXXConfigurer帮助我们进行扩展配置;
在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置;
编写国际化配置文件;[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)]
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; }效果:根据浏览器语言显示界面语言;
//国际化对象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; }点击链接实现国际化;
//通过自己定义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容器中禁用模板引擎
spring.thymeleaf.cache=false使用拦截器进行过滤检查
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"); }发送POST求后,带_method参数,将其修改为PUT,或者DELETE请求
//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
在启动类中调用了SpringApplication的静态run方法;
创建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; }