##Ⅰ Hello World小应用内部小解
###一、POM文件
####1.父项目
-
pom.xml中的
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
-
它的父项目是
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.5.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
-
它是来管理spring boot应用里面所有的依赖版本 -> Spring boot 版本仲裁中心
-
所以以后导入依赖就不需要写版本
org.springframework.boot spring-boot-starter-web
####2.导入依赖 -
这里注意到Spring-boot-starter 这个就是SpringBoot场景启动器 他其实就是帮助我们导入了web模块所需要的所有的依赖
-
每个starter其实就是若干个依赖的集合,springBoot为我们准备了各种各样的starter,在我们需要的时候,只要导入相应的场景来使用
###二、主程序类(程序入口)
####1.注解
@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**
- 再进去是 @Import({Registrar.class})
- 这里其实就是给容器导入一个组件,组件由Registrar.class决定
- 而它其实就是获取当前类(主配置类)所在的包,然后再包下的所有类及其子包内的类全部加载到容器当中
-
同时回头看,与enable同级的注解还有一个**@Import({AutoConfigurationImportSelector.class})**
- 同样的导入一个组件,这个组件决定导入哪些组件,返回全类名String数组
- 它会给容器导入很多自动配置类(xxxAutoConfiguration,以供给容器配置starter所需的所有组件)
- 免去了手动配置、注入功能部件
-
那么这些个全类名String数组从何而来呢,
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
-
主要是这么一句
loadFactoryNames
代码,它呢,其实就是去类路径下面的META-INF/spring.factories里面获取EnableAutoConfiguration指定的值,然后返回作为配置组件的全类名
##Ⅱ 快速构建
###使用https://start.spring.io/这个网址来构建中直接通过IDEA里面的spring initialize来创建
##Ⅲ 部分注解相关
###@RestController
- @Responsebody这个注解使用controller里面的方法,代表返回前台的数据直接使用json格式,可以使用于类上,代表这个类的controller的所有方法都返回json
- 那么,类上面也会有controller这个注解,所以就有一个简化的标签,就是**@RestController**
###@ConfigurationProperties
-
这个注解可以让配置文件中的属性绑定到当前类的属性里面
@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
-
上面那个注解默认就是在全局配置文件里面读取前缀相同的属性进行绑定,如果属性不在全局的配置文件里面,那么它就束手无策了,那么这个注解就派上了用场
@PropertySource(value = {"classpath:xxx.properties"})
它可以为一个类导入一个专门的配置文件
###@ImportResource
-
这个注解是专门为了自己写的spring配置而生的,想要自己写的Spring配置生效就需要用它把配置文件导入
@ImportResource(locations = {"classpath:bean.xml"})
###@Validated
JSR303数据校验
- 给类添加这个一个注解,那么就可以给它的属性添加校验条件
###@Bean
- 这个注解如果在方法上,那么就把这个方法的返回值添加到容器中,并且这个组件的默认名字是这个方法名
##Ⅳ 配置文件
###application.properties、application.yml
这俩个都是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的语法格式
###YAML的语法
####多行缩进
数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号“-”来表示,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
多个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下)
优先响应优先级最高的,最高的配置生效
###项目打包后的配置修改
- 如果项目打包以后还想要修改项目里面的默认参数,那么就可以在java -jar 的后面加上参数,可以直接是配置文件里面的属性,也可以是一个指定的配置文件,通过spring.config.location=xxx来指定
- 或者不用那个参数,直接把写好的配置文件放在打包完的那个目录下,直接java -jar就会自动重新加载配置
##Ⅴ 自动配置原理
启动springboot的主类,然后启动了**@EnableAutoConfiguration这个注解,这个注解在前面已经介绍过了,就是通过import然后通过@Import({AutoConfigurationImportSelector.class})来从META-INF/spring.factories导入相关的EnableAutoConfiguration类的自动配置类的全类名
###现在以HttpEncodingAutoConfiguration**为例子,解释自动配置类的原理
@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
上面涉及到一系列关于@Condition的注解,例如**@ConditionalOnWebApplication**,这个其实就是springboot为我们写的一些拓展注解,点开这个注解就可以看到:
@Conditional(OnWebApplicationCondition.class)
其实就是那个OnWebApplicationCondition类,里面的match方法,返回值是true还是false,就决定了是否运行这个注解的类
####那么,在spring.factories里面有众多的的自动配置类,由于@Condition注解,导致有些自动配置类不会生效,那么如何看到哪些生效哪些不生效呢,在主配置文件里面加入
debug=true
这句话以后,运行应用在控制台就可以查看这个信息
##Ⅵ 日志
首先我们需要区别一点,就是关于日志,它存在接口和实现俩种
接口:
slf4j jcl jboss-logging
实现:
log4j
logback
log4j2
jul
那么这些里面,springboot默认的搭配选择就是 slf4j + logback
这张图片就是slf4j与实现搭配的图片
再让我们看看
在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
##Ⅶ Web开发
###SpringBoot对静态资源的映射规则
@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=
- 接受的参数可以是一个数组
###前端模板引擎
首先需要注意俩点
- 我们的springboot是打包成jar,而不是war
- springboot内置tomcat
由于上面俩个原因,这里 不支持jsp,所以我们需要 其他模板引擎,springboot推荐的是thymeleaf
###Thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
###Thymeleaf语法
老样子通过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)
- 获取对象的属性、执行方法
- 使用内置的对象,类似于session
- 内置的工具对象,类似于strings
-
*{}: (变量的选择表达式)
- 其实它和上面这个是一样的,不过有一个拓展功能
- 那么就是再配合th:object使用的时候 可以不再写前面的对象名,直接使用属性
-
#{}
:- 获取国际化内容
-
@{}:
- 定义url链接
- @{http://localhost:8080/index(name=‘macky’,id=${NId})}
- 还可以直接不屑主机名和项目名
- @{/index(name=‘macky’,id=${NId})}
-
~{}:
- 片段引用表达式
-
[[]] 相当于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默认高亮需要动态配置的时候,那么我们就可以通过这个参数来设置)
###接下来介绍一下spring mvc的自动配置
####The auto-configuration adds the following features on top of Spring’s defaults:
-
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在配置很多组件的时候,只需要直接写一个组件。容器会自动加载
###如果扩展spring mvc的功能
@Configuration
public class SpringMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/macky").setViewName("success");
}
}
- 编写一个类,注解@Configuration,继承WebMvcConfigurerAdapter,在里面可以添加各种mvc组件
###关于@EnableWebMvc
这个注解如果标注在自己的配置类上面,那么就将关闭springmvc的自动配置,由我们自己全面接管springmvc的配置,配置就如同我们使用ssm框架的时候配置mvc使用
-
这是为啥呢!
- 这个注解点进去发现
@Import({DelegatingWebMvcConfiguration.class})
-
再进去是
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
-
注意这个WebMvcConfigurationSupport类
-
让我回头看WebMvcAutoConfiguration这个自动配置类
-
它的注解里面有一个
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
- 上面已经说过了Conditional注解的作用了,这里已经很明了了,其实就是存在了WebMvcConfigurationSupport这个类导致了自动配置类整个失效了
##Ⅷ web实战
###国际化
sprintboot下的国际化设置
- 编写国际化语言文件 xxx.properties
- springboot自动配置好了ResourceBundleMessageSource()
- 在这个MessageSource的自动配置类中,默认的配置文件的前缀是message,所以我们在这里可以指定自己的文件的前缀
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(); }
###登陆页面
正常简单的Controller九不需要经行记录了
-
这里记录一个登陆失败后的文本提示
-
表示的是,返回的字符串是否为空来决定要不要显示这个标签
-
添加一个登陆必要的拦截器
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object login = request.getSession().getAttribute("login"); if (login == null){ request.getSession().setAttribute("msg","没有权限请先登陆"); request.getRequestDispatcher("/index").forward(request, response); return false; }else { return true; } }
-
然后再配置类加上这个拦截器
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandleIntercept()).addPathPatterns("/**") .excludePathPatterns("/index","/","/user/login"); }
###内置servlet容器
Springboot采用的是内置的tomcat作为Servlet容器
我们可以通过俩种方式修改servlet的配置
- 类似于**server.port=**这样的配置
- 自己写一个类,通过配置container类来配置
####那么我们还需要关心的一个点就是 关于servlet filter listener
- 使用 xxxRegistrationBean