Spring Boot初阶篇笔记

SpringBoot笔记

SpringBoot官方文档

一、SpringBoot的常用注解

@ConfigurationProperties、@PropertySource、@ImportResource的区别

1.@ConfigurationProperties:
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性与配置文件中的相关属性进行绑定;
如:@ConfigurationProperties(prefix = "person") 就是将配置文件中前缀为person下边的属性与该注解所在类中的属性进行绑定
2.@PropertySource:
@PropertySource:加载指定的配置文件
如:@PropertySource("classpath:person.properties")
3.@ImportResource:
@ImportResource:导入Spring的配置文件xxx.xml,让配置文件中的内容生效;:@ImportResource(location={"classpath:bean.xml"})(作用在启动类上)
4.@Configuration:
@Configuration:指明当前类是一个配置类;用来代替之前的Spring配置文件.xml

二、Profile多环境支持

1.多文件

约定文件名:application-{profile}.properties 使用spring-profiles.active={profile}激活对应的环境

application-dev.properties

application-prod.properties

2.yml多文档块


server:
  port: 8080
spring:
  profiles:
    active: prod
---
server:
  port: 8081
spring:
  profiles: dev
---
server:
  port: 8082
spring:
  profiles: prod

三、配置文件加载位置

SpringBoot启动会扫描一下位置的application.properties或application.yml文件作为SpringBoot的默认配置文件。

file:./config/

file:./

classpath:/config/

classpath:/

优先级由高到低,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件,故会形成一个互补配置

四、外部配置加载顺序

1.命令行参数

如:java -jar xxx.jar --server.port=8087

由jar包外向jar包内

优先加载带profile的

2.jar包外部的application-{profile}.properties或application.ym|(带spring.profile)配置文件
3.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

再来加载不带profile的

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

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

五、自动配置原理(面试必问题)

application.properties配置文件参考

@EnableAutoConfiguration
->@Import({AutoConfigurationImportSelector.class})
->getCandidateConfigurations(annotationMetadata, attributes);
->SpringFactoriesLoader.loadFactoryNames();
->classLoader.getResources(“META-INF/spring.factories”)

①配置原理:

1.SpringBoot启动时加载主配置类,开启了自动配置功能 @EnableAutoConfiguration

2.@EnableAutoConfiguration的作用:
●利用@EnableAutoConfigurationImportSelector给容器中导入一些组件?
●List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
●SpringFactoriesLoader.loadFactoryNames()扫描所有jar包类路径下 META-INF/spring.factoryies,把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把它们添加在容器中

将类路径下 META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中;

每一个xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置

3.每一个配置类进行自动配置功能

4.以HttpEncodingAutoConfiguration为例解释自动配置原理:

@Configuration
@EnableConfigurationProperties({ServerProperties.class})//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把这个类加入到ioc容器中

@ConditionalOnWebApplication//Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类中的配置就会生效   判断当前类是不是Web应用,如果是则生效;

@ConditionalOnClass({CharacterEncodingFilter.class})//判断当前项目有没有这个类

@ConditionalOnProperty(
    prefix = "server.servlet.encoding",
    value = {"enabled"},
    matchIfMissing = true
)//判断配置文件中是否存在某个配置 如:server.servlet.encoding.enable ; 如果不存在,判断也是成立的
public class HttpEncodingAutoConfiguration {
    private final Encoding properties;//
	//只有一个有参构造方法的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(ServerProperties properties) {
        this.properties = properties.getServlet().getEncoding();
    }
    @Bean//给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @ConditionalOnMissingBean//判断容器中没有这个组件才会加入该组件
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        						      	filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
        return filter;
    }

一但这个自动配置类生效,这个配置类就会给容器中添加组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;

精髓:

1)、SpringBoot启动会加载大量的自动配置类

2)、看我们需要的功能有没有SpringBoot默认写好的自动配置类

3)、我们再来看这个自动配置类中到底配置了那些组件;

4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可在配置文件中指定这些属性的值;

xxxAutoConfiguration自动配置类

给容器中添加组件

xxxProperties封装配置文件中相关属性

②细节:

@Conditional派生注解

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的内容才生效。

@Conditional扩展注解作用(判断是否满足当前指定条件)
@ConditionalOnJava系统的java版本是否符合要求
@ConditionalOnBean容器中存在指定的Bean
@ConditionalOnMissingBean容器中不存在指定的Bean
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty系统中指定的属性是否有指定的值
@ConditionalOnResource类路径下是否存在指定资源文件
@ConditionalOnWebApplication当前是Web环境
@ConditionalOnNotWebApplication当前不是Web环境
@ConditionalOnJndiJNDI存在指定项

自动配置类必须在一定条件下才能生效

故该怎么知道哪些自动配置类是否生效?

只需在配置文件中开启debug=true 来让控制台打印自动配置报告

