赞
踩
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@SpringBootApplication //标注这是一个springboot应用类 public class HelloWorldApplication { @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration //标注这是一个springboot的配置类 @EnableAutoConfiguration //标志这是一个自动配置包,允许自动配置 @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration {
@SpringBootApplication
@SpringBootConfiguration #Springboot的配置类;标注在某个类上说明类是springboot的配置类。
@Configuration #配置类上标注这个注解;配置类<----配置文件;配置类也是一个组件 @Component。
@AutoConfigurationPackage //
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@EnableAutoConfiguration #开启自动配置功能;以前我们需要配置的东西,springboot帮我们自动 配置,@EnableAutoConfiguration告诉Springboot开启自动配置功能,这样自动配置才生效。
@AutoConfigurationPackage #自动配置包
@Import({Registrar.class}) #Spring的底层注解@Import,给容器中导入一个组件,导 入的组件是Registrar.class注册;
#@AutoConfigurationPackage将主配置类(即@SpringbootApplication注解标注的类)的所在 包下所有子包下面的组件扫描到springboot容器中
@Import # 给容器中倒入一个组件EnableAutoConfigurationImportSelector.class
这是一个导入哪些组件的选择器,将所有需要导入的组件以全类名的形式返回,这些组件就会 被导入到容器中;
会给容器中注入许多自动配置类(xxxAutoConfiguration),就是给容器中导入场景所需要的 所有组件,并且配置好这些组件;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vbuy3asS-1582269377083)(/Users/shufang/Desktop/WechatIMG11.png)]
有了自动配置类,就免去了我们手动编写配置注入功能组件等工作;
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
//从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
从类路径下的***META-INF/spring.factories***中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们自动配置,以前需要我们自己配置的,自动配置类都帮我们做了,与上图对应。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DViJGBBQ-1582269377085)(/Users/shufang/Desktop/WechatIMG12.png)]
springboot的配置文件有2种:
配置文件的所有默认配置都可以在配置文件中修改指定,然后通过Springboot帮我们配置好
server.port=8081
yml文件比xml、json文件更适合做配置文件,因为yaml是一门以数据为中心的标记语言;
yml与xml的对比如下:
这是xml的配置格式
<server>
<port>8081</port>
</server>
server:
port: 8081
很明显yml比xml要简洁很多;
server:
port: 8081
path: /hello
#在yaml文件中,以 K: v代表KV关系,以\n+TAB代表层级关系,以上的port、path是同一层级的
#“ ”与‘ ’的区别
name: "zhangsan\nlisi" #输出结果为:zhangsan换行lisi
address: 'tianjin\nbeijing' #输出结果为:tianjin\nbeijing
在yml文件中,V的写法有以下几种:
1.字面量(数字、字符串、布尔值)
“ ”与‘ ’的作用不一样,案例如上代码块。
2.对象、Map键值对
friends:
lastname: zhangsan
age: 18
#也可以写成如下格式
friends: {lastname: zhangsan,age: 18}
3.数组、集合(List、Set)
pets:
- cat
- dog
- pig
# 以上是通过- 来表示集合里面的元素,
# 行内写法如下:
pets: [cat,dog,pig]
yml配置文件如下:
person:
id: 1001
name: zhangsan
num: 88
map: {k1: v1,k2: v2}
list:
- hello
- world
- flink
- spark
# list: [hello,world,flink,spark]
Dog:
name: 小狗
hobby: 遛弯
//必须要是springboot中的组件,才能使用配置文件中的值 @Component @ConfigurationProperties(prefix = "person") public class Person { private String id; private String name; private Integer num; private Map<String,Integer> map; private List<String> list; private Dog dog; ..... } //Dog类 public class Dog{ private String name; private String hobby; .... } //最后在test单元测试类下面进行打印测试
解决方法:在idea设置栏中搜索file encoding ,然后勾选下面的勾选框
Transparent native-to-ascii conversion
栏
这2个注解默认从配置文件中读取值,如果配置全写在配置文件中,配置文件就太大了
1.@Values()只支持一个一个绑定,而@ConfigurationProperties是批量绑定
2.@不支持松散语法绑定,而后者支持(lastName、last-name)
@Component
//@ConfigurationProperties(prefix = "person")
public class Person {
@Value("#{11*2}")
private String id;
@Value("${person.name}")
@PropertySource是读取指定的配置文件,并使其生效;
@Component
@ConfigurationProperties(prefix = "person")
@PropertySource(value = {"classpath:person.properties"}) //指定类路径下的person.properties
public class Person {
// @Value("#{11*2}")
private String id;
// @Value("${person.name}")
private String name;
private Integer num;
private Map<String,String> map;
private List<String> list;
private Dog dog;
@ImoprtResource:导入任意Spring的配置文件并使其中的内容生效;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorldService" class="com.shufang.bootstrap.service.HelloWorldService"></bean>
</beans>
@Autowired
ApplicationContext ioc;
@Test
public void isExists(){
System.out.println(ioc.containsBean("helloWorldService"));
}
//这样的话打印的是:false
现在做这样的操作,将@ImportResource加在一个配置类上,如:SpringbootApplication主配置类
@ImportResource(locations = "classpath:beans.xml")
@SpringBootApplication
public class HelloWorldApplication {
1.配置类<=======配置文件;Springboot推荐使用全注解的模式;
这个xml文件是以前的配置文件通过配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorldService" class="com.shufang.bootstrap.service.HelloWorldService"></bean>
</beans>
现在的配置方式:
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件的id默认就是方法名;
//所以这里
@Bean
public HelloWorldService helloWorldService(){
// 这个new HelloWorldService()就是添加到容器中了,方法名helloWorldService就是id
// 然后调用applicationContext.containsBean("${id}")就ok了,应该是返回true
return new HelloWorldService();
}
}
#server.port=8081
#这里的${}是配置文件的占位符,里面可以用来运算或者取值
person.id=1001${random.int}
person.name=zhangsan${random.uuid}
person.num=1000
person.map.k1=v1
person.map.k2=12121
person.list=hello,world,flink,spark
person.dog.name=小狗_of${person.name}
#用:指定默认值make something spaecial
person.dog.hobby=遛弯{person.hobyy:make something spaecial}
命名规则:application-dev.properties\application-prod.properties
通过在properties中指定:spring.profiles.active=dev/prod 来激活其他配置文件
在文件中用---
来作为文档块的分隔符;
#---属于一个文档块的分隔符,此时是dev这个环境配置生效 server: port: 8081 spring: profiles: active: dev --- server: port: 8084 spring: profiles: dev --- server: port: 8085 spring: profiles: prod
1.在application.properties文件中 通过spring.profiles.active=dev
2.在yml中通过以上文档进行激活
3.在Edit Configuration选项通过 Program Arguments中:--spring.profiles.active=prod
4.在VM Options中指定:-Dspringboot.profiles.active=prod
5.通过命令行方式:java -jar xxxxxxx.jar --spring.profiles.active=dev
按照优先级加载排序为下(从高往低):
1.file:/config :这个是project路径下的config
2./config
3.classpath:config/
4.classpath:/
所有路径下的配置文件都会被加载,但是高优先级会覆盖低优先级
server.context-path= 项目访问根路径指定
spring.config.location=指定默认配置文件的路径,指定的配置文件和以上4个位置的配置文件形成互补,共同对项目产生配置作用,常用于项目已经打包部署到服务器上
# 使用方法:java -jar xxxxx.jar --spring.config.location=g:/xxxxx.properties来使配置文件生效
常用的springboot配置优先加载顺序,不同的配置形成互补,相同的使用优先级最高的:
#springboot项目在打jar包的时候,只会将classpath里面的配置文件进行打包,包括classpath:/和/config
1.命令行的形式
2.Java VM Options的形式
3.Program Arguments的形式
#优先加载带带profile的,jar包外的优先级要比jar包外的高
4.jar包外的带application-{profile[dev、prod]}.properties 或 application.yml带{profile}
5.jar包内的带application-{profile[dev、prod]}.properties 或 application.yml带{profile}
#其次加载不带profile的
6.jar包外的不带application-{profile[dev、prod]}.properties 或 application.yml
7.jar包内的不带application-{profile[dev、prod]}.properties 或 application.yml
8.@Configuration标注的配置类的 @PropertySource(location = "")
9.在代码中通过SpringApplication.setDefaultProperties("","")的形式
配置文件能写什么?怎么写?自动配置原理参考:
https://docs.spring.io/spring-boot/docs/1.5.10.BUILD-SNAPSHOT/reference/html/
//1.首先进入主配置类 @SpringBootApplication public class Springboot02WebRestfulcrudApplication { //2.点进@SpringBootApplication @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { //3.这里重要的自动配置注解:@EnableAutoConfiguration 启用自动配置功能 @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { //4.然后通过@Import将AutoConfigurationImportSelector通过反射的形式注入到容器中,点进AutoConfigurationImportSelector类,发现一个选择注入的方法:selectImports public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //得到自动配置节点getAutoConfigurationEntry AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } //5.点进getAutoConfigurationEntry方法一探究竟 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //这个获取候选配置的方法getCandidateConfigurations,返回候选配置configurations List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); //6.点进getCandidateConfigurations方法,我们发现实际上是调用loadFactoryNames,那么点进去 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); //7.我们发现继续调用loadSpringFactories方法,获取List<> SpringFactories,继续点进去 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //....此处省略代码 //这个getResouce是从META-INF/spring.factories目录下获取资源 Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); //8.我们发现,实际上是通过反射的方式获取classpath:META-INF/spring.factories下的指定资源,我们进入 //classpath:META-INF/spring.factories # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ //....more //9.此处,就将EnableAutoConfiguration下的所有类都加载到了容器中,随便点进一个类HttpEncodingAutoConfiguration @Configuration//表明这个类是个配置类,可以将其他组件通过@Bean导入到容器中 @EnableConfigurationProperties(HttpProperties.class)//告诉Springboot允许HttpProperties这个类从配置文件获取属性配置,很明显HttpProperties这个类上面标注了@ConfigurationProperties注解: 【 @ConfigurationProperties(prefix = "spring.http") public class HttpProperties {】 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)//判断是否是web应用,不是的话功能不生效 @ConditionalOnClass(CharacterEncodingFilter.class) //判断是否有CharacterEncodingFilter这个类,没有的话HttpEncoding组件的作用不生效 @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) // 看配置文件中是否有当前属性,没有的话也继续进行匹配并生效 public class HttpEncodingAutoConfiguration { @Bean //将返回的filter对象注入到容器中,filter对象的属性都从properties对象属性中获取,而properties的属性值与配置文件的值通过@ConfigurationProperties进行绑定 @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; }
debug=true 很方便知道哪些自动配置类生效
positive-match
negative-match
1.创建Springboot应用、选择我们需要的模块如:Web、Jdbc、Mabatis、Mysql等
2.Springboot已经默认将这些场景配置好了,只需要在配置文件中指定少量的配置就能运行起来
3.自己编写业务代码
在这里我们很好奇springboot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展等等…
1.xxxxAutoConfiguration 帮我们给容器中自动配置服务组件
2.xxxxProperties 用来封装配置文件的内容的属性类
所有springMVC的相关组件都由WebMvcAutoConfiguration这个配置类自动配置
public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); } else { Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); //所有/webjars/**的都去"classpath:/META-INF/resources/webjars/"中找资源 if (!registry.hasMappingForPattern("/webjars/**")) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl)); } // String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl)); } } } @Bean public SimpleUrlHandlerMapping faviconHandlerMapping() { SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); mapping.setOrder(-2147483647); mapping.setUrlMap( Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler())); return mapping; }
1)以jar包的形式引入静态资源
所有/webjars/**
的都去"classpath:/META-INF/resources/webjars/
"中找资源
参考网址:http://www.webjars.org
通过http://localhost:8080/webjars/jquery/3.3.1/jquery.js访问
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
将常用的/webjars/jquery/3.3.1/jquery.js
2)“/**”,访问当前项目的任何资源(静态资源的文件夹)
springboot会默认从这些目录去获取静态资源
"classpath:/META-INF/resources"
"classpath:/resources"
"classpath:/static"
"classpath:/public"
"/" :当前项目的根目录
类路径:main目录下的所有目录都是类路径的根路径:java、resources
//访问方式:http://localhost:8080/abc ;这样就会在上面的5个默认目录下进行查找
“/**/
映射,也是在静态资源的目录下找http://localhost:8080/ => 会直接去找静态资源下的index.html页面
4)所有的.**/favicon.ico
都是在静态资源目录下映射
#指定默认的静态资源目录
spring.resources.static-locations=classpath:/udfdir/,classpath:/udfdir2/
<properties>
<!--覆盖默认的版本-->
<!--<java.version>1.8</java.version>-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<themleaf-layout-dialect.version>2.1.1</themleaf-layout-dialect.version>
<!--<themleaf.version>3.0.9.RELEASE</themleaf.version>-->
</properties>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf 默认使用2.1.6版本-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
很明显,只需要将HTML页面放在 classpath:/templates/ 下,thymeleaf就能自动渲染;
<!DOCTYPE html>
<!--引入thymeleaf的名称空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>hello一下 你就知道</title>
</head>
<body>
<h1>好运不会眷顾傻瓜!~欢迎访问</h1>
<!--th:text=${hello},这个hello是后端传过来的东西}-->
<div th:text="${hello}">这是前端自己的东西</div>
</body>
</html>
使用:
1.引入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org"
#禁用thymeleaf缓存
spring.thymeleaf.cache=false
#页面修改完以后 ctrl+F9刷新页面到浏览器
2.使用thymeleaf的配置
spring.thymeleaf.cache=true # Whether to enable template caching.一般都是禁用 spring.thymeleaf.check-template=true # Whether to check that the template exists before rendering it. spring.thymeleaf.check-template-location=true # Whether to check that the templates location exists. spring.thymeleaf.enabled=true # Whether to enable Thymeleaf view resolution for Web frameworks. spring.thymeleaf.enable-spring-el-compiler=false # Enable the SpringEL compiler in SpringEL expressions. spring.thymeleaf.encoding=UTF-8 # Template files encoding. spring.thymeleaf.excluded-view-names= # Comma-separated list of view names (patterns allowed) that should be excluded from resolution. spring.thymeleaf.mode=HTML # Template mode to be applied to templates. See also Thymeleaf's TemplateMode enum. spring.thymeleaf.prefix=classpath:/templates/ # Prefix that gets prepended to view names when building a URL. spring.thymeleaf.reactive.chunked-mode-view-names= # Comma-separated list of view names (patterns allowed) that should be the only ones executed in CHUNKED mode when a max chunk size is set. spring.thymeleaf.reactive.full-mode-view-names= # Comma-separated list of view names (patterns allowed) that should be executed in FULL mode even if a max chunk size is set. spring.thymeleaf.reactive.max-chunk-size=0B # Maximum size of data buffers used for writing to the response. spring.thymeleaf.reactive.media-types= # Media types supported by the view technology. spring.thymeleaf.render-hidden-markers-before-checkboxes=false # Whether hidden form inputs acting as markers for checkboxes should be rendered before the checkbox element itself. spring.thymeleaf.servlet.content-type=text/html # Content-Type value written to HTTP responses. spring.thymeleaf.servlet.produce-partial-output-while-processing=true # Whether Thymeleaf should start writing partial output as soon as possible or buffer until template processing is finished. spring.thymeleaf.suffix=.html # Suffix that gets appended to view names when building a URL. spring.thymeleaf.template-resolver-order= # Order of the template resolver in the chain. spring.thymeleaf.view-names= # Comma-separated list of view names (patterns allowed) that can be resolved.
3.语法规则(可参考thymeleaf的官方文档)
th:text="${}"
th:href="#{}"
"@{/assert/css/min.js}"
Springboot自动配置好了Spring MVC;
以下是Springboot对Spring MVC的自动配置:
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(view),视图对象决定如何渲染(转发?重定向?));
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver:是用来组合所有的视图解析器的;
如何定制:我们可以自己在容器中添加一个视图解析器;自动将其组合进来;
@Configuration class A //注入myViewResolver对象到容器中 @Bean public MyViewResolver myViewResolver(){ return new MyViewResolver(); } //实现ViewResolver,重写方法 public static class MyViewResolver implements ViewResolver { @Override public View resolveViewName(String s, Locale locale) throws Exception { return null; } } //如何看自定义的视图解析器有没有生效,找到这个类,打断点,debug,访问localhost:8080, public class DispatcherServlet extends FrameworkServlet { ....... //找到适配方法 doDispatch protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null;
Support for serving static resources, including support for WebJars
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
Converter
转换器,类型转换使用Converter 比如将文本转换成Integer;
Formatter
格式化器,2020.02.02 ===Date;
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
registry.addConverter(converter);
}
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
registry.addFormatter(formatter);
}
}
Support for HttpMessageConverters
HttpMessageConverters
:SpringMVC用来转换Http请求和响应的,User—json;HttpMessageConverters
是从容器中确定;获取所有的HttpMessageConverters
;HttpMessageConverters
只需要自己将组件注册在容器中;Automatic registration of MessageCodesResolver
Static index.html
support.
Custom Favicon
support (covered later in this document).
Automatic use of a ConfigurableWebBindingInitializer
bean (covered later in this document).
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, you can declare a WebMvcRegistrationsAdapter
instance to provide such components.
If you want to take complete control of Spring MVC, you can add your own @Configu
1)Springboot在自动配置很多组件的时候,先看容器中有没有用户自己的配置(@Bean @Component),如果有,则用用户自己配置的,如果没有,则自动配置默认的,如果有些组件可以有多个如:ViewResolver,将用户配置与默认配置进行组合;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:view-controller path="/hello" view-name="success"></mvc:view-controller>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
现在:编写一个类@Configuration,是WebMvcConfigurationAdapter,不能标注@EnableWebMvc,否则会让springboot默认配置全失效,这样既保留了默认配置,自定义配置也生效了
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//localhost:8080/shufang => localhost:8080/success跳转映射
registry.addViewController("/shufang").setViewName("success");
}
}
原理:
@Configuration
//这个注解也是允许所有的Mvc配置
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
//WebMvcConfigurer是springboot自动配置的主接口,
//现在所有的自动默认配置都写进了WebMvcAutoConfigurationAdapter这个类,它属于WebMvcAutoConfiguration的静态内部类成员
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@Import(EnableWebMvcConfiguration.class)
//首先我们点进EnableWebMvcConfiguration @Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware { //这是个配置类,然后继承来自DelegatingWebMvcConfiguration的方法 //这个类里面通过@Bean向容器中注入了很多类及配置,我们再看看DelegatingWebMvcConfiguration,这个类也是一个配置类,同时里面有一个final属性WebMvcConfigurerComposite configurers,而且这个类里面的很多方法都是调用configurers的方法,我们去看看! @Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); @Override protected void configurePathMatch(PathMatchConfigurer configurer) { this.configurers.configurePathMatch(configurer); } @Override protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { this.configurers.configureContentNegotiation(configurer); } @Override protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { this.configurers.configureAsyncSupport(configurer); } @Override protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { this.configurers.configureDefaultServletHandling(configurer); } //进来之后发现WebMvcConfigurerComposite也是WebMvcConfigurer的实现类,与WebMvcAutoConfigurationAdapter一样,这里面的方法可真多 class WebMvcConfigurerComposite implements WebMvcConfigurer { //以下均是方法 addWebMvcConfigurers configurePathMatch //配置路径匹配 configureContentNegotiation //配置视图合并 configureAsyncSupport //添加异步支持 configureDefaultServletHandling addFormatters //添加格式化器 addInterceptors //添加拦截器 addResourceHandlers //配置静态资源映射 addCorsMappings addViewControllers //添加视图控制器,如页面跳转 configureViewResolvers //配置视图解析器 addArgumentResolvers //添加参数解析器 addReturnValueHandlers configureMessageConverters extendMessageConverters // configureHandlerExceptionResolvers extendHandlerExceptionResolvers
只需要在配置类上标注@EnableWebMvc
即可,此时默认的静态资源都无法访问了,默认配置全失效
如果访问:localhost:8080 ,Springboot会默认去静态资源目录下找index.html文件;
改变文件映射的方法如下:
@RequestMapping({"/","/index.html"})
public String index(){
return "index";
}
@Configuration public class MyMvcConfig02 extends DelegatingWebMvcConfiguration { @Override protected void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/shufang").setViewName("success"); } } //与上面的形式其实是一样的,因为实际上 WebMvcConfigurer是DelegatingWebMvcConfiguration的一个属性,而且 @Override protected void configurePathMatch(PathMatchConfigurer configurer) { //实际上是调用configurers的方法 this.configurers.configurePathMatch(configurer); } @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/shufang").setViewName("success"); } }
#这个是英文配置页面
login.btn=Sign in
login.password=password
login.remember=remember me
login.tip=Please Sign in
login.username=username
1.而且做国际化的组件在Springboot中是以MessageSourceAutoConfiguration自动配置了
//这个类就是用来做自动配置国际化的类 @EnableConfigurationProperties public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = {}; @Bean @ConfigurationProperties(prefix = "spring.messages") //通过messageSourceProperties来封装spring.messages的配置 public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } //给容器中添加一个组件messageSource //其实配置可以直接放在默认类路径下的message.properties的文件下 private String basename = "messages"; @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { //这个getBasename()就是获取配置文件的基础名,让这些配置文件生效,默认配置"message" 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; }
既然是有默认自动配置,我们就可以通过springboot的配置文件修改默认配置如下:
#增删改查的访问路径设置
server.servlet.context-path=/crud
#这哥login就是基础名.比如 login_zh_CN.properties的基础名就是login
spring.messages.basename=classpath:i18n/login
#在thymeleaf中通过“#{}”来获取配置文件中的值如:
th:text="#{login.username}"等,这样的话就会经过模版引擎进行优化
2.springboot还自动注入并配置了默认的国际化区域信息解析器
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
//容器中没有区域解析器才会进行默认配置@ConditionalOnMissingBean,只要我们自己配置了 然后通
//@Configuration类进行注入,默认配置才不会生效
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
//默认使用的是这个LocaleResolver
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
//默认就是根据请求头带来的区域信息获取locale进行国际化,
那么我们自定义的区域解析器如下:
/** * 需要在请求html请求中带区域信息 * 如:th:href="@{/index.html(l='en_US')}" * th:href="@{/index.html(l='zh_CN')}" * 这样在前端url请求的时候就是localhost:8080/index.html?l=en_US */ public class MyLocaleResolver implements LocaleResolver { //逻辑写好了之后,然后在一个config类中进行配置 @Override public Locale resolveLocale(HttpServletRequest request) { String para = request.getParameter("l"); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(para)) { String[] s = para.split("_"); locale = new Locale(s[0], s[1]); } //最终将这个国际化区域信息进行返回 return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } } //注入到容器中 @Bean public MyLocaleResolver myLocaleResolver(){ return new MyLocaleResolver(); }
//1 spring.thymeleaf.cache= false //2在idea修改完页面之后ctrl+F9,重新编译页面代码 ctrl+F9 //3登陆错误消息前端的显示,if的优先级比text要高 <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}" ></p> //4 防止重复提交=>重定向 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { .......... registry.addViewController("/main.html").setViewName("dashboard.html"); } } @PostMapping("/user/login") public String login( @RequestParam("username") String username, @RequestParam("password") String password, //保存错误信息 Map<String,Object> map){ //如果登陆成功 跳转到dashboard.html页面 if (!StringUtils.isEmpty(username) && "123456".equals(password)){ //防止刷新的时候页面重新提交,这时候需要用到重定向到main.html,这个main相当于一个中间页面 //然后main.html通过视图映射到dashboard.html return "redirect:/main.html"; }else { //如果用户密码错误,保存错误信息,并返回到登陆页面 //这个是到时候要在前端显示的 map.put("msg","用户名或密码错误"); return "login"; } }
自定义拦截器,是需要实现HandlerIntercepter接口实现里面的方法就行了
//抽象类,用来管理配置 abstract class DataSourceConfiguration { @SuppressWarnings("unchecked") protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) { return (T) properties.initializeDataSourceBuilder().type(type).build(); } /** * Tomcat Pool DataSource configuration. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true) //这个是一个静态内部类,用来配置Tomcat数据源 static class Tomcat { @Bean //与配置文件中的spring.datasource.tomcat属性进行绑定 @ConfigurationProperties(prefix = "spring.datasource.tomcat") org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) { org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class); DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl()); String validationQuery = databaseDriver.getValidationQuery(); if (validationQuery != null) { dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(validationQuery); } return dataSource; //用来创建自定义数据源 @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(DataSource.class) //通过type来指定数据源的类型,比如DruidDataSource @ConditionalOnProperty(name = "spring.datasource.type") static class Generic { @Bean DataSource dataSource(DataSourceProperties properties) { //通过builder创建数据源 return properties.initializeDataSourceBuilder().build(); } } //创建自定义数据源的方法 public DataSourceBuilder<?> initializeDataSourceBuilder() { return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName()) .url(determineUrl()).username(determineUsername()).password(determinePassword()); }
在DataSourceAutoConfiguration类中,也有自动配置数据源的代码如下:
@Configuration(proxyBeanMethods = false) //DataSourceInitializerInvoker.class这个类实际上是用来初始化的一个类 @Import({ DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class }) //这个类是通过DataSourceAutoConfiguration这个类@Import进来的 class DataSourceInitializationConfiguration { /**这个是用来初始化·建表sql·的 * Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on * {@link InitializingBean#afterPropertiesSet()} and {@literal data-*.sql} SQL scripts on * a {@link DataSourceSchemaCreatedEvent}. */ class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean { private void initialize(DataSourceInitializer initializer) { try { this.applicationContext.publishEvent(new DataSourceSchemaCreatedEvent(initializer.getDataSource())); // The listener might not be registered yet, so don't rely on it. if (!this.initialized) { this.dataSourceInitializer.initSchema(); this.initialized = true; } } catch (IllegalStateException ex) { logger.warn(LogMessage.format("Could not send event to complete DataSource initialization (%s)", ex.getMessage())); } }
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {
@Bean
@Primary
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
shema: 指定初始化的sql文件 classpath:employee.sql
通过注解的方式访问步骤如下:
1.创建实例对象
/*创建bean对象,属性与数据库中的字段进行绑定*/
package com.shufang.springbootdatamybatis.beans;
public class Department {
private String id;
private String depatmentName;
//这个get set方法是必须要的,底层是这样调用
getter and setter()
constructor.....
}
2.创建一个映射接口
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; //这个是一个映射注解,不标注的话bean与数据库的字段无法进行映射表绑定 //@MapperScan( ? = "path")标注在配置类上也可以实现全局的Mapper效果 @Mapper public interface DepartmentMapper { //select注解就是 “查” @Select("select * from emp where id = #{id}") public Department getByid(String id); //Insert注解就是 “增” //Update注解就是 “改” //Delete注解就是 “删” @Insert("insert into employee(id,departmentName) values(#{id},#{departmentName})") public int insert(Employee employee); }
3.一些mybatis的配置可以通过配置类进行设置
/** * 这个mybatis的配置类,主要是配置一些mabatis的设置,如驼峰命名规则 */ @Configuration public class MybatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { //此处可以用lambda表示这个匿名内部类 return new ConfigurationCustomizer() { @Override public void customize(org.apache.ibatis.session.Configuration configuration) { //配置驼峰命名规则 configuration.setMapUnderscoreToCamelCase(true); } }; } }
1.创建bean对象
public class Employee { //此处一定要有getter setter方法, private String id; private String lastName; private String email; ........get set.... //为此类创建一个映射接口 @Mapper public interface EmployeeMapper { public Employee getById(String id); public int deleteEmp(String id); public int updateEmp(Employee employee); public void insert(Employee employee); }
mybatis的项目已经与2013年上传到github
https://mybatis.org/mybatis-3/configuration.html:配置文件参考这个官方文档的
2.创建一个mybatis的全局配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--指定驼峰命名规则/-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
3.创建一个映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这个namespace需要与我们的EmployeeMapper接口进行绑定,需要填写全类名/-->
<mapper namespace="org.mybatis.example.BlogMapper">
<!--这个id就是接口的方法名,resultType就是方法的返回类型的全类名/-->
<select id="getById" resultType="com.shufang.springbootdatamybatis.beans.Employee">
select * from employee where id = #{id}
</select>
<insert id="insert" >
insert into employee(id,lastName,email) values(#{id},#{lastName},#{email})
</insert>
</mapper>
4.在配置文件里面指定这2个文件的位置
# mybatis配置,可以在MybatisProperties里面查看
# 用来指定mybatis的主配置xml文件的位置
mybatis.config-location=classpath:mybatis/*.xml
# 用来指定映射文件xml的位置
mybatis.mapper-locations=classpath:mybatis/mapper/employeeMapper.xml
前言:首先在application里面添加数据库MySQL的配置:
# Mysql 数据源的配置
spring.datasource.data-username=root
spring.datasource.url=jdbc:mysql://localhost/jpa
spring.datasource.data-password=000000
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
1.首先创建一个entitiy的类
//表明不是一个普通的bean,是与表对应的对象
@Entity
//映射的表,如果省略,那么表名就是User的小写:user
@Table(name = "tbl_user")
public class User {
//表明是主键,并且策略是自增
@Id()
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "lastname")
private String lastName;
@Column //省略字段,使用默认的字段:email
private String email;
....此处省略getter setter
2.然后自定义一个接口interface extend JpaRepository,不需要实现任何方法
//下面的范型User就是需要操作的实体类,Integer是操作实体类的·主键属性·类型
public interface UserRepository extends JpaRepository<User,Integer> {}
//这个类会自动在mysql中创建与entities实例对应的表
3.然后编写一个UserController,写入插入和删除的方法
@RestController public class UserController { @Autowired UserRepository userRepository; //请求方式:localhost:8080/user/1 <=(if id=1) @GetMapping("/user/{id}") public User getUserById(@PathVariable("id") Integer id) { User user = userRepository.getOne(id); return user; } //请求方式:localhost:8080/user?id=xx&lastName=x&email=x @PostMapping("/user") public User insertUser(User user){ //这里不需要穿参数,只需要在访问的时候添加id=xx&lastName=x&email=x就行了! User savedUser = userRepository.save(user); return savedUser; } }
4.最后在application里面添加jpa的相关配置
# JPA配置
#自动根据实体类创建对应的表
spring.jpa.hibernate.ddl-auto=update
#在控制台打印出初始化执行的sql
spring.jpa.show-sql=true
前言:首先调用程序的main方法进来;
public static void main(String[] args) {
SpringApplication.run(SpringbootDataJpaApplication.class, args); }
1.首先通过SpringApplication的构造器创建一个SpringBoot的实例
//最终返回一个ConfigurableApplicationContext的ioc容器
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//这里调用了2个方法:
//primarySources是从主类同级目录下的所有的子包下的类,通过反射的方式获取;
//SpringApplication()构造方法、run()方法,我们先点进SpringApplication()
return new SpringApplication(primarySources).run(args);
}
2.这里首先校验primarySources,然后将primarySources通过吧HashSet保存起来,然后调用*WebApplicationType.deduceFromClasspath()*方法,确定应用类型,返回一个
//这个构造方法调用许多的方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
3.那么我们点进WebApplicationType.deduceFromClasspath方法:SpringbootApplication中的method
//这个方法从类路径下找到对应的类,推断出Web应用的类型:deduce[推断] static WebApplicationType deduceFromClasspath() { //推断一 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } //推断二 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } //推断3,默认是Servlet的类型 return WebApplicationType.SERVLET; } private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext"; private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
4.然后调用*setInitializers()*设置初始化器,点进去
//这里方法中传入的是一个 初始化器的集合,那么这些初始化器是通过调用getSpringFactoriesInstances方法获取实例,只要是ApplicationContextInitializer的实现类全部加载进来,那么我们点进去这个方法一谈究竟 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //实际上是调用SpringFactoriesLoader.loadFactotyNames()方法,那么我们点进去看看 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } // SpringFactoriesLoader.loadFactotyNames()调用loadSpringFactories()方法: Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" //不难发现我们还是去从所有包的类路径下的META-INF/spring.factories下去寻找资源(Initallizers)
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
5.然后调用*setListeners()*方法,拿我们点进去看看
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//不难发现还是去从META-INF/spring.factories下获取资源,
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//然后通过反射的方式从配置spring.factories文件中获取实例
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
6.然后通过方法是不是main推断出主应用类,此时SpringApplication实例创建完毕
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
........
7.然后创建一个开始通过SpringApplication的实例调用run()方法
return new SpringApplication(primarySources).run(args); //那么我们点进去run()方法,首先是获取Listeners并启动 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); //获取args传入参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备执行环境,这个准备执行环境是在初始化ioc ApllicationContext之前完成的 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //点进prepareEnvironment方法 // Create and configure the environment来自源码。 [ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment);] //然后通过配置文件中的内容@ConfigurationProperties中的配置 private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties"; //绑定配置标签 ConfigurationPropertySources.attach(environment); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //创建上下文环境的对象,其实是通过WebApllication的类型创建对应的上下文环境 context = createApplicationContext(); 【 //createApplicationContext()里面的逻辑 switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); }】 //准备上下文,这个步骤最终Listener将上下文环境加载,并进行监听 prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); //刷新context之后,所有的Listner循环遍历启动对应的context listeners.started(context); //回调各种Runner的run()方法,我们点进去看看 callRunners(context, applicationArguments); 【 //优先添加ApplicationRunner runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); //其次添加所有的命令行Runner,这里存在优先级的关系 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); 】
8.最后先后回调ApplicationRunner、CommandLineRunner的方法、大吉大利,程序已经跑起来了!
需要被加在classpath:META-INF/spring.factories中才能生效
ApplicationContextInitializer
HelloSpringApplicationRunListener
简单的实践如下:
//初始化context public class HelloApplicationContextInitializer implements ApplicationContextInitializer { //HelloApplicationRunner启动器 @Component public class HelloApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { } } //命令行启动器 @Component public class HelloCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { } } //程序运行监听器 public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { //这个有参构造器必须要有 ,不然启动的时候会报错 public HelloSpringApplicationRunListener(SpringApplication application ,String[] args){ } }
1.这个场景需要用到的依赖是什么?
2.如何编写自动配置
@ConditionOnXXX
@Configuration //表明是一个配置类
@Bean //添加组件到容器中
@AutoConfigureAfter //修改自动配置的顺序
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。