<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> 复制代码
@SpringBootApplication:这个注解表示这个类就是springBoot的主配置类,需要运行这个类的main方法来启动应用
这个注解其实是
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) 复制代码
其中:
@SpringBootConfiguration:这个配置代表配置类(区别去Spring中的配置文件,这里用配置类来代替,底层其实就是spring的@Configuration),其实它也就是一个组件
@EnableAutoConfiguration:开启自动配置功能
里面是**@AutoConfigurationPackage**
同时回头看,与enable同级的注解还有一个**@Import({AutoConfigurationImportSelector.class})**
那么这些个全类名String数组从何而来呢,
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); 复制代码
主要是这么一句 loadFactoryNames
代码,它呢,其实就是去类路径下面的META-INF/spring.factories里面获取EnableAutoConfiguration指定的值,然后返回作为配置组件的全类名
这个注解可以让配置文件中的属性绑定到当前类的属性里面
@ConfigurationProperties(prefix = "prefix") 复制代码
其中prefix指定配置文件中的前缀,以便于找到对应的属性
因为这个注解需要要求类是容器中的组件才能使用,所以需要给这个类加上**@Component**这个注解
此外,他还需要一个依赖来完成
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> 复制代码
上面这个注解是给一个类添加属性绑定,涉及到单个值的话,那么就是使用到了@Value这个属性来进行,它还支持SpEl
@Value("${value.value}") 复制代码
上面那个注解默认就是在全局配置文件里面读取前缀相同的属性进行绑定,如果属性不在全局的配置文件里面,那么它就束手无策了,那么这个注解就派上了用场
@PropertySource(value = {"classpath:xxx.properties"}) 复制代码
它可以为一个类导入一个专门的配置文件
这个注解是专门为了自己写的spring配置而生的,想要自己写的Spring配置生效就需要用它把配置文件导入
@ImportResource(locations = {"classpath:bean.xml"}) 复制代码
JSR303数据校验
这俩个都是spring boot的配置文件,那么啥是yml呢,它其实是yaml
yaml(YAML Ain't Markup Language)为了强调这种语言以数据做为中心,而不是以置标语言为重点
举个例子
1. server.port=8080 2. server: port: 8080 3. <server> <port>8080</port> </server> 复制代码
上面这种就是properties的语法格式,而后面这种就是yml的语法格式,第三种是正常xml的语法格式
数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号“-”来表示,map结构里面的key/value对用冒号“:”来分隔。样例如下:
house: family: name: Doe parents: - John - Jane children: - Paul - Mark - Simone address: number: 34 street: Main Street city: Nowheretown zipcode: 12345 复制代码
字串不一定要用双引号标识;
若有“”则表示可以转义字符串中的特殊符号,特殊符号还是特殊意思,'' 表示不会转义,即特殊符号不再含有意义
在缩排中空白字符的数目并不是非常重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用TAB字符);
允许在文件中加入选择性的空行,以增加可读性;
在一个档案中,可同时包含多个文件,并用“——”分隔;
选择性的符号“...”可以用来表示档案结尾(在利用串流的通讯中,这非常有用,可以在不关闭串流的情况下,发送结束讯号)。 ####单行缩写 YAML也有用来描述好几行相同结构的数据的缩写语法,数组用'[]'包括起来,hash用'{}'来包括。因此,上面的这个YAML能够缩写成这样:
house: family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] } address: { number: 34, street: Main Street, city: Nowheretown, zipcode: 12345 }
多个Profile版本控制多个配置文件
配置文件默认使用的是application.properties/yml
但是如果有多个配置文件可以使用不同的profile,格式是:
application-dev.properties application-prod.properties
像这样的配置文件,如果需要它生效,那么就需要在默认主配置文件里面填写
spring.profiles.active=dev 复制代码
等号后面跟的是profile的名字,也就是配置文件-后面的名字
如果是yaml的格式的配置文件,可以通过 ---
来分割文档,spring.profiles指定文档块名称,spring.profiles.active激活
server: port: 8080 spring: profiles: active: dev --- server: port: 8081 spring: profiles: dev --- spring: profiles: prod 复制代码
还可以通过启动参数program arguments指定激活, --spring.profiles.active=xx
或者项目打包成为jar包的时候,通过java -jar运行的时候添加和上面相同的参数
又又或者,通过添加虚拟机参数,在VM options里面 -Dspring.profiles.active=xx
按照优先级从高到低:
1.当前项目根目录的/config下
2.当前项目根目录
3.类路径(resources下)的/config下
4.类路径(resources下)
优先响应优先级最高的,最高的配置生效
启动springboot的主类,然后启动了**@EnableAutoConfiguration 这个注解,这个注解在前面已经介绍过了,就是通过import然后通过 @Import({AutoConfigurationImportSelector.class})**来从META-INF/spring.factories导入相关的EnableAutoConfiguration类的自动配置类的全类名
@Configuration //spring默认的配置类注解,相当于之前用过的配置文件 @EnableConfigurationProperties(HttpEncodingProperties.class) //开启自动配置指定配置的属性类 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //@condition是spring底层的注解,表示符合某个条件这个类才会生效,这里表示如果应用是web应用那么这个配置类久生效 @ConditionalOnClass(CharacterEncodingFilter.class) //如果存在CharacterEncodingFilter这个过滤器,这个注解踩生效(这个过滤器是在spring中解决网页乱码问题的过滤器) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //这个表示在属性中,存在spring.http.encoding.enabled属性为true且若没有这个属性也默认生效 public class HttpEncodingAutoConfiguration { 复制代码
上面说到的这个 HttpEncodingProperties.class 这个类,其实就是一个属性的集合类
@ConfigurationProperties(prefix = "spring.http.encoding") public class HttpEncodingProperties { 复制代码
它的这个注解表示在配置文件中 spring.http.encoding 的属性就是它用来绑定属性的值
@Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } 复制代码
它通过xxxproperties.class获取属性值,然后在这里面进行复制,然后通过@Bean返回给容器
那么现在看起来其实已经明朗了,这里就是,
HttpEncodingAutoConfiguration -> HttpEncodingProperties.class -> 配置文件 复制代码
我们想要知道哪些属性,就找到这个自动配置类的properties.class这个类,里面就是它的所有属性了,然后再去配置文件里面修改就可以修改这个自动配置类的默认配置啦
上面涉及到一系列关于@Condition的注解,例如**@ConditionalOnWebApplication**,这个其实就是springboot为我们写的一些拓展注解,点开这个注解就可以看到:
@Conditional(OnWebApplicationCondition.class) 复制代码
其实就是那个 OnWebApplicationCondition 类,里面的match方法,返回值是true还是false,就决定了是否运行这个注解的类
debug=true 复制代码
这句话以后,运行应用在控制台就可以查看这个信息
首先我们需要区别一点,就是关于日志,它存在接口和实现俩种 接口: slf4j jcl jboss-logging 实现: log4j logback log4j2 jul
那么这些里面,springboot默认的搭配选择就是 slf4j + logback
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSal6e83-1577429397655)( i.imgur.com/wTrRepZ.png )]
这张图片就是slf4j与实现搭配的图片
再让我们看看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACvogTNd-1577429397656)( i.imgur.com/1tidme1.png )]
在springboot的日志系统中,已经包含了logback和slf4j,而且还具备其他日志接口转slf4j的jar包,这些包可以替换掉原来的日志接口 所以导入其他日志框架的时候,需要排除它自己的日志接口(),然后就可以正常使用
在SpringBoot里面使用日志非常简单
Logger logger = LoggerFactory.getLogger(getClass()); 复制代码
首先在需要使用日志的类里面声明这样一个logger 然后再需要打印的地方:
logger.trace("trace"); logger.debug("debug"); logger.info("info"); logger.warn("warn"); logger.error("error"); 复制代码
这是日志的五种级别,从上到下依次增大的级别,springboot里面默认是info级别,那么也就是说没有任何设置的情况下,前面俩种级别的日志你是不能再控制台查看的,那么如果想要设置那么就是再主配置文件里面:
logging.level.com.macky.springboot.start.original=trace 复制代码
通过level设置日志级别,而且level后面跟着的就是你的项目包名
配置文件里面还可以指定
#指定输出日志文件的名字,也可以直接指定路径名 logging.file=loggingfile.log 复制代码
这个表示,把日志内容输出到当前目录下的loggingfile.log文件中
#在当前磁盘根目录下创建这个文件夹,并且把日志输出到这个路径下的 logging.path=/spring/log 复制代码
值得一提的是 file 和 path同时存在的时候,是file生效,不过通常的用法都是只用path,然后文件名使用的是springboot默认的日志名称spring.log
还可以指定输出的格式
#在控制台输出的格式 logging.pattern.console= #在文件输出的格式 logging.pattern.file= 复制代码
如果你想给logback添加更多自己的配置,那么,其实只要在类路径下(Resources)添加一个logback.xml或者是logback-spring.xml文件,里面写上你的配置,容器会自动帮你读取这个文件
上面俩个文件,没有spring后缀的那个直接由日志框架识别读取,而有后缀的将会被springboot识别,这样的好处是可以通过profile在不同环境起作用而使用不同的配置,所以官方推荐使用的是logback-spring.xml,在配置文件里面添加:
<springProfile name="dev"> ... </springProfile> 复制代码
如果有需要让我们切换日志框架,其实也是可以实现的
按照上面那张slf4j的日志的架构图进行
首先需要排除需要的日志框架的包,然后导入相应的日志的接口包和实现包
或者直接简单除暴,排除logging的starter,启用log4j2的starter
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache() .getCachecontrol().toHttpCacheControl(); if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry .addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)) .setCacheControl(cacheControl)); } String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations( this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)) .setCacheControl(cacheControl)); } } 复制代码
从上面这个 WebMvcAutoConfiguration 里面的 addResourceHandlers 类看来,我们需要去/webjars/**找东西的时候,其实就是映射classpath:/META-INF/resources/webjars/里面的资源
那么 webjars 其实是:jar方式的静态资源
而且webjar提供一整套关于静态文件的解决办法——直接通过依赖的方式进行导入
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.0</version> </dependency> 复制代码
这样导入依赖以后,其实就是放在/META-INF/resources/webjars/下面的
不仅可以看到静态文件路径,还可以看到 resourceProperties 这个类
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties 复制代码
这个类其实就是用来配置静态资源文件的,可以在配置文件通过spring.resources来配置
上面的代码里面还有一个**/****的匹配模式
也就是说,如果存在没有匹配的资源,那么就去
"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" "/" 当前项目根目录 复制代码
这么几个文件路径下面查找,这几个就叫做 静态资源文件夹
总之,代码中,静态资源的位置是通过 staticLocations 来指定的,所以我们可以在配置文件中修改这个属性来自己指定静态资源的位置
spring.resources.static-locations= 复制代码
首先需要注意俩点
由于上面俩个原因,这里 不支持jsp,所以我们需要 其他模板引擎,springboot推荐的是 thymeleaf
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 复制代码
老样子通过autoConfiguration找到 ThymeleafProperties 这个类
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html"; 复制代码
可以看到的是,默认前缀 classpath:/templates/ 和默认后缀 .html
通过这个就可以明白了Thymeleaf的文件是放在哪个位置的
也就是说,如果controller返回的是index 那么就会去找classpath:/templates/index.html文件
首先我们可以给html文件添加一个标签
加上这个可以添加代码提示
然后看到这样一个代码
可以看到th:xxx的语法结构,这个th后面可以跟任意html代码,并且可以通过各种类似于**${}**取值然后讲原来的html值替代
上面说到了各种类似于**${}**,那么也就是其实这种表达式不止这一种:
${}:(其实就是OGNL)
*{}: (变量的选择表达式)
#{}
:
@{}:
~{}:
[[]] 相当于text [()]相当于utext
表示公共部分
三种引用方式
<body> ... <div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div> </body> 复制代码
三种方式分别的效果
<body> ... <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> </body> 复制代码
可以看的出来,第一个多了一层,中间是原始的,最后一个少了一层父标签
而且不仅可以使用 模板名::片段名的方式 还可以使用 模板名::选择器
如果我们引入的片段需要改变里面的一些东西(比如引入toolBar默认高亮需要动态配置的时候,那么我们就可以通过这个参数来设置)
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
Support for serving static resources, including support for WebJars (see below).
Automatic registration of Converter, GenericConverter, Formatter beans .
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).
springboot在配置很多组件的时候,只需要直接写一个组件。容器会自动加载
@Configuration public class SpringMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/macky").setViewName("success"); } } 复制代码
这个注解如果标注在自己的配置类上面,那么就将关闭springmvc的自动配置,由我们自己全面接管springmvc的配置,配置就如同我们使用ssm框架的时候配置mvc使用
这是为啥呢!
@Import({DelegatingWebMvcConfiguration.class})
再进去是
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { 复制代码
注意这个WebMvcConfigurationSupport类
让我回头看WebMvcAutoConfiguration这个自动配置类
它的注解里面有一个
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
sprintboot下的国际化设置
sprint.message.i18n.login
th:text="#{login.tip}"
####之所以可以实现国际化,就是因为Locale,springboot默认的一个localeResolver就是可以通过请求头获取当前区域的locale,然后修改语言,如果我们需要自己通过点击按钮设置语言,那么就需要先把这个默认的替代掉
所以这里就自己写一个LocaleResolver
public class OwnLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String la = httpServletRequest.getParameter("la"); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(la)){ String[] split = la.split("_"); locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } 复制代码
通过请求url的参数,通过前端模板按钮的链接跳转
<a class="btn btn-sm" th:href="@{/index(la='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/index(la='en_US')}">English</a> 复制代码
为了让我们的Resolver确实生效,前往config文件里面通过@Bean将他添加到容器中
@Bean public LocaleResolver localeResolver(){ return new OwnLocaleResolver(); } 复制代码