六、Spring Boot与日志

1.市面上常用的日志框架:

JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…

日志门面(日志的抽象层)日志实现
JCL(Jakarta Commons Logging)
SLF4j(Simple Logging Facade for Java)
Jboss-logging
log4j JUL (java.util.logging)
log4j2 Logback

左边选个日志门面、右边选一个来实现;

日志门面:SLF4J;

日志实现:Logback;

SpringBoot:底层是Spring框架,Spring框架默认使用JCL;

SpringBoot选用SLF4j和Logback;

2.SLF4j使用

1> 如何在系统中使用SLF4J

以后开发的时候,日记方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;

给系统里面导入slf4j的jar和logback的实现;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

image-20220730075858149

每一个日志的实现框架都有自己的配置文件,使用slf4j后,配置文件还是使用日志实现框架自己本身的配置文件

2 >遗留问题

SpringBoot使用的是slf4j+logback,但其他框架本身也可能在使用别的日志框架,如Spring(commons-logging)、Hibernate(jboss-logging)、Mybatis…故需要统一使用slf4j进行输出

image-20220730075944379

3.SpringBoot日志间的关系

如何让系统中的日志都统一到slf4j:

1.将系统的中其他日志框架先排出去

2.用中间包来替换原有的日志框架

3.再导入slf4j其他的实现

image-20220730080004727

总结:

1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录

2)、SpringBoot把其他的日志都替换成了slf4j

3)、中间替换包(log4j-to-slf4j、jul-to-slf4j)

一句话:SpringBoot能适应所有的日志,而且底层使用slf4j的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉。

4.日志使用

1.默认配置
 //记录器
    Logger logger = LoggerFactory.getLogger(getClass());
    @Test
    void contextLoads() {
        //日志的级别: trace<debug<info<warn<error  由低到高,低于当前级别的则不显示,默认为info级别,可自定义在配置文件中指定级别
        logger.trace("这是trace日志");
        logger.debug("这是debug日志");
        logger.info("这是info日志");
        logger.warn("这是warn日志");
        logger.error("这是error日志");

    }
2.日志输出格式:

%d表示时间、

%thread表示线程名、

%-5level表示级别从左显示5个字符宽度、

%logger{50}表示logger名字最长50个字符,否则按照句点分割

%msg日志消息、

%n 换行

如:%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n

SpringBoot修改日志配置:
#指定级别 (可具体到哪个包)
logging.level.com=trace
#不指定路径则在当前项目下生成日志文件,也可以指定路径
logging.file=
#在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用spring.log作为默认文件
logging.file.path=/spring/log
#在控制台输出的日志格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n
#指定文件中输出的日志格式
logging.pattern.file=
3.logback.xml配置样例
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/app/log" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="atguigu-springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
			%d表示日期时间,
			%thread表示线程名,
			%-5level:级别从左显示5个字符宽度
			%logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
			%msg:日志消息,
			%n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
		logger主要用于存放日志对象,也可以定义日志类型、级别
		name:表示匹配的logger类型前缀,也就是包的前半部分
		level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
		additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
		false:表示只用当前logger的appender-ref,true:
		表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger -->
    <logger name="com.atguigu" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 
log4j.properties配置样例
### set log levels ###
log4j.rootLogger = debug ,  stdout ,  D ,  E

### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n

#### 输出到日志文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
#
#### 保存异常信息到单独文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/error.log ## 异常日志文件名
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

七.Web开发

1.SpringBoot对静态资源的映射规则

private final ResourceProperties resourceProperties;//设置和静态资源有关的参数,如缓存时间
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();
                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 WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
            welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
            welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
            return welcomePageHandlerMapping;
        }

1) 所有/webjars/**,都去classpath:/META-INF/resources/webjars/找资源

webjars:以jar包的方式引入静态资源 (即在pom.xml文件中引入资源的依赖https://www.webjars.org/https://mvnrepository.com/

2)“/**”:访问当前项目所有资源(静态济源文件夹)

"classpath:/META-INF/resources/", 
"classpath:/resources/", 
"classpath:/static/", 
"classpath:/public/"
"/" :当前项目所有的根路径

3)欢迎页;静态资源文件夹下的所有index.html页面被"/**"映射。

4)所有的**/favicon.ico 都是在静态资源文件下找。

2.模板引擎

SpringBoot推荐Thmeleaf,功能强大,语法简单。

1)引入thymeleaf
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2)Thymeleaf使用&语法
@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
	private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    ...
}

1.表达式:

