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环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置类必须在一定条件下才能生效
故该怎么知道哪些自动配置类是否生效?
只需在配置文件中开启debug=true 来让控制台打印自动配置报告
六、Spring Boot与日志
1.市面上常用的日志框架:
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…
日志门面(日志的抽象层) | 日志实现 |
---|---|
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");
}
}
每一个日志的实现框架都有自己的配置文件,使用slf4j后,配置文件还是使用日志实现框架自己本身的配置文件
2 >遗留问题
SpringBoot使用的是slf4j+logback,但其他框架本身也可能在使用别的日志框架,如Spring(commons-logging)、Hibernate(jboss-logging)、Mybatis…故需要统一使用slf4j进行输出
3.SpringBoot日志间的关系
如何让系统中的日志都统一到slf4j:
1.将系统的中其他日志框架先排出去
2.用中间包来替换原有的日志框架
3.再导入slf4j其他的实现
总结:
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
andBeanNameViewResolver
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">
© 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">
© 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>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
6.错误处理机制
1、SpringBoot默认的错误处理机制
1)、默认:浏览器返回一个默认的网页
2)、客户端会返回 Json格式数据
参照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支持将软件编译成一个镜像,然后在镜像中配置好各种软件,将镜像发布出去,其他使用者可以直接使用这个镜像。运行中的这个镜像称为容器,容器启动是非常快的。
2、核心概念
docker主机(host):安装了Docker程序的机器(Docker直接安装在操作系统上)
docker客户端(Client):连接docker主机进行操作
docker仓库(Registry):用来保存各种打包好的软件镜像
docker镜像(Images):软件打包好的镜像;放在docker仓库中
docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用
使用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 镜像名 : tag | tag是可选的,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.DataSource、HikariDataSource、BasicDataSource
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版本以上是
1、createSchema():运行建表语句
2、initSchema():运行插入数据的SQL语句
二者都调用runScripts()方法,但2.0版本以上要在配置文件中加入
# 初始化模式
spring.datasource.initialization-mode=always
其有三个值:always为始终执行初始化,embedded只初始化内存数据库(默认值),如h2等,never为不执行初始化
是应为2.0版本有个isEnabled()方法,用来判断类型的。
2.0版本以下:
1、runSchemaScripts():运行建表语句
2、runDataScripts():运行插入数据的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)、注解版
注解版不能配置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简介
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