SpringBoot笔记
SpringBoot官方文档
@ConfigurationProperties、@PropertySource、@ImportResource的区别
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性与配置文件中的相关属性进行绑定;
如:@ConfigurationProperties(prefix = "person") 就是将配置文件中前缀为person下边的属性与该注解所在类中的属性进行绑定
@PropertySource:加载指定的配置文件
如:@PropertySource("classpath:person.properties")
@ImportResource:导入Spring的配置文件xxx.xml,让配置文件中的内容生效;
如:@ImportResource(location={"classpath:bean.xml"})(作用在启动类上)
@Configuration:指明当前类是一个配置类;用来代替之前的Spring配置文件.xml
约定文件名:application-{profile}.properties 使用spring-profiles.active={profile}激活对应的环境
application-dev.properties
application-prod.properties
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扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定的Bean |
@ConditionalOnMissingBean | 容器中不存在指定的Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是Web环境 |
@ConditionalOnNotWebApplication | 当前不是Web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置类必须在一定条件下才能生效
故该怎么知道哪些自动配置类是否生效?
只需在配置文件中开启debug=true 来让控制台打印自动配置报告
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;
以后开发的时候,日记方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;
给系统里面导入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后,配置文件还是使用日志实现框架自己本身的配置文件
SpringBoot使用的是slf4j+logback,但其他框架本身也可能在使用别的日志框架,如Spring(commons-logging)、Hibernate(jboss-logging)、Mybatis…故需要统一使用slf4j进行输出
如何让系统中的日志都统一到slf4j:
1.将系统的中其他日志框架先排出去
2.用中间包来替换原有的日志框架
3.再导入slf4j其他的实现
总结:
1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录
2)、SpringBoot把其他的日志都替换成了slf4j
3)、中间替换包(log4j-to-slf4j、jul-to-slf4j)
一句话:SpringBoot能适应所有的日志,而且底层使用slf4j的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉。
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
void contextLoads() {
//日志的级别: trace
logger.trace("这是trace日志");
logger.debug("这是debug日志");
logger.info("这是info日志");
logger.warn("这是warn日志");
logger.error("这是error日志");
}
%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=
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<property name="LOG_HOME" value="/app/log" />
<property name="appName" value="atguigu-springboot">property>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
layout>
appender>
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${appName}.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.logfileNamePattern>
<MaxHistory>365MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%npattern>
layout>
appender>
<logger name="com.atguigu" level="debug" />
<logger name="org.springframework" level="debug" additivity="false">logger>
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
root>
configuration>
### 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
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 都是在静态资源文件下找。
SpringBoot推荐Thmeleaf,功能强大,语法简单。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
@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}:
Name: Sebastian.
Surname: Pepper.
Nationality: Saturn.
Message Expressions: #{...}:获取国际化内容
Link URL Expressions: @{...}:定义URL;
@{/order/process(execId=${execId},execType='FAST')}
Fragment Expressions: ~{...}:片段引用表达式
...
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: _
Spring Boot Reference Guide
Spring Boot自动配置好了SpringMVC
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
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).
Automatic registration of MessageCodesResolver
(see below).定义错误代码生成规则
Static index.html
support.静态首页访问
Custom Favicon
support (see below).
Automatic use of a ConfigurableWebBindingInitializer
bean (see below).
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
.
编写一个配置类(@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的自动配置和我们自己扩展的自动配置都会起作用;
SpringBoot对SpringMVC的自动配置不需要了,所有的都是自己配置的;只需要在配置类上添加@EnableWebMvc注解
模式:
1)SpringBoot在自动配置很多组建的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户自己配置的,如果没有才自动配置;如果有写组件有多个(如:ViewResolver)将用户配置的和默认的组个起来;
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>
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;
}
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 myException_1(Exception e){
// Map 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改变需要返回的内容
SpringBoot默认使用内嵌式Tomcat.
①:修改和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);
}
};
}
由于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;
}
默认支持:
Tomcat(默认使用):
Jetty:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcatartifactId>
<groupId>org.springframework.bootgroupId>
exclusion>
exclusions>
dependency>
<dependency>
<artifactId>spring-boot-starter-jettyartifactId>
<groupId>org.springframework.bootgroupId>
dependency>
dependency>
Undertow:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcatartifactId>
<groupId>org.springframework.bootgroupId>
exclusion>
exclusions>
dependency>
<dependency>
<artifactId>spring-boot-starter-jettyartifactId>
<groupId>org.springframework.bootgroupId>
dependency>
dependency>
嵌入式Servlet应打成Jar包
优点:简单、便捷
缺点:默认不支持JSP、优化定制比较复杂
外置的Servlet容器:外面安装Tomcat 应用打成War包
步骤:
1、创建一个War项目
2、将嵌入式Tomcat指定为provided
<dependency>
<artifactId>spring-boot-starter-tomcatartifactId>
<groupId>org.springframework.bootgroupId>
<scope>providedscope>
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容器。
Docker是一个开源的应用容器引擎;
Docker支持将软件编译成一个镜像,然后在镜像中配置好各种软件,将镜像发布出去,其他使用者可以直接使用这个镜像。运行中的这个镜像称为容器,容器启动是非常快的。
docker主机(host):安装了Docker程序的机器(Docker直接安装在操作系统上)
docker客户端(Client):连接docker主机进行操作
docker仓库(Registry):用来保存各种打包好的软件镜像
docker镜像(Images):软件打包好的镜像;放在docker仓库中
docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用
使用Docker的步骤:
1)、安装Docker
2)、去Docker仓库找到这个软件对应的镜像
3)、使用Docker运行这个镜像,这个镜像就会生成一个Docker容器
4)、对容器启动的停止就是对软件的启动停止
1、VMWare、VirtualBox(轻量)
2、导入虚拟机文件
3、双击启动导入的虚拟机(用户名:root 密码:123456)
4、使用客户端连接Linux服务器进行命令操作
5、设置虚拟机网络 (桥接网络>根据实机选择网络选择界面名称>接入网线)
6、重启虚拟机网卡
service network restart
7、查看虚拟机IP地址
ip addr
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.
操作 | 命令 | 说明 |
---|---|---|
检索 | docker search xxx 如 docker search mysql | 与docker hub官网一样,可以检索镜像的信息,如镜像的Tag |
拉取 | docker pull 镜像名 : tag | tag是可选的,tag表示标签,多为软件的版本,默认是latest |
列表 | docker images | 查看本地所有镜像 |
删除 | docker rmi image-id | 删除指定的本地镜像 |
docker hub官网搜索镜像
软件镜像(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命令参考
拉取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的一些参数
导入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
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
添加Druid数据源依赖:
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.8version>
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监控
1)、引入MyBatis依赖
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
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
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映射文件:
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接口文件的全类名。