Simple expressions:(表达式语法)
    Variable Expressions: ${...}:获取变量值;OGNL;
    		1)、获取对象的属性、调用方法
    		2)、使用内置的基本对象:
    			#ctx : the context object.
    			#vars: the context variables.
                #locale : the context locale.
                #request : (only in Web Contexts) the HttpServletRequest object.
                #response : (only in Web Contexts) the HttpServletResponse object.
                #session : (only in Web Contexts) the HttpSession object.
                #servletContext : (only in Web Contexts) the ServletContext object.
                
                ${session.foo}
            3)、内置的一些工具对象:
#execInfo : information about the template being processed.
#messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris : methods for escaping parts of URLs/URIs
#conversions : methods for executing the configured conversion service (if any).
#dates : methods for java.util.Date objects: formatting, component extraction, etc.
#calendars : analogous to #dates , but for java.util.Calendar objects.
#numbers : methods for formatting numeric objects.
#strings : methods for String objects: contains, startsWith, prepending/appending, etc.
#objects : methods for objects in general.
#bools : methods for boolean evaluation.
#arrays : methods for arrays.
#lists : methods for lists.
#sets : methods for sets.
#maps : methods for maps.
#aggregates : methods for creating aggregates on arrays or collections.
#ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).

    Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
    	补充:配合 th:object="${session.user}:
   <div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
    </div>
    
    Message Expressions: #{...}:获取国际化内容
    Link URL Expressions: @{...}:定义URL;
    		@{/order/process(execId=${execId},execType='FAST')}
    Fragment Expressions: ~{...}:片段引用表达式
    		<div th:insert="~{commons :: main}">...</div>
    		
Literals(字面量)
      Text literals: 'one text' , 'Another one!' ,…
      Number literals: 0 , 34 , 3.0 , 12.3 ,…
      Boolean literals: true , false
      Null literal: null
      Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
    String concatenation: +
    Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
    Binary operators: + , - , * , / , %
    Minus sign (unary operator): -
Boolean operations:(布尔运算)
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
Conditional operators:条件运算(三元运算符)
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
Special tokens:
    No-Operation: _ 

3.SpringMVC自动配置原理

Spring Boot Reference Guide

Spring Boot自动配置好了SpringMVC

1.以下是SpringBoot对SpringMVC的默认:
  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
    • ContentNegotiatingViewResolver:组合所有的视图解析器
    • 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合起来;
  • Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars。

  • Automatic registration of Converter, GenericConverter, Formatter beans.

    • 自动注册了
    • Converter:类型转换器
    • GenericConverter,
    • Formatter:格式化器
    • 自己添加的格式化转换器,我们只需要放在容器中即可;
  • Support for HttpMessageConverters (see below).

    • HttpMessageConverter :SpringMVC用来转换请求和响应的;
    • HttpMessageConverters:是从容器中确定;获取所有的HttpMessageConverter ;
    • 自己给容器中添加HttpMessageConverter ,只需将自己的组件注册到容器中(@Bean,@Component)
  • Automatic registration of MessageCodesResolver (see below).定义错误代码生成规则

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

  • Custom Favicon support (see below).

  • Automatic use of a ConfigurableWebBindingInitializer bean (see below).

    • 我们可以配置一个ConfigurableWebBindingInitializer添加到容器来替换默认的;

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

2.扩展SpringMVC

编写一个配置类(@Configuration)是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;

既保留了所有的自动配置,也能用我们扩展的配置;

/**
 * 使用WebMvcConfigurerAdapter可以来扩展SpringMvc的功能
 */
