一、springBoot入门
springBoot Hello world
1、创建一个maven工程
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.2</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2、写一个启动类
@SpringBootApplication
public class HelloSpringBoot {
public static void main(String[] args) {
SpringApplication.run(HelloSpringBoot.class, args);
}
}
3、写一个controller
@Controller
public class Hello {
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "hello world";
}
}
4、简化部署
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
这个插件可以将这个应用打成jar包直接使用java -jar执行
5、hello world 探究
从这个启动类注解开始
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
1、@SpringBootApplication头上的@SpringBootConfiguration
标注在类上面表示为springboot的配置类
他也属于spring中的@Configuration也是一个组件
2、@SpringBootApplication头上的@EnableAutoConfiguration
告诉springboot帮我们自动配置,开启自动配置
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
1、@EnableAutoConfiguration头上的@AutoConfigurationPackage
会导入一个AutoConfigurationPackages.Registrar.class组件;这个组件会将配置类(标有 @SpringBootApplication)的所在的包及其子包里面的所有组件扫描到spring容器中
2、@EnableAutoConfiguration头上的@Import(AutoConfigurationImportSelector.class)
会帮我们导入一个AutoConfigurationImportSelector组件,这个组件的作用是,将所有需要导入的组件全类名返回;这些组件会被添加到容器中,会给容器中添加好多自动类配置类(XXXConfiguration),就是给容器中导入这个场景所需要的一些组件
例如:
springboot怎么找到的这些自动配置类呢?
用这段代码找List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class;,
getBeanClassLoader());
springboot在启动时候会从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration.class的值,将这些值作为自动配置类导入到容器中,各个框架的自动配置类就生效了,帮我们进行自动配置工作
二、配置文件
1、@ConfigurationProperties用法
@ConfigurationProperties(prefix = "配置文件中的属性前缀")//默认从全局配置文件中获取值
@Component
public class User {
private String name;
private String password;
private int id;
get set
}
我们还可以使用@EnableConfigurationProperties(User.class)这个注解来注册User这个配置绑定,这样的user配置绑定类就不需要写@Component了;@EnableConfigurationProperties这个会将user导入到容器中
2、@Value和@ConfigurationProperties区别
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量导入配置文件中的属性 | 一个个的指定 |
松散语法 | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
3、@PropertySource()和@ImportResource()
@PropertySource()可以加载指定的配置文件
@ImportResource()导入spring配置文件,让配置文件里面的内容是生效,也就是配置文件之间相互导入
4、@Profile
1、多Profile文件
我们在主配置文件编写的时候,文件名可以是 application-{Profile}.properties\yml
默认使用 application.properties的配置
2、激活指定的 application-{Profile}.properties文件
-
在配置文件中指定spring.profiles.active=dev
-
还可以使用命令行
-
我们还可以使用虚拟机参数
3、yml支持多文档块
#第一块文档
server:
port: 8080
spring:
profiles:
# 激活第三块文档
active: prod
---
#第二块文档
server:
port: 8081
spring:
profiles: dev
---
#第三块文档
server:
port: 8082
spring:
profiles: prod
5、配置文件加载位置及其顺序
springboot启动时会扫描一下位置的application.yml或.properties作为springboot的默认配置文件
1、file:./config/
2、file:./
3、classpath:/config
4、classpath:/
注:
- 这是file目录
- 这是classpath目录
以上是优先级从高到低的顺序,所有位置的文件都会被加载。高优先级会覆盖低优先级的文件的内容
我们可以通过配置spring.config.location来改变默认配置
项目打包好后,我们可以使用命令行参数的形式,启动项目时候去硬盘加载指定的配置文件,指定的配置文件和默认的配置文件会起到互补配置的作用
6、自动配置原理
1、springBoot启动时加载主配置类,主配置类上面标有@SpringBootApplication上面标有 @EnableAutoConfiguration
2、@EnableAutoConfiguration上面有一个@AutoConfigurationPackage注解
-
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {} static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //这个作用是利用注解元信息拿到我们启动类的包名 //拿到包名后就可以扫描包开始注册里面的组件了 register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); }
3、@EnableAutoConfiguration作用
-
利用@Import(AutoConfigurationImportSelector.class)给容器中导入一些组件
-
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, getBeanClassLoader());//扫描所有jar包类路径下的META-INF/spring.factories文件中得到以EnableAutoConfiguration.class为key的值
-
将类路径下的META-INF/spring.factories文件中配置的所有的EnableAutoConfiguration的值加入到了容器中
-
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
每一个这样的XXXAutoConfiguration类都是容器中的一个组件了,springboot用他们来做自动配置
-
3、每一个配置类进行自己的自动配置功能
4、以HttpEncodingAutoConfiguration为例解释自动配置原理
//这是一个配置类
@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能来加载配置文件,将配置文件的值与ServerProperties绑定起来
//@ConfigurationProperties而且将标有这个注解的类加到容器中
@EnableConfigurationProperties(ServerProperties.class)
//spring底层的@Conditional注解,根据不同的条件来让这个配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目有没有这个类
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否有这个配置server.servlet.encoding=enabled;matchIfMissing = true如果没有那这个配置类也生效
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {}
-
所有在yml或properties中配置属性都在XXXProperties类中封装这;配置文件中能配置什么就参照这个类
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Server HTTP port. */ private Integer port; /** * Network address to which the server should bind. */ private InetAddress address;
5、springboot精髓
-
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
-
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
-
生效的配置类就会给容器中装配很多组件
-
只要容器中有这些组件,相当于这些功能就有了
-
定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
三、日志
1、日志框架
日志门面(日志的抽象层) | 日志实现 |
---|---|
JCL(Commons Logging)、SLF4j、jboss-logging | Log4j、Log4j2、Logback、JUL |
左边选一个门面,右边选一个实现
SLF4j、Log4j、Logback出自同一人,Log4j太老
所以我们选择SLF4j和Logback
spring底层默认的日志是JCL,springboot默认使用的是SLF4j和Logback
2、SLF4j使用
-
如何在系统中使用slf4j
给系统中导入slf4j和logbcak jar包就可以 日志记录的调用,不应该用实现类,我们应该调用抽象层的方法 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图示:
-
如何统一所有框架使用同一个日志实现呢?
我们需要在日志抽象层之前引入一层抽象层转换
1、将系统中其他日志框架先排除出去
2、用中间包来替换原有的日志框架
3、导入slf4j的其他实现
####3、springboot日志关系
总结
1、springboot底层使用的是slf4j + logback
2、springboot把其他其他日志都替换成了slf4j
3、如果我们引入了其他框架,一定要把这个框架自带的日志从pom中排除掉
springboot能适配所有的日志框架,而且底层使用的是slf4j + logback的方式。我们只需要在引入其他框架时候从pom中排除掉该框架使用的日志即可。
4、日志的使用
1、默认配置
springboot默认给我们配置好了日志
@SpringBootTest
class DemoApplicationTests {
Logger logger = LoggerFactory.getLogger(getClass());
//日志级别从低到高trace<debug<info<warn<error
// 调整日志级别打印出的日志会包含此级别及比它高的级别
@Test
void contextLoads() {
logger.trace("这是trace");
logger.debug("这是debug");
// springboot默认给我们开启info日志级别
logger.info("这是info");
logger.warn("这是warn");
logger.error("这是error");
}
}
-
日志输出的格式
%d表示时间 %thread表示线程名 %-5level 级别从左显示5个字符宽度 %{logger50} 表示logger名字最长50个字符,否则按照句点分割 %msg日志消息 %n换行 %-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
-
日志输出成文件
//1、如果不指定路径那springboot默认在项目下生成springboot.log日志文件 Logging.file //2、在当前磁盘的根目录下创建spring文件夹和log文件夹,使用springboot默认的文件名称(springboot.log) Logging.path=/spring/log
2、指定配置
Logging System | Customization |
---|---|
Logback | logback-spring.xml, logback-spring.groovy,logback.xml, or logback.groovy |
Log4j2 | log4j2-spring.xml or log4j2.xml |
JDK (Java Util Logging) | logging.properties |
四、Web开发
1、静态资源
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
改变默认的静态资源路径和请求
spring:
mvc:
#将访问静态资源的请求路径改成请求中加入res。来区别于其他请求
static-path-pattern: /res/**
resources:
#修改默认放置静态资源文件夹
static-locations: [classpath:/haha/]
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}
1、webjars
所有的/webjars/**,都去classpath:/META-INF/resources/webjars/ 找资源
webjars就是以jar包的方式引入静态资源;https://www.webjars.org/
2、放静态资源的文件夹
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
访问当前项目任何路径(/**)springboot都会去这个四个路径找静态资源
2、请求参数处理
1、rest使用与原理
-
@xxxMapping;
-
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
-
以前:**/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
-
现在: /user *GET-*获取用户 *DELETE-*删除用户 *PUT-*修改用户 *POST-*保存用户
-
核心Filter;HiddenHttpMethodFilter
-
用法: 表单method=post,隐藏域 _method=put
-
SpringBoot中手动开启
-
扩展:如何把_method 这个名字换成我们自己喜欢的。
Rest原理(表单提交要使用REST的时候)
-
表单提交会带上**_method=PUT**
-
请求过来被HiddenHttpMethodFilter拦截
-
请求是否正常,并且是POST
-
获取到**_method**的值。
-
兼容以下请求;PUT.DELETE.PATCH
-
原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
-
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用****requesWrapper的。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 这里找到请求对应的handle(也就是对应的controller那个方法来处理)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//HandlerMapping:处理器映射。/xxx->>xxxx
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。
所有的请求映射都在HandlerMapping中。
-
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
-
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
-
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
-
如果有就找到这个请求对应的handler
-
如果没有就是下一个 HandlerMapping
-
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
3、模板引擎Thymeleaf
常见的模板引擎:JSP 、Freemarker、Thymeleaf
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 = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
//我们只需要将HTML放到classpath:/templates/文件中thymeleaf就可以帮我们自动渲染了
-
导入thymeleaf名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
-
使用thymeleaf语法
<!DOCTYPE html > <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>成功</h1> <!--th:text将div里面的文件内容用hello取出来放这里--> <div th:text="${hello}"></div> </body> </html>
-
语法规则
- 表达式
Simple expressions:
#获取对象的属性、调用方法;使用一些内置对象;内置一些工具对象
Variable Expressions: ${...} 获取变量值
#选择表达式功能和${}一样;多了一点是配合th:object="${session.user}"来使用
Selection Variable Expressions: *{...}
#获取国际化内容
Message Expressions: #{...}
#定义URL 例如:<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
Link URL Expressions: @{...}
#片段引入表达式
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: _
4、SpringMVC自动配置
#springboot已经配置好了springmvc,一下是springboot对springMVC的默认配置
#自动配置了ViewResolver视图解析器;根据方法返回值得到视图对象(View)视图对象决定如何渲染(转发?重定向?)
#我们可以自己写一个视图解析器添加到容器中,springboot自己就会加入进来
• Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
#静态资源文件夹路径WebJars
• Support for serving static resources, including support for WebJars (covered later in this document).
#静态首页访问
• Static index.html support.
#自动注册了转换器(Converter)用于java类型转换,
#格式化器(Formatter)用于日期格式化 自己可以添加格式化转换器放到容器中就可以
• Automatic registration of Converter, GenericConverter, and Formatter beans.
#HttpMessageConverters是springMVC转换HTTP请求和响应的 例如将user转换成json
• Support for HttpMessageConverters (covered later in this document).
#定义错误代码生成规则
• Automatic registration of MessageCodesResolver (covered later in this document).
#初始化WebDataBinder(其实就是将请求数据转换为java对象)
• Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
五、错误处理机制
1、springboot默认错误处理机制
- 如果是浏览器默认效果返回一个错误页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ez3NVQqr-1625900819956)(F:\java框架学习笔记\springboot\图片\image-20210704185607526.png)]
- 如果是客户端访问错误默认给一个json数据
原理:
可以参照ErrorMvcAutoConfiguration这个类错误处理的自动配置类
ErrorMvcAutoConfiguration这个类在容器中添加了这些组件
1、DefaultErrorAttributes
2、BasicErrorController
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
//产生HTML数据;浏览器错误请求来到这里
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
//产生JSON数据;其他客户端错误请求来到这里1
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
3、ErrorPageCustomizer
@Value("${error.path:/error}")
private String path = "/error";
4、DefaultErrorViewResolver
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);
}
步骤:系统一旦出现4XX或5XX之类的错误,ErrorPageCustomizer就会生效,就会来到/error请求;就会被BasicErrorController处理
2、如何定制错误
1、定制错误页面
2、定制错误json
六、配置嵌入式Servlet容器
springboot默认使用的是嵌入式servlet容器(Tomcat)
问题:
-
那我们如何修改servlet容器相关配置呢?
1、修改server有关的配置
server: port: 8080
2、我们编写一个WebServerFactoryCustomizer也可以对servlet容器属性值修改
@Component public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(9000); } }
-
springboot能不能支持其他的servlet容器呢?
七、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>
</dependencies>