Springboot从入门到精通

Springboot从入门到精通

Springboot的依赖

1.父工程(parent 版本仲裁中心)

<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>

2.注解解析

@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的配置文件

springboot的配置文件有2种:

  • application.properties
  • application.yml

配置文件的所有默认配置都可以在配置文件中修改指定,然后通过Springboot帮我们配置好

application.properties

server.port=8081

application.yml

yml文件比xml、json文件更适合做配置文件,因为yaml是一门以数据为中心的标记语言;

yml与xml的对比如下:

这是xml的配置格式
<server>
<port>8081</port>
</server>
server:
	port: 8081

很明显yml比xml要简洁很多;

yml的语法
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单元测试类下面进行打印测试
properties文件解决中文乱码问题
  • 解决方法:在idea设置栏中搜索file encoding ,然后勾选下面的勾选框

    Transparent native-to-ascii conversion

@Value&@ConfigurationProperties的获取值的区别

这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&@ImportResource

@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 {
Springboot推荐给容器中添加组件的方式

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();
    }
}
Springboot配置文件占位符
#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}

Profile多环境支持

1.多profile文件方式

命名规则:application-dev.properties\application-prod.properties

通过在properties中指定:spring.profiles.active=dev/prod 来激活其他配置文件

2.yml多文档块方式

在文件中用---来作为文档块的分隔符;

#---属于一个文档块的分隔符,此时是dev这个环境配置生效
server:
  port: 8081
spring:
  profiles:
    active: dev
---
server:
  port: 8084
spring:
  profiles: dev
---
server:
  port: 8085
spring:
  profiles: prod
3.profile激活方式

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("","")的形式

springboot自动配置原理

配置文件能写什么?怎么写?自动配置原理参考:

https://docs.spring.io/spring-boot/docs/1.5.10.BUILD-SNAPSHOT/reference/html/

自动配置原理(Springboot2.1.12)
//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 

Springboot日志

Springboot Web开发

  • 如何使用springboot

1.创建Springboot应用、选择我们需要的模块如:Web、Jdbc、Mabatis、Mysql等

2.Springboot已经默认将这些场景配置好了,只需要在配置文件中指定少量的配置就能运行起来

3.自己编写业务代码

在这里我们很好奇springboot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展等等…

1.xxxxAutoConfiguration 帮我们给容器中自动配置服务组件
2.xxxxProperties 用来封装配置文件的内容的属性类

Springboot对静态资源的映射

所有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个默认目录下进行查找
  1. 欢迎页,静态资源目录下的index.html被“/**/映射,也是在静态资源的目录下找

http://localhost:8080/ => 会直接去找静态资源下的index.html页面

4)所有的.**/favicon.ico都是在静态资源目录下映射

#指定默认的静态资源目录
spring.resources.static-locations=classpath:/udfdir/,classpath:/udfdir2/ 

Springboot推荐的模版引擎Thymeleaf

   <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>
Thymeleaf的使用&语法
@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就能自动渲染;

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}"

Spring MVC自动配置

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

    • 静态资源文件夹路径和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

如何替换springboot的默认配置

1)Springboot在自动配置很多组件的时候,先看容器中有没有用户自己的配置(@Bean @Component),如果有,则用用户自己配置的,如果没有,则自动配置默认的,如果有些组件可以有多个如:ViewResolver,将用户配置与默认配置进行组合;

2)以前通过.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" 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
    
3)全面接管SpringMvc

只需要在配置类上标注@EnableWebMvc即可,此时默认的静态资源都无法访问了,默认配置全失效

主页映射

如果访问:localhost:8080 ,Springboot会默认去静态资源目录下找index.html文件;

改变文件映射的方法如下:

1.通过@RequestMapping
@RequestMapping({"/","/index.html"})
public String index(){
  return "index";
}
2.通过addViewController()添加视图映射
@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接口实现里面的方法就行了


JDBC自动配置数据源原理

1.DataSourceConfiguration
//抽象类,用来管理配置
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());
	}

2.DataSourceAutoConfiguration

在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()));
		}
	}
3.JdbcTemplate用来与数据库进行交互
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {

	@Bean
	@Primary
	JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {

shema: 指定初始化的sql文件 classpath:employee.sql

Mybatis访问Mysql数据库

1.通过注解的方式进行映射

通过注解的方式访问步骤如下:

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);
            }
        };
    }
}
2.通过配置的方式进行映射

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

JPA的方式hibernate访问MySQL

前言:首先在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

Springboot的启动原理(源码解析2.2.4.REALESE)

前言:首先调用程序的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

    • 需要被加到ioc容器中才能生效
    • HelloApplicationRunner
    • HelloCommandLineRunner

简单的实践如下:

//初始化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){      
    }
}

自定义Starter

1.这个场景需要用到的依赖是什么?

2.如何编写自动配置

@ConditionOnXXX
@Configuration //表明是一个配置类
@Bean  //添加组件到容器中
@AutoConfigureAfter  //修改自动配置的顺序
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值