//@EnableWebMvc//能全权接管SpringMvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    //注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        SpringBoot2.0+ 会拦截静态资源
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login", "/asserts/**", "/webjars/**");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**", "/asserts/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/static");
    }

    //所有的组件会一起起作用
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //登录页视图解析
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");
        //主页视图解析
        registry.addViewController("/purchase_order.html").setViewName("main");
    }
}

原理:

​ 1)、WebMvcAutoConfiguration是SpringMvc的自动配置类

​ 2)、在做其他自动配置时,会导入:@Import(EnableWebMvcConfiguration.class)

 public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    //从容器中获取所有的WebMvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
     //一个参考实现
     //protected void addViewControllers(ViewControllerRegistry registry) {
        //this.configurers.addViewControllers(registry);
   // }
 }

3)、容器中所有的WebMvcConfiguration都会一起起作用;

4)、我们的配置类也会被调用

效果:SpringMVC的自动配置和我们自己扩展的自动配置都会起作用;

3.全面接管SpringMVC

SpringBoot对SpringMVC的自动配置不需要了,所有的都是自己配置的;只需要在配置类上添加@EnableWebMvc注解

4.如何修改SpringBoot的默认配置

模式:

1)SpringBoot在自动配置很多组建的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户自己配置的,如果没有才自动配置;如果有写组件有多个(如:ViewResolver)将用户配置的和默认的组个起来;

5.RestfulCRUD

1.Thymeleaf公共页面元素抽取

1、抽取公共片段
<div th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</div>
2、在需要的地方引入公共片段
<body>
...
<div th:insert="~{footer :: copy}"></div>
</body>
~{templatename::selector}  模板名::选择器
~{templatename::fragmentname}  模板名::片段名
3、默认效果
insert的功能片段在div标签中

三种引入功能片段的th属性:

**th:insert ** :将整个公共片段插入到声明引入的元素中

th:replace :将声明引入的元素替换为公共片段

th:include :将被引入的片段的内容包含近这个标签中

<footer th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

//三种引入方式
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
//效果
<div>
    <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</div>
    <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
<div>
	&copy; 2011 The Good Thymes Virtual Grocery
</div>

6.错误处理机制

1、SpringBoot默认的错误处理机制

1)、默认:浏览器返回一个默认的网页

image-20220730080127662

2)、客户端会返回 Json格式数据

image-20220730080138411

参照ErrorMvcAutoConfiguration:错误处理的自动配置

给容器中添加了一下组件:

DefaultErrorAttributes:帮我们在页面共享信息

@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
    boolean includeStackTrace) {
    Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
    errorAttributes.put("timestamp", new Date());
    addStatus(errorAttributes, requestAttributes);
    addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
    addPath(errorAttributes, requestAttributes);
    return errorAttributes;
}

BasicErrorController:处理默认/error请求

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = {"text/html"})//产生html类型数据;浏览器发送的请求被这个方法处理
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }
    
    
    @RequestMapping//产生json数据;客户端发送的请求被这个方法处理
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }
    
    

ErrorPageCustomizer:

public class ErrorProperties {
    @Value("${error.path:/error}")
    private String path = "/error";//系统出现错误后来到/error请求进行处理

DefaultErrorViewResolver:

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }

        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //默认SpringBoot去找error/404这个页面
        String errorViewName = "error/" + viewName;
        //模板引擎可以解析这个页面就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        //模板引擎可用的情况下返回到errorViewName指定的视图地址,模板引擎不能用就在静态资源文件夹下找errorViewName对应的页面  error/404.html
        return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
    }

步骤:

一但系统出现4xx或5xx之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),继而发送/error请求,继而被BasicErrorController处理

响应页面;去哪个页面是由DefaultErrorViewResolver解析得到的

 protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
        Iterator var5 = this.errorViewResolvers.iterator();

        ModelAndView modelAndView;
        do {
            if (!var5.hasNext()) {
                return null;
            }

            ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
            modelAndView = resolver.resolveErrorView(request, status, model);
        } while(modelAndView == null);

        return modelAndView;
    }
2.如何定制错误响应

1)、如何定制错误的页面:

有模板引擎的情况下:error/状态码:将错误页面命名为 错误状态码.html放在模板引擎文件夹里的error文件夹下,发生此状态码错误就会来到对应的页面;

我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确状态码页面优先。

页面能获取的信息:

​ timestamp:时间戳

​ status:状态码

​ error:错误提示

​ exception:异常对象

​ message:异常消息

​ errors:JSR303数据校验的错误都在这里

**无模板引擎的情况:**模板引擎找不到错误页面,静态资源文件夹下找

以上都没有错误页面,就默认来到SpringBoot默认的错误页面提示

2)、如何定制错误的 Json数据:

①:自定义异常处理&返回定制json数据(不能自适应效果)

@ControllerAdvice //异常处理注解
public class MyExceptionController {
    //SpringBoot不能自动识别是客户端访问还是浏览器访问,返回的全部时Json数据
   // @ResponseBody
    //@ExceptionHandler(Exception.class)//处理所有的异常,也可以处理特定的异常
    //public Map<String,Object> myException_1(Exception e){
      //  Map<String,Object> map = new HashMap<>();
      //  map.put("code","自己定义的异常" );
       // map.put("message",e.getMessage() );
      //  return map;
}

②:转发到/error进行自适应响应效果处理

@ControllerAdvice //异常处理注解
public class MyExceptionController { 
//可以根据客户端或浏览器 自适应返回数据类型
    @ExceptionHandler(Exception.class)//处理所有的异常,也可以处理特定的异常
    public String myException_2(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        //传入自己的状态码
        request.setAttribute("javax.servlet.error.status_code", 400);
        map.put("code","自己定义的异常" );
        map.put("message",e.getMessage() );
        return "forward:/error";
    }
}

③:携带自定义错误信息数据

出现错误后回来到/error请求,会被BasicErrorController处理,向应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法)

1.完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中

2.页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getAttributes得到,容器中DefaultErrorAttributes.getAttributes(),默认进行处理的;

自定义ErrorAttributes

/**
 * 能够在浏览器或者客户端访问时显示自定义的错误信息
 */
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String,Object> map = super.getErrorAttributes(webRequest, options);
        map.put("company","com");
        //我们的异常处理器携带的数据
        Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
        map.put("ext",ext );
        return map;
    }
}

最终结果:响应是自适应的,可以通过定制ErrorAttributes改变需要返回的内容

7.配置嵌入式Servlet容器

SpringBoot默认使用内嵌式Tomcat.

1)、如何定制和修改Servlet容器的相关配置

①:修改和server有关的配置(serverProperties)

server.port=8080
server.servlet.context-path=/curd
server.tomcat.uri-encoding=UTF-8

#通用的Servlet容器设置
server.xxx
#Tomcat的设置
server.tomcat.xxx

②:编写一个WebServerFactoryCustomizer(嵌入式Servlet定制器)来修改servlet容器的配置

/**
     * 2.0+的SpringBoot中EmbeddedServletContainerCustomizer已经不存在,被WebServerFactoryCustomizer替代了
     * @return
     */
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryWebServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
            //定制嵌入式的Servlet规则
            @Override
            public void customize(ConfigurableWebServerFactory factory) {
                factory.setPort(9999);
            }
        };
    }
2)、注册Servlet三大组件【servlet、Filter、Listener】

由于SpringBoot默认是以jar包的形式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件

注册三大件用一下方法:

ServletRegistrationBean

// SpringBoot注册Web三大组件(servlet、filter、listener)
    //1.注册Servlet
    @Bean
    public ServletRegistrationBean myServlet() {
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
        return registrationBean;
    }

FilterRegistrationBean

//2.注册Filter
    @Bean
    public FilterRegistrationBean myFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFilter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
        return registrationBean;
    }

ServletListenerRegistrationBean

//3.注册Listener
    @Bean
    public ServletListenerRegistrationBean myListener() {
        ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
        return registrationBean;
    }

SpringBoot自动配置SpringMVC时,自动注册了SpringMVC的前端控制器:DispatcherServlet

@Bean( name = {"dispatcherServletRegistration"} )
@ConditionalOnBean(value = {DispatcherServlet.class},name = {"dispatcherServlet"})
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
//默认拦截 "/" 所有请求,包括静态资源,但是不拦截JSP,"/*"拦截JSP
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }
3)、替换为其它嵌入式Servlet容器

默认支持:

Tomcat(默认使用):

Jetty:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--引入其他的Servlet容器-->
        <dependency>
            <artifactId>spring-boot-starter-jetty</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>
</dependency>

Undertow:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--引入其他的Servlet容器-->
        <dependency>
            <artifactId>spring-boot-starter-jetty</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>
</dependency>

8.使用外置Servlet容器

嵌入式Servlet应打成Jar包

​ 优点:简单、便捷

​ 缺点:默认不支持JSP、优化定制比较复杂

外置的Servlet容器:外面安装Tomcat 应用打成War包

步骤:

1、创建一个War项目

2、将嵌入式Tomcat指定为provided

<dependency>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <groupId>org.springframework.boot</groupId>
    <scope>provided</scope>
</dependency>

3、必须编写一个SpringBootServletInitializer的子类,并调用configure方法

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        //传入SpringBoot应用主程序
        return builder.sources(SpringBootRestfulcrudApplication.class);
    }
}

4、启动Tomcat服务器即可使用。

原理:

jar包:执行SpringBoot主启动类的main方法,启动ioc容器,创建嵌入式的Servlet容器。

war包:启动服务器,服务器启动SpringBoot应用(SpringBootServletInitializer),启动ioc容器。

八、SpringBoot与Docker

1、简介

Docker是一个开源的应用容器引擎;

Docker支持将软件编译成一个镜像,然后在镜像中配置好各种软件,将镜像发布出去,其他使用者可以直接使用这个镜像。运行中的这个镜像称为容器,容器启动是非常快的。

image-20220730080320998

image-20220730080332329

2、核心概念

docker主机(host):安装了Docker程序的机器(Docker直接安装在操作系统上)

docker客户端(Client):连接docker主机进行操作

docker仓库(Registry):用来保存各种打包好的软件镜像

docker镜像(Images):软件打包好的镜像;放在docker仓库中

docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用

image-20220730080355673

使用Docker的步骤:

1)、安装Docker

2)、去Docker仓库找到这个软件对应的镜像

3)、使用Docker运行这个镜像,这个镜像就会生成一个Docker容器

4)、对容器启动的停止就是对软件的启动停止

3、安装Docker

1)、安装Linux虚拟机

​ 1、VMWare、VirtualBox(轻量)

​ 2、导入虚拟机文件

​ 3、双击启动导入的虚拟机(用户名:root 密码:123456)

​ 4、使用客户端连接Linux服务器进行命令操作

​ 5、设置虚拟机网络 (桥接网络>根据实机选择网络选择界面名称>接入网线)

​ 6、重启虚拟机网卡

service network restart

​ 7、查看虚拟机IP地址

ip addr
2)、在Linux虚拟机上安装Docker
1、查看Centos版本(要求Centos版本内核高于3.10)
# uname -r
2、安装docker
# yum install docker
3、启动docker
# systemctl start docker
注:如果启动时报错:Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details.
解决:vi进入 /etc/sysconfig/docker
OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false'改为OPTIONS='--selinux-enabled=false --log-driver=journald --signature-verification=false'即可。
4、查看docker版本
# docker -v
Docker version 1.13.1, build 7d71120/1.13.1
5、开机自启docker
# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

4、Docker常用命令&操作

1)、镜像操作
操作命令说明
检索docker search xxx 如 docker search mysql与docker hub官网一样,可以检索镜像的信息,如镜像的Tag
拉取docker pull 镜像名 : tagtag是可选的,tag表示标签,多为软件的版本,默认是latest
列表docker images查看本地所有镜像
删除docker rmi image-id删除指定的本地镜像

docker hub官网搜索镜像

2)、容器操作

软件镜像(xxx.exe)----运行镜像----产生一个容器(正在运行的软件)

步骤:

1、搜索镜像
[root@localhost ~]# docker search tomcat
2、拉取镜像
[root@localhost ~]# docker pull tomcat  //拉取的tomcat镜像不要新版本
3、根据镜像启动容器
[root@localhost ~]# docker run --name mytomcat -d containerName // -d代表后台运行
f4991f8cf0bf0573f9549929aa98985b7148afd71bbe9c1f37c70aaa00acf461
4、停止运行中的容器
# docker stop 容器的id/docker container stop 容器ID
5、查看运行中的容器 
# docker ps
6、查看所有的容器
# docker ps -a 
7、启动容器
# docker start 容器的id/docker container start 容器ID
8、删除一个容器
# docker rm 容器的id
9、启动一个做了端口映射的tomcat
# docker run -d -it -p 8888:8080 tomcat;
-p:主机的端口映射到容器的一个端口  #(主机端口8888:映射虚拟机容器中的端口8080)
#注:docker容器运行必须有一个前台进程, 如果没有前台进程执行,容器认为空闲,容器运行的命令如果不是那些一直挂起的命令(eg. 运行top,tail等),就会自行退出,
# docker run -d -it -p 8080:8080 tomcat /bin/bash
10、查看日志
# docker logs containerName/containerId

更多docker命令参考

3)、安装MySQL示例

拉取mysql镜像

# docker pull mysql:5.7.38

错误启动演示:

# docker run -d mysql:5.7.38
使用docker ps查看没有后台运行的容器
查看日志docker logs mysql容器的id 
Database is uninitialized and password option is not specified
    You need to specify one of the following:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_ALLOW_EMPTY_PASSWORD
    - MYSQL_RANDOM_ROOT_PASSWORD  这三个参数必须指定一个

正确启动演示:

# docker run  -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7.38   未做端口映射
# docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7.38   做了端口映射

其他带参高级操作:

# docker run --name some-mysql -v /conf/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag   
-v /conf/mysql:/etc/mysql/conf.d   把主机的/conf/mysql挂载到mysql容器的/etc/mysql/conf.d文件夹里面,改mysql的配置文件放在/conf/mysql下

# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
指定mysql的一些参数 

九、SpringBoot与数据访问

1、JDBC

导入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2.0+版本的SpringBoot默认使用的数据源:com.zaxxer.hikari.HikariDataSource

数据源的相关配置都在DataSourceProperties.class中

自动配置原理:

和数据源有关的配置都在这个包下:

org.springframework.boot.autoconfigure.jdbc

1)、参考DataSourceConfiguration.class,根据配置创建数据源,默认使用Hikari连接池,可以使用spring.datasource.type指定自定义的数据源类型;

2)、SpringBoot默认支持以下数据源

org.apache.tomcat.jdbc.pool.DataSourceHikariDataSourceBasicDataSource

3)、也可以使用以下方法自定义数据源类型


@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(name = {"spring.datasource.type"})
static class Generic {
    Generic() {
    }

    @Bean
    DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

使用DataSourceBuilder来创建数据源,其中有一个build()方法,通过BeanUtils进行反射创建相应的type的数据源,并且绑定相关属性

4)、还有一处配置:DataSourceAutoConfiguration ,代表数据源的自动配置。

由于2.0版本以上的SpringBoot源码变动,DataSourceAutoConfiguration 中没有DataSourceInitializer 。不过,有一个DataSourceInitializerInvoker,是一个ApplicationListener监听器,其中则有。

其作用:

2.0版本以上是
1createSchema():运行建表语句
2initSchema():运行插入数据的SQL语句
二者都调用runScripts()方法,但2.0版本以上要在配置文件中加入
# 初始化模式
spring.datasource.initialization-mode=always
其有三个值:always为始终执行初始化,embedded只初始化内存数据库(默认值),如h2等,never为不执行初始化
是应为2.0版本有个isEnabled()方法,用来判断类型的。
2.0版本以下:
1runSchemaScripts():运行建表语句
2runDataScripts():运行插入数据的sql语句。

默认只需将文件命名为:

schame-*.sql、data-*.sql
默认规则:schame.sql或schema-all.sql
如果不是用默认规则,则需要在配置文件中使用spring.datasource.schema=xxx
指定sql脚本文件的路径,如spring.datasource.schema=classpath:department.sql

在运行建表语句时,先通过getScripts()方法获取sql脚本文件。可以通过resources指定文件的位置,如果找不到,就去找类路径下找fallback,而fallback就是schema

5)、操作数据库:自动配置了jdbcTemplate操作数据库

注:每次启动项目都会重新执行建表语,可以删除sql脚本文件,也可在配置文件中将spring.datasource.initialization-mode改为never

2、整合Druid数据源

添加Druid数据源依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.8</version>
</dependency>

给容器中注入Druid数据源:

@Configuration
public class DruidConf {
   
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")//绑定数据元的其他配置
    public DataSource druid(){
        return new DruidDataSource() ;
    }

    /**
     * 配置druid监控
     * 配置一个管理后台的Servlet:statViewServlet
     * @return
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String,Object> map = new HashMap<>();
        map.put("loginUsername", "admin");
        map.put("loginPassword", "123456");
        map.put("allow", ""); //""或null为访问所有
        map.put("deny","192.168.2.175" );//拒绝访问
        //为此注册设置初始化参数。调用此方法将替换任何现有的初始化参数。
        bean.setInitParameters(map);
        return bean;
    }

    /**
     * 配置一个监控Web的filter:webStatFilter
     * @return
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
        Map<String,Object> map = new HashMap<>();
        //释放这些请求
        map.put("exclusions", "*.js,*.css,/druid/*");
        bean.setInitParameters(map);
        //拦截所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }
}

application.properties

# 应用名称
spring.application.name=spring-boot-data-jdbc
# 应用服务 WEB 访问端口
server.port=8080
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 初始化模式
spring.datasource.initialization-mode=never
# 数据库脚本文件路径
spring.datasource.schema=classpath:department.sql
# 数据库连接地址
spring.datasource.url=jdbc:mysql://192.168.2.175:3306/jdbc?serverTimezone=UTC
# 自定义数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j2
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.useGlobalDataSourceStat=true  
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

#是否启用StatViewServlet(监控页面)默认值为false(考虑到安全问题默认并未启动,如需启用建议设置密码或白名单以保障安全)
#spring.datasource.druid.stat-view-servlet.enabled=true
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=root

Druid监控

3、整合MyBatis

1)、引入MyBatis依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>

2)、注解版

image-20220730080619895

注解版不能配置xxx.xml,所以注解版的配置需要使用MybatisAutoConfiguration类中的ConfigurationCustomizer定制一个MyBatis配置类加入到容器中

@Configuration
public class MybatisConf {
    @Bean
    public ConfigurationCustomizer configurationCustomizer(){
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true);//开启驼峰命名法
            }
        };
    }
}

注:如果mapper包下的mapper太多,可以在SpringBoot启动类上使用@MapperScan注解扫描mapper包。

3)、配置文件版

创建一个Mybatis全局配置文件:mybatis-conf.xml

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

在SpringBoot配置文件application.properties中增加以下配置:

# 指定MyBatis全局配置文件路径
mybatis.config-location=classpath:mybatis/mybatis-conf.xml
#指定mapper映射文件路径
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

创建mapper.xml映射文件:

<?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">
<mapper namespace="com.atcpl.springboot.mapper.Mappers">
    <select id="queryEmpById" resultType="com.atcpl.springboot.entity.Employee">
        select * from employee where id = #{id}
    </select>
</mapper> 

注: ,namespace的值为mapper接口文件的全类名。

<select id=" “>,id的值为mapper接口中抽象方法的方法名。

更多配置参考Mybatis官方文档

4、整合SpringData JPA

1)、SpringData简介

image-20220729112327635

2)、整合SpringData JPA

jpa:ORM(Object Relational Mapping)

步骤:

①:编写一个实体类(bean)和数据表进行映射,并且配置好映射关系

/**
 * 使用JPA注解配置映射关系
 */
@Entity //告诉JPA这是一个实体类 (与数据表映射的类)
@Table(name = "user_table") // @Table指定和哪个数据表对应;默认是实体类类名小写user
public class User {
    @Id // @Id 主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
    private Integer id;
    @Column(name = "email",length = 25) // 这是和数据表对应的一个列; name指定字段名  length指定长度
    private String email;
    @Column  //字段名默认为属性名
    private String userName;
}

②:创建一个Dao接口来操作实体类对应的数据表(Repository)

/**
 * 这是一个Dao接口,来操作实体类对应的数据表(Repository)
 * JpaRepository<T,ID> :
 * 泛型T:代表要操作的实体类
 * 泛型ID:实体类主键
 */
public interface UserRepository extends JpaRepository<User,Integer> {

}

③:JPA的基本配置

spring:
  jpa:
    hibernate:
      # 更新或者创建数据表结构
      ddl-auto: update
    # 在控制台显示sql
    show-sql: true

所有的JPA配置都在JpaProperties.class类中。

findOne()与findById():

调用findByid方法,返回实体类的Optional,再调用isPresent进行判断是否不为空,若是就调用get获取对象,然后判断对象是否存在,删除

Optional<Permission> permissions = permissionDao.findById(id);
if (permissions.isPresent()){
    Permission permission = permissions.get();
    if (permission != null){
        permissionDao.delete(permission);
    }
}

调用findById()方法和orElse(null)方法,获取实体类对象,然后再判断实体类是否为空

permissionDao.findById(id).ifPresent(permission -> {
    if (permission != null){
        permissionDao.delete(permission);
    }
});

十、启动配置原理

几个重要的事件回调机制

配置在META-INF/spring.factories

ApplicationContextInitializer

SpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

CommandLineRunner

启动原理:

1、创建SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
    	//保存主配置类
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    	//判断当前应用是否为Web应用
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    	//从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	//从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	//从多个配置类中找到有main方法的主配置类
		this.mainApplicationClass = deduceMainApplicationClass();
	}


private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

2、调用run()方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //从类路径META-INF/spring.factories下获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的获取SpringApplicationRunListener.starting()方法
    listeners.starting();
    try {
        //封装命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
        configureIgnoreBeanInfo(environment);
       	//打印banner图,就是控制台中那个大Spring
        Banner printedBanner = printBanner(environment);
        //创建ApplicationContext;决定创建web的ioc还是普通的ioc
        context = createApplicationContext();
        //做异分析报告
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        //准备上下文环境;将environment保存到ioc中;而且applyInitializers()方法回调之前保存的所有的ApplicationContextInitializer的initialize方法;listeners.contextPrepared(context)则回调所有的SpringApplicationRunListener的contextPrepared();
        //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
        refreshContext(context);
        //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
		//ApplicationRunner先回调,CommandLineRunner再回调
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    //整个SpringBoot应用启动完成以后返回启动的ioc容器;
    return context;
}

3、事件监听机制

配置在META-INF/spring.factories

ApplicationContextInitializer

public class HelloApplicationContextInitializer implements
    ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
    }
}

SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
    //必须有的构造器
    public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
    }
    @Override
    public void starting() {
    	System.out.println("SpringApplicationRunListener...starting...");
    }
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
    	Object o = environment.getSystemProperties().get("os.name");
    	System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
    	System.out.println("SpringApplicationRunListener...contextPrepared...");
    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
    	System.out.println("SpringApplicationRunListener...contextLoaded...");
    }
    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception){
   		System.out.println("SpringApplicationRunListener...finished...");
    }
}

配置(META-INF/spring.factories)

org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

CommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
    	System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

十一、自定义Starter

starter:又叫场景启动器

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

2、如何编写自动配置

@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF/spring.factories

3、模式

启动器只用来做依赖导入;

专门来写一个自动配置模块;

启动器依赖自动配置;别人只需要引入启动器(starter)

启动器命名规约

  • 官方命名空间
    • 前缀:“spring-boot-starter-”
    • 模式:“spring-boot-starter-模块名”
    • 如:spring-boot-starter-web、spring-boot-starter-jdbc
  • 自定义命名空间
    • 后缀:“-spring-boot-starter”
    • 模式:模块-spring-boot-starter
    • 如:mybatis-spring-boot-starter

即:自定义启动器名-spring-boot-starter

xtLoaded…");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception){
System.out.println(“SpringApplicationRunListener…finished…”);
}
}


**配置(META-INF/spring.factories)** 

```properties
org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

CommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
    	System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

十一、自定义Starter

starter:又叫场景启动器

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

2、如何编写自动配置

@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF/spring.factories

3、模式

启动器只用来做依赖导入;

专门来写一个自动配置模块;

启动器依赖自动配置;别人只需要引入启动器(starter)

启动器命名规约

  • 官方命名空间
    • 前缀:“spring-boot-starter-”
    • 模式:“spring-boot-starter-模块名”
    • 如:spring-boot-starter-web、spring-boot-starter-jdbc
  • 自定义命名空间
    • 后缀:“-spring-boot-starter”
    • 模式:模块-spring-boot-starter
    • 如:mybatis-spring-boot-starter

即:自定义启动器名-spring-boot-starter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值