SpringBoot 笔记(雷神)
掺杂了一些相关知识
SpringBoot:
是一个便捷搭建基于Spring工程的脚手架,作用是帮助开发人员搭建大型的Spring项目。简化工程的配置,依赖管理;实现开发人员把时间集中在业务开发上。
优点:
– 快速创建独立运行的Spring项目以及与主流框架集成
– 使用嵌入式的Servlet容器,应用无需打成WAR包
– starters自动依赖与版本控制
– 大量的自动配置,简化开发,也可修改默认值
– 无需配置XML,无代码生成,开箱即用
– 准生产环境的运行时应用监控
– 与云计算的天然集成
spring-boot-starter-web:
spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件;
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
/** * @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/ @SpringBootApplication
public class HelloWorldMainApplication
{
public static void main(String[] args)
{ // Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@SpringBootApplication:
Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用;
@EnableAutoConfiguration:开启自动配置功能;
以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage:自动配置包
@Import(AutoConfigurationPackages.Registrar.class):
Spring的底层注解@Import,给容器中导入一个组件;导入的组件由 AutoConfigurationPackages.Registrar.class;
将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
EnableAutoConfigurationImportSelector:导入哪些组件的选择器;
将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件, 并配置好这些组件;
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;
原理:
通过这个SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader)方法;
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将 这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;
以前我们需要自己配置的东西,自动配置类都帮我们;
J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar;
默认生成的Spring Boot项目;
主程序已经生成好了,我们只需要我们自己的逻辑 resources文件夹中目录结构
static:保存所有的静态资源; js css images;
templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页 面);可以使用模板引擎(freemarker、thymeleaf);
application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
yml语法:。。。
调用配置文件内容注入
@Value获取值和@ConfigurationProperties获取值比较
@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定(松散语法) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value; 如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties
4、@PropertySource&@ImportResource&@Bean
@PropertySource:加载指定的配置文件
@PropertySource(value = {“classpath:person.properties”})//加载指定的配置文件,就不用把所有配置信息全放在全局配置文件下面
1.@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效; Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别; 想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上(也可以写在主配置类上,也可以是专门写的一个配置类)
@ImportResource(locations = {“classpath:beans.xml”})
导入Spring的配置文件让其生效
2.SpringBoot推荐给容器中添加组件的方式而不是上面@ImportResource;
推荐使用全注解的方式
1、配置类@Configuration------>Spring配置文件
2、使用@Bean给容器中添加组件
**
在配置文件中写标签添加组件
*/
@Configuration //指明当前类是一个配置类:就是来替代spring配置文件(.xml)
public class MyAppConfig {
//将方法的返回值添加到容器中,容器中这个组件默认的id就是方法名
@Bean
public HelloService helloService(){
System.out.println(“配置类给容器中添加组件”);
return new HelloService();
}
}
5.profile
1、多Profile文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
默认使用application.properties的配置;
3、激活指定profile
1、在配置文件中指定 spring.profiles.active=dev
#在yml文件中做类似于properties文件中profile文件的工作
#yml文档块模式
server:
port: 8084
spring:
profiles: prod
2、命令行:
生成打包的jar文件,在该文件目录下输入命令行
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
可以直接在测试的时候,配置传入命令行参数
3、虚拟机参数;
-Dspring.profiles.active=dev
6、配置文件加载位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文 件
–file:./config/
–file:./
–classpath:/config/
–classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
我们还可以通过spring.config.location来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默 认加载的这些配置文件共同起作用形成互补配置;
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
这样在实际开发中如果需要新的配置文件,就不需要重新打包文件,直接在命令行窗口下输入命令
就可在新的配置文件下执行。
7、外部配置加载顺序
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会 形成互补配置
1.命令行参数
所有的配置都可以在命令行上进行指定
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc 多个配置用空格分开; --配置项=值
(2,3,4,5用的少)
2.来自java:comp/env的JNDI属性
3.Java系统属性(System.getProperties())
4.操作系统环境变量
5.RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找(项目打包完成的jar包);
优先加载带profile
6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
再来加载不带profile
8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件
9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件
10.@Configuration注解类上的@PropertySource
11.通过SpringApplication.setDefaultProperties指定的默认属性
1、自动配置原理(spring boot1.xx 和2.xx内容不相同):
1)、SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
2)、@EnableAutoConfiguration 作用: 利用EnableAutoConfigurationImportSelector给容器中导入一些组件?
可以查看selectImports()方法的内容;
List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
此getCandidateConfigurations方法下
loadFactoryNames 此方法下
SpringFactoriesLoader.loadFactoryNames
此方法下的
loadSpringFactories()方法、
{
Enumeration urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”);
}
扫描所有jar包类路径下 META‐INF/spring.factories 把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器 中
总之:将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;
类似如下:每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;
每一个自动配置类进行自动配置功能;
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.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ 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.CassandraRepositoriesAutoConfiguration ,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration ,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration
,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfi guration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration, \ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
4)、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的
ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把 HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果 满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
@ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有这个类 CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器; @ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的 //machIfMissing=true即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值就会从ioc容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties)
{
this.properties = properties;
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件?
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;
}
总结:
根据当前不同的条件判断,决定这个配置类是否生效?
一但这个配置类生效:这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
精髓:
1)SpringBoot启动会加载大量的自动配置类
2)我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这 些属性的值;
xxxxAutoConfigurartion:自动配置类;
作用:给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
自动配置类必须在一定的条件下才能生效;
我们怎么知道哪些自动配置类生效;
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置 类生效;
META-INF文件夹下的
Positive matches:(自动配置类启用的)
Negative matches:(没有启动,没有匹配成功的自动配置类)
日志:
SpringBoot:底层是Spring框架,Spring框架默认是用JCL;‘
SpringBoot选用 SLF4j和logback;
slf4j是一个门面(抽象层),logback是其中的一种实现
每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;
2、遗留问题
a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx
统一日志记录,即使是别的框架和我一起统一使用slf4j进行输出?
如何让系统中所有的日志都统一到slf4j;
1、将系统中其他日志框架先排除出去;
2、用中间包来替换原有的日志框架;
3、我们导入slf4j其他的实现
SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要 把这个框架依赖的日志框架排除掉即可;
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void contextLoads() {
//System.out.println();
//日志的级别;
//由低到高
trace
日志就只会在这个级别以以后的高级别生效
logger.trace(“这是trace日志…”);
logger.debug(“这是debug日志…”);
//SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;
root 级别
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
#设置日志级别
logging.level.com.itguigu=trace
#不指定路径当前项目下生成springboot.log日志文件
#logging.file.name=springboot.log
#可以指定完整的路径
#logging.file.name=G:/springboot.log
#当前磁盘的根路径下创建Spring文件夹和里面的log文件夹:使用Spring.log作为默认文件,即生成在E盘根目录下的
logging.file.path=/spring/log
#在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
#在指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd}=[%thread]= %-5level === %logger{50}=== %msg%n
2、指定配置
给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了、
logback.xml:直接就被日志框架识别了;
logback-spring.xml(推荐使用这种):日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot 的高级Profile功能
例如:
可以指定某段配置只在某个环境下生效
#spring.profiles.active=dev
指的是开发环境下使用此配置 %d{yyyy‐MM‐dd HH:mm:ss.SSS} ‐‐‐‐> [%thread] ‐‐‐> %‐5level %logger{50} ‐ %msg%n 不在开发环境下使用此配置 %d{yyyy‐MM‐dd HH:mm:ss.SSS} ==== [%thread] ==== %‐5level %logger{50} ‐ %msg%nWeb开发
使用SpringBoot;
1)、创建SpringBoot应用,选中我们需要的模块; 2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 3)、自己编写业务代码;
自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx
xxxxAutoConfiguration:帮我们给容器中自动配置组件;
xxxxProperties:配置类来封装配置文件的内容
SpringBoot对静态资源的映射规则
1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;
webjars:以jar包的方式引入静态资源;
localhost:8080/webjars/jquery/3.3.1/dist/jquery.js
在访问的时候只需要写webjars下面资源的名称即可
org.webjars
jquery
3.3.1
“/**” 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
静态资源文件夹下有以下:
“classpath:/META‐INF/resources/”,
“classpath:/resources/”,
“classpath:/static/”,
“classpath:/public/”
“/”:当前项目的根路径
3)、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
网址直接输入localhost:8080/,默认找index页面
4)配置喜欢的图标
所有的 **/favicon.ico 都是在静态资源文件下找;
3、模板引擎
JSP、Velocity、Freemarker、Thymeleaf
由于springboot采用的是jar包的方式,用的也是嵌入式的Tomcat,不支持jsp,
纯静态的页面开发太麻烦,
SpringBoot推荐的Thymeleaf模板引擎;
语法更简单,功能更强大;
Thymeleaf是用来开发Web和独立环境项目的服务器端的Java模版引擎
Spring官方支持的服务的渲染模板中,并不包含jsp。而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVC的视图技术,及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,你只用关注Thymeleaf的语法即可。
Thymeleaf官方文档
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expressions-on-selections-asterisk-syntax
Simple expressions:(表达式语法)
Variable Expressions: ${…}:获取变量值;OGNL;
1)、获取对象的属性、调用方法
2)、使用内置的基本对象:
#ctx : the context object.
Selection Variable Expressions: *{…}:
选择表达式:和 在 功 能 上 是 一 样 ; 补 充 : 配 合 t h : o b j e c t = " {}在功能上是一样; 补充:配合 th:object=" 在功能上是一样;补充:配合th:object="{session.user}:
Name: Sebastian.
Surname: Pepper.
Nationality: Saturn.
[[${user}]]
4、SpringMVC自动配置
2、扩展SpringMVC
编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;
既保留了所有的自动配置,也能用我们扩展的配置;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
package springbootwebrestfulcrud.demo.config;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import static org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.*;
//使用WebMvcAutoConfigurationAdapter扩展SpringMvc的功能
//新版本被WebMvcConfigurer接口替换
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//浏览器发送atguigu到success页面
registry.addViewController("/atguigu").setViewName("success");//用什么方法跳转到什么页面
}
}
原理:
1)、WebMvcAutoConfiguration是SpringMVC的自动配置类
2)、在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
@Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); //从容器中获取所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
//一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;
@Override
// public void addViewControllers(ViewControllerRegistry registry) {
// for (WebMvcConfigurer delegate : this.delegates) {
// delegate.addViewControllers(registry);
// }
}
}
}
3)、容器中所有的WebMvcConfigurer都会一起起作用;
4)、我们的配置类也会被调用;
效果:SpringMVC的自动配置和我们的扩展配置都会起作用;
3、全面接管SpringMVC;
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了 我们需要在配置类中添加@EnableWebMvc即可;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc
@Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName(“success”);
}
}
原理:
为什么@EnableWebMvc自动配置就失效了;
1)@EnableWebMvc的核心
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
WebMvcAutoConfiguration.class
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //容器中没有这个组件的时候,这个自动配置类才生效,// 而自己写的配置类添加了EnableWebMvc
//最终添加了WebMvcConfigurationSupport,所以WebMvcConfigurationSupport的基本配置都没生效
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
4)、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;
5)、导入的WebMvcConfigurationSupport只是SpringMVC基本的功能;
2)、国际化
1)、编写国际化配置文件;
2)、使用ResourceBundleMessageSource管理国际化资源文件
3)、在页面使用fmt:message取出国际化内容
步骤:
1)、编写国际化配置文件,抽取页面需要显示的国际化消息
2)、SpringBoot自动配置好了管理国际化资源文件的组件;
@ConfigurationProperties(prefix = “spring.messages”) public class MessageSourceAutoConfiguration
{
/** * Comma‐separated list of basenames (essentially a fully‐qualified classpath
* location), each following the ResourceBundle convention with relaxed support for
//我们的配置文件可以直接放在类路径下叫messages.properties; 但是我们不是,所有需要在配置文件中设置文件路径 spring.messages.basename=
@Bean public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(this.basename)) {
//设置国际化资源文件的基础名(去掉语言国家代码的)
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(this.basename)));
}
if (this.encoding != null) {
messageSource.setDefaultEncoding(this.encoding.name());
}
messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
messageSource.setCacheSeconds(this.cacheSeconds);
messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
return messageSource;
3)、去页面获取国际化的值;
效果:根据浏览器语言设置的信息切换了国际化;
Signin Template for Bootstrap
效果:根据浏览器语言设置的信息切换了国际化;
原理:
国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象);
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = “spring.mvc”, name = “locale”)
public LocaleResolver localeResolver() {
if (this.mvcProperties
.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
默认的就是根据请求头带来的区域信息获取Locale进行国际化
4)、点击链接切换国际化
package springbootwebrestfulcrud.demo.component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
public class MyLocaleResolver implements LocaleResolver {
//区域信息解析器,写完需要添加在容器中才生效,在myMvcConfig中添加到配置中 Bean
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
//解析区域信息
String l = httpServletRequest.getParameter(“l”);//从httpServletRequest获取参数的值即l代表的值
Locale locale=Locale.getDefault();
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
Sign in© 2017-2018
中文 此处用index.html要注意,切换国际化的时候url地址会一起改变 English3)、登陆
开发期间模板引擎页面修改以后,要实时生效
1)、禁用模板引擎的缓存
spring.thymeleaf.cache=false
2)、页面修改完成以后ctrl+f9:重新编译;
© 2017-2018
中文 English为了防止重复提交表单,不用上面这种写法,通过重定向的方式返回页面,避免重复提交
@PostMapping(value = “/user/login”)
// @RequestMapping(value = “/user/login”,method = RequestMethod.POST)
public String login(@RequestParam(“username”)String username,
@RequestParam String password, Map
// 用RequestParam标注的属性一旦没有提交就报错
if(!StringUtils.isEmpty(username) && “123456”.equals(password)){
//登录成功
//return “dashboard”;
//为了防止重复提交表单,不用上面这种写法,通过重定向的方式返回页面,避免重复提交
return “redirect:/main.html”;//再配置文件中添加视图映射,转到dashboard.html
}else {
//登录失败
map.put(“msg”,“用户名密码错误”);
return “login”;
}
由于使用了重定向,直接输入url:http://localhost:8080/crud/main.html 就可直接访问到页面,跳过了登录检测的步骤,这不合理,需要添加拦截器进行登录检测
4)、拦截器进行登陆检查
拦截器
thymeleaf公共页面元素抽取
thymeleaf公共页面元素抽取
1、抽取公共片段
三种引入公共片段的th属性:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
引入方式
效果
第二种:通过选择器的方式
高亮设置:选择哪个按钮哪个高亮
7。错误处理机制
默认效果:
1)、浏览器,返回一个默认的错误页面
2)、如果是其他客户端,默认响应一个json数据
浏览器和客户端发送请求的请求头中的accept不一样
原理:
可以参照ErrorMvcAutoConfiguration;错误处理的自动配置;
步骤:
一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error 请求;就会被BasicErrorController处理;
1)响应页面;去哪个页面是由DefaultErrorViewResolver解析得到的;
1、DefaultErrorAttributes:
帮我们在页面共享信息;Model的值就是调用DefaultErrorAttributes中的方法
即页面能获取的信息就是通过DefaultErrorAttributes来显示的;
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里
@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;
}
2、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 = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
//去哪个页面作为错误页面;包含页面地址和页面内容
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
}
@RequestMapping
@ResponseBody //产生json数据,其他客户端来到这个方法处理;
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
3、ErrorPageCustomizer:
@Value("${error.path:/error}")
private String path = "/error"; 系统出现错误以后来到error请求进行处理;(web.xml注册的错误页面规则)
4、DefaultErrorViewResolver:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), 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) {
//默认SpringBoot可以去找到一个页面? error/404
String errorViewName = "error/" + viewName;
//模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) {
//模板引擎可用的情况下返回到errorViewName指定的视图地址
return new ModelAndView(errorViewName, model);
}
//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
return resolveResource(errorViewName, model);
}
步骤:
一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被**BasicErrorController**处理;
1)响应页面;拿到状态码,拿到一些Model数据,返回一个ModelAndView(即要去的页面,怎么得到的,是使用resolveErrorView方法),去哪个页面是由**DefaultErrorViewResolver**解析得到的;
```java
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
//所有的ErrorViewResolver(异常视图解析器)得到ModelAndView
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
2)、如果定制错误响应:
1)、如何定制错误的页面;
1)、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到 对应的页面;即在resources下面的temeplates下新建一个error文件夹,里面放置404.html等一些其他页面
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态 码.html);
页面能获取的信息;
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里
即可在自定义错误页面4xx.html或者404.html中显示自己想要的信息
有模板引擎先通过BasicErrorController的errorviewResolver(异常视图解析器)找template下的error文件夹下的页面显示,没有即返回null,返回null就默认转发到error视图,error视图在ErrorMvcAutoConfiguration 下面,容器中有一个视图对象叫error,
@Bean(“error”),返回defaultErrorView
2)、如何定制错误的json数据;
1)、自定义异常处理&返回定制json数据;
//浏览器客户端返回的都是json请求,没有自适应效果
@ControllerAdvice //异常处理器标识
public class MyExceptionHandler {
@ResponseBody
@ExceptionHandler(UserNotExistException.class) //运用SpringMvc的ExceptionHandler捕获到UserNotExistException异常后,
// 通过ResponseBody将数据显示出来
public Map handle(Exception e){//把异常对象传进来
Map map=new HashMap<>(); //响应自己的json数据,把内容传到Map中
map.put("code","user.notexist");
map.put("message",e.getMessage());
return map; //页面返回自己定制的json数据
}
}
转发到/error进行自适应响应效果处理
@ExceptionHandler(UserNotExistException.class) //运用SpringMvc的ExceptionHandler捕获到UserNotExistException异常后,
// 通过ResponseBody将数据显示出来
public String handle(Exception e, HttpServletRequest request){//把异常对象传进来
Map map=new HashMap<>(); //响应自己的json数据,把内容传到Map中
//传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程
//需要传入我们自己的错误状态码 4xx.5xx
// Integer statusCode = (Integer)request.getAttribute(“javax.servlet.error.status_code”); springmvc获取状态码源码
request.setAttribute(“javax.servlet.error.status_code”,500);
map.put(“code”,“user.notexist”);
map.put(“message”,e.getMessage());
return “forward:error”; //转发到error是因为底层处理错误页面BasiErrorController是通过处理/error请求,而/error请求是自适应的
// 所以我们就把自定义的异常处理转给/error处理,让它来判断是客户端请求还是浏览器请求,判断完成后
// 然后会转发到我们自定义的错误页面5xx.html,会返回自己定制的json数据
3)将我们的定制数据携带出去(上一种方法客户端访问时返回的错误数据code和exception并没有显示我们自定义的数据,改用此方法);
出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由 BasicErrorController.class中的
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response)
方法中
getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
1、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;(太麻烦)
2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;
那么就自定义一个MyErrorAttribute设置显示自己想要的数据
//给容器中加入我们自定义的ErrorAttribute
@Component
public class MyErrorAttribute extends DefaultErrorAttributes {
@Override
public Map
Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
errorAttributes.put("company","guigu");
return errorAttributes;
// return super.getErrorAttributes(webRequest, includeStackTrace);
}
}
最终的效果:响应是自适应的(即判定是客户端请求还是浏览器请求),可以通过定制ErrorAttributes改变需要返回的内容,
package springbootwebrestfulcrud.demo.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import springbootwebrestfulcrud.demo.exception.UserNotExistException;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice //异常处理器标识
public class MyExceptionHandler {
// //浏览器客户端返回的都是json请求
// @ResponseBody
// @ExceptionHandler(UserNotExistException.class) //运用SpringMvc的ExceptionHandler捕获到UserNotExistException异常后,
// // 通过ResponseBody将数据显示出来
// public Map
//
// Map
// map.put(“code”,“user.notexist”);
// map.put(“message”,e.getMessage());
// return map; //页面返回自己定制的json数据
// }
@ExceptionHandler(UserNotExistException.class) //运用SpringMvc的ExceptionHandler捕获到UserNotExistException异常后,
// 通过ResponseBody将数据显示出来
public String handle(Exception e, HttpServletRequest request){//把异常对象传进来
Map map=new HashMap<>(); //响应自己的json数据,把内容传到Map中
//需要传入我们自己的错误状态码 4xx.5xx,否则就不会进入定制错误页面的解析流程
// Integer statusCode = (Integer)request.getAttribute(“javax.servlet.error.status_code”); springmvc BasicErrorController获取状态码源码
request.setAttribute(“javax.servlet.error.status_code”,500);
map.put(“code”,“user.notexist”);
map.put(“message”,“用户出错了”);
request.setAttribute(“ext”,map);//数据放在请求域中
return “forward:error”; //转发到error是因为底层处理错误页面BasiErrorController是通过处理/error请求,而/error请求是自适应的
// 所以我们就把自定义的异常处理转给/error处理,让它来判断是客户端请求还是浏览器请求,判断完成后
// 然后会转发到我们自定义的错误页面5xx.html,会返回自己定制的json数据
}
}
package springbootwebrestfulcrud.demo.component;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
//给容器中加入我们自定义的ErrorAttribute
@Component
public class MyErrorAttribute extends DefaultErrorAttributes {
//返回值的map就是页面和json能获取的所有字段
@Override
public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
errorAttributes.put("company","guigu");
//我们自定义的异常处理器MyExceptionHandler携带的数据
Map map = (Map) webRequest.getAttribute("ext", 0);//从请求域中获取自定义额外添加的信息,在MyExceptionHandler.java中
errorAttributes.put("ext",map);
return errorAttributes;
// return super.getErrorAttributes(webRequest, includeStackTrace);
}
}
5xx.html
8、配置嵌入式Servlet容器
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器
1)、如何定制和修改Servlet容器的相关配置;
1、修改和server有关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】);
server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx
2、编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器;来修改Servlet容器的 配置
Spring Boot2.0以上版本EmbeddedServletContainerCustomizer被WebServerFactoryCustomizer替代
@Bean
public WebServerFactoryCustomizer webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer(){
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8083);
}
};
}
老版本
@Bean
//一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer()
{
return new EmbeddedServletContainerCustomizer() {
//定制嵌入式的Servlet容器相关的规则
@Override
public void customize(ConfigurableEmbeddedServletContainer container)
{
container.setPort(8083);
}
};
}
模式:
1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如 果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默 认的组合起来;
2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
3)、替换为其他嵌入式Servlet容器
默认支持:
Tomcat(默认使用)
org.springframework.boot
spring‐boot‐starter‐web
引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器;
Jetty
org.springframework.boot
spring‐boot‐starter‐web
spring‐boot‐starter‐tomcat
org.springframework.boot
spring‐boot‐starter‐jetty
org.springframework.boot
Undertow
spring‐boot‐starter‐undertow
org.springframework.boot
嵌入式Servlet容器工厂
TomcatServletWebServerFactory
JettyServletWebServerFactory
nettyServletWebServerFactory
undertowServletWebServerFactory
以TomcatServletWebServerFactory为例
//创建一个tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环境
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入进去,返回一个嵌入式的Servlet容器。并且getTomcatWebServer()方法中启动Tomcat服务器
return this.getTomcatWebServer(tomcat);
4)、嵌入式Servlet容器自动配置原理
步骤:
1)、SpringBoot根据导入的依赖情况,给容器中添加相应的 EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
(新版的TomcatServletWebServerFactory)嵌入式容器工厂创建出对象
2)、容器中某个组件要创建对象就会惊动后置处理器; EmbeddedServletContainerCustomizerBeanPostProcessor; 只要是嵌入式的Servlet容器工厂,后置处理器就工作;
3)、后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,即调用定制器的定制方法
5)、嵌入式Servlet容器启动原理;
什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat;
获取嵌入式的Servlet容器工厂:
1)、SpringBoot应用启动运行run方法
2)、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一 个组件】;
如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext,否则是默认的: AnnotationConfigApplicationContext容器
3)、refresh(context);刷新刚才创建好的ioc容器;
4)、 onRefresh(); web的ioc容器重写了onRefresh方法
5)、web ioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer();
6)、获取嵌入式的Servlet容器工厂:
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
从ioc容器中获取EmbeddedServletContainerFactory 组件;TomcatEmbeddedServletContainerFactory创建 对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;
7)、使用容器工厂获取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());
8)、嵌入式的Servlet容器创建对象并启动Servlet容器;
先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;
IOC容器启动时创建嵌入式的Servlet容器
9、使用外置的Servlet容器
嵌入式Servlet容器:应用打成可执行的jar
优点:简单、便携;
缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义 EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂 【EmbeddedServletContainerFactory】);
外置的Servlet容器:外面安装Tomcat—应用war包的方式打包;
原理 :
jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器;
war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器
servlet3.0(Spring注解版):
8.2.4 Shared libraries / runtimes pluggability:
规则:
1)、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:
2)、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为 javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
3)、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
流程:
1)、启动Tomcat
2)、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\METAINF\services\javax.servlet.ServletContainerInitializer: Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer
3)、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型 的类都传入到onStartup方法的Set>;为这些WebApplicationInitializer类型的类创建实例;
4)、每一个WebApplicationInitializer都调用自己的onStartup;
SpringBootServletInitializer 实现了WebApplicationInitializer接口方法
5)、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法
6)、SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器
protected WebApplicationContext createRootApplicationContext( //创建容器
ServletContext servletContext) {
//1、创建SpringApplicationBuilder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info(“Root context already created (using as parent).”);
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
//调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = configure(builder);
//使用builder创建一个Spring应用
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ “configure method or add an @Configuration annotation”);
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.getSources().add(ErrorPageFilterConfiguration.class);
}
//启动Spring应用
return run(application);
}
7)、Spring的应用就启动并且创建IOC容器
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新IOC容器
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
先启动Servlet容器(Tomcat、Jetty、JBoss、webLogic),再启动SpringBoot应用
ServletInitializer调用configure方法将SpringBoot的主程序类传入了进来,先启动Servlet容器,再启动SpringBoot应用
和jar包的方式相反
web容器(web服务器)主要有:Apache、IIS、Tomcat、Jetty、JBoss、webLogic等,而Tomcat、Jetty、JBoss、webLogic同时也是servlet容器,或者说他们还包含了servlet容器。没有servlet容器,你也可以用web容器直接访问静态页面,比如安装一个apache等,但是如果要显示jsp/servlet,你就要安装一个servlet容器了,但是光有servlet容器是不够的,因为它要被解析成html输出,所以你仍需要一个web容器。大多数servlet容器同时提供了web容器的功能,也就是说大多servelt容器可以独立运行你的web应用。
五、Docker
1、简介
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)、导入虚拟机文件centos7-atguigu.ova;
3)、双击启动linux虚拟机;使用 root/ 123456登陆
4)、使用客户端连接linux服务器进行命令操作;
5)、设置虚拟机网络;
桥接网络=选好网卡==接入网线;
6)、设置好网络以后使用命令重启虚拟机的网络
service network restart
7)、查看linux的ip地址
ip addr
8)、使用客户端连接linux;
步骤:
1、检查内核版本,必须是3.10及以上
uname -r
2、安装docker
yum install docker
3、输入y确认安装
4、启动docker
[root@localhost ~]# systemctl start docker
[root@localhost ~]# docker -v
Docker version 1.12.6, build 3e8e77d/1.12.6
5、开机启动docker
[root@localhost ~]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
6、停止docker
systemctl stop docker
操作 | 命令 | 说明 |
---|---|---|
检索 | docker search 关键字 eg:docker search redis | 我们经常去docker hub上检索镜像的详细信息,如镜像的TAG。 |
拉取 | docker pull 镜像名:tag | :tag是可选的,tag表示标签,多为软件的版本,默认是latest |
列表 | docker images | 查看所有本地镜像 |
删除 | docker rmi image-id | 删除指定的本地镜像 |
https://hub.docker.com/
命令:
1、搜索镜像 [root@localhost ~]# docker search tomcat
2、拉取镜像 [root@localhost ~]# docker pull tomcat
3、根据镜像启动容器 docker run ‐‐name mytomcat ‐d tomcat:latest
–name:自定义容器名
-d:后台运行
image-name:指定镜像模板
4、docker ps 查看运行中的容器
5、 停止运行中的容器 docker stop 容器的id
6、查看所有的容器 docker ps ‐a
7、启动容器 docker start 容器id
8、删除一个容器 docker rm 容器id
9、启动一个做了端口映射的tomcat [root@localhost ~]# docker run ‐d ‐p 8888:8080 tomcat ‐d:后台运行 ‐p: 将主机的端口映射到容器的一个端口 主机端口:容器内部的端口
8888是虚拟机端口号,映射到容器Tomcat的8080
10、为了演示简单关闭了linux的防火墙 service firewalld status ;查看防火墙状态 service firewalld stop:关闭防火墙
11、查看容器的日志 docker logs container‐name/container‐id
更多命令参看 https://docs.docker.com/engine/reference/commandline/docker/
可以参考每一个镜像的文档
SpringBoot与数据访问
1、JDBC
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://192.168.15.22:3306/jdbc
driver-class-name: com.mysql.jdbc.Driver
效果:
默认是用class com.zaxxer.hikari.HikariDataSource作为数据源;
数据源的相关配置都在DataSourceProperties里面;
效果:
默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;(老版本)
默认是用class com.zaxxer.hikari.HikariDataSource作为数据源(新版本);
数据源的相关配置都在DataSourceProperties里面;
自动配置原理:
org.springframework.boot.autoconfigure.jdbc:
1、参考DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池;(老版本)
可以使用 spring.datasource.type指定自定义的数据源类型
自定义数据源类型:c3p0,dbcp...;
3、自定义数据源类型
/**
* Generic DataSource configuration.
*/
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
//使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
return properties.initializeDataSourceBuilder().build();
}
}
4、DataSourceInitializer:ApplicationListener;
作用:
1)、runSchemaScripts();运行建表语句;
2)、runDataScripts();运行插入数据的sql语句;
默认只需要将文件命名为:
schema‐*.sql、data‐*.sql 默认规则:
schema.sql,schema‐all.sql;
也 可以使用在配置文件中加
schema:
‐ classpath:department.sql
指定位置
5、操作数据库:自动配置了JdbcTemplate操作数据库
6。Springboot整合Druid
7。Springboot整合Druid+Mybatis
package com.itcast.springbootmybatis.config;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//自定义MyBatis的配置规则;给容器中添加一个ConfigurationCustomizer
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true); //开启驼峰命名法映射规则,当数据库中字段名是驼峰命名规则和对应Bean字段名不一致时,开启
}
};
}
}
package com.itcast.springbootmybatis.mapper;
import com.itcast.springbootmybatis.bean.Department;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
//指定这是一个操作数据库的mapper
//@Mapper
@Repository
public interface mapper {
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);
@Delete("delete * from department where id=#{id}")
public int deleteDeptById(Integer id);
@Options(useGeneratedKeys = true,keyProperty = "id") //主键自动生成,keyProperty告知哪个是主键
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department department);
@Update("update department set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department department);
}
package com.itcast.springbootmybatis.controller;
import com.itcast.springbootmybatis.bean.Department;
import com.itcast.springbootmybatis.mapper.mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DeptController {
@Autowired
mapper departmentMapper;
@GetMapping("/dept/{id}")
public Department getDepartment(@PathVariable("id") Integer id){
return departmentMapper.getDeptById(id);
}
@GetMapping("/dept")
public Department insertDept(Department department){
departmentMapper.insertDept(department);
return department;
}
}
package com.itcast.springbootmybatis;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
@MapperScan(value = "com.itcast.springbootmybatis.mapper") //批量扫描整个mapper,避免每个Mapper文件下写@Mapper
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
注解版:
mybatis:
# 全局配置文件位置
config-location: classpath:mybatis/mybatis-config.xml
# sql映射文件位置
mapper-locations: classpath:mybatis/mapper/*.xml
# - classpath:employee.sql
package com.itcast.springbootmybatis.mapper;
import com.itcast.springbootmybatis.bean.Department;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
//指定这是一个操作数据库的mapper
//@Mapper
@Repository
public interface mapper {
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);
@Delete("delete * from department where id=#{id}")
public int deleteDeptById(Integer id);
@Options(useGeneratedKeys = true,keyProperty = "id") //主键自动生成,keyProperty告知哪个是主键
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department department);
@Update("update department set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department department);
}
配置文件:
全局配置文件
数据库文件
INSERT into employee(lastName,email,gender,d_id) values (#{lastName},#{email},#{gender},#{dId})
Spring Data
JPA的使用
package com.itcast.springbootjpa.controller;
import com.itcast.springbootjpa.entity.User;
import com.itcast.springbootjpa.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
@RestController
public class UserController {
@Autowired
UserRepository userRepository;
// @GetMapping("/user/{id}")
// public Optional getUser(@PathVariable("id") Integer id){
// Optional user=userRepository.findById(id);
// return user;
// }
@GetMapping("/user/{id}")
public User getUser(@PathVariable("id") Integer id){
// 因为jsonplugin用的是java的内审机制.hibernate会给被管理的pojo加入一个hibernateLazyInitializer属性,
// jsonplugin会把hibernateLazyInitializer也拿出来操作,并读取里面一个不能被反射操作的属性就产生了这个异常.
User user = userRepository.findById(id).orElse(null); //需要在实体类中配置@JsonIgnoreProperties(value ="hibernateLazyInitializer" )
// User one = userRepository.getOne(id);//使用此方法会报错500,原因未知,也可使用上面注释的方法
return user;
}
@GetMapping("/user")
public User insertUser(User user){
User save = userRepository.save(user);
return save;
}
}
package com.itcast.springbootjpa.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
//使用JPA注解 配置映射关系
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "tbl_user") //@Table来指定和哪个数据库表对应,如果name默认表名就是user;如果数据库此时没有则会自己创建数据库表
@JsonIgnoreProperties(value ="hibernateLazyInitializer" )
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增主键
private Integer id;
@Column(name = "last_Name",length = 50) //这是和数据库表对应的一个列
private String lastName;
@Column //省略默认列名就是属性名
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package com.itcast.springbootjpa.repository;
import com.itcast.springbootjpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository {
}
spring:
datasource:
url: jdbc:mysql://192.168.0.191/jpa
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
#更新或者创建数据表结构
ddl-auto: update
#控制台显示sql
show-sql: true
七、启动配置原理
几个重要的事件回调机制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
1、创建SpringApplication对象
initialize(sources);
private void initialize(Object[] sources)
{
//保存主配置类
if (sources != null && sources.length > 0)
{
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment();
//从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起 来
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
//从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
八、自定义starter
starter:
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类要能加载 将需要启动就加载的自动配置类,配置在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
3、模式:
启动器(starter)只用来做依赖导入;
专门来写一个自动配置模块(XXXAutoConfigure);
启动器依赖自动配置;别人只需要引入启动器(starter)
mybatis-spring-boot-starter;
自定义启动器名-spring-boot-starter
8.Cache缓存
package com.itguigu.springbootcache;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* 快速体验缓存:
* 步骤:开启基于注解的缓存
* 2.标注缓存注解即可
* @CachePut 保证方法被调用,又希望结果被缓存。
* @CacheEvict 清空缓存
* @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
*
* 需要另在启动类上加上@CacheEnable注解缓存才会生效
* 原理:
* 1.自动配置类CacheAutoConfiguration
* 2.缓存的配置类
* 0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
* 1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
* 2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
* 3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
* 5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
* 4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
* 6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
* 7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
* 8 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
* 9 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
* 3.默认SimpleCacheConfiguration生效
* 4、给容器注册了一个CacheManager:ConcurrentMapCacheManager。
* 可以获取和创建ConcurrentMapCache类型的缓存组件,他的作用将数据保存在ConcurrentMap(extends Map)中
* 运行流程:
* @Cacheable:
* 1.方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取:
* (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
* 2.去Cache中查找缓存的内容,使用一个key,默认就是方法的参数:
* key是按照某种策略生成的:默认是使用keyGenerator生成的,默认是使用SimpleKeyGenerator生成的key
* SimpleKeyGenerator生成key的默认策略:
* 如果没有参数:key=new SimpleKey();
* 如果有一个参数:key=参数的值
* 如果有多个参数:key=new SimpleKey(params);
* 3.没有查询到缓存就调用目标方法
* 4.将目标方法返回的结果,放进缓存中
* @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存
* 如果没有,就运行方法并将结果放入缓存,以后再来调用就可以直接使用缓存中的数据
*
* 核心:
* 1.使用CacheManager(ConcurrentMapCacheManager)按照名字得到cache(ConcurrentMapCache)组件
* 2.key使用keyGenerator生成的,默认是SimpleKeyGenerator
*
*/
@EnableCaching
@MapperScan("com.itguigu.springbootcache.mapper") ////批量扫描整个mapper,避免每个Mapper文件下写@Mapper
@SpringBootApplication
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
}
}
package com.itguigu.springbootcache.service;
import com.itguigu.springbootcache.bean.Employee;
import com.itguigu.springbootcache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
@CacheConfig(cacheNames = "emp") //抽取缓存的公共配置,此处注解后内部不用再写
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
/**
* 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存获取,不用调用方法;
*
* CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字
* 几个属性:
* cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
* key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
* 编写SpEL: #id:参数id的值 #a0 #p0 #root.args[0]
* keyGenerator:key的生成器:可以自己指定key的生成器的组件id
* key/keyGenerator:二选一使用
*
* cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
* condition:指定符合条件的情况下缓存
* condition = "#a0>1" 第一个参数的值大于1才进行缓存
* unless:否定缓存:当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断(#result取出结果的值)
* unless="#a0==2":假如第一个参数的值是2,结果不缓存
* sync:是否使用异步模式
* 当sync等于true时,unless则不能使用
* @param id,key = "#id",condition = "#id>0",unless = "#result==null"
* @return
*/
//@Cacheable(cacheNames = "emp",keyGenerator = "myKeyGenerator") //key = "#root.methodName+'['+#id+']'"自定义key
@Cacheable(cacheNames = "emp") //key = "#root.methodName+'['+#id+']'"自定义key
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
/**
* @CachePut:既调用方法,又更新缓存数据
* 修改了数据库的某个数据,同时更新缓存
* 运行时机:先调用目标方法,将方法的结果缓存起来
* 测试步骤:
* 1.查询1号员工,查到的结果会放在缓存中
* key;1 value:lastname:wangwu 缓存是通过键值对的方式保存的
* 2.以后查询还是之前的结果
* 3.更新1号员工 [ lastName:keliu ]
* 将方法的返回值也放进了缓存中
* 而此时key:传入的employee对象 值:返回的employee对象 缓存中之前的key没有更新,需要在参数中添加key的值
*
* 4.查询一号员工:
* 应该是更新后的员工,结果是更新前的员工信息 (原因因为更新员工时,key为指定为之前缓存的key,所以之前缓存的key没有更新)
* key="#employee.id" :使用传入的参数的员工id;
* key="#result.id" 使用返回后的id (这两个效果一样,因为@Cacheput是方法完成后将结果缓存起来)
* @Cacheable的key是不能用#result
*/
@CachePut(value = "emp",key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("updateEmp"+employee);
employeeMapper.updateEmp(employee);
return employee;
}
/**
* @CacheEvict:缓存清除
* key:指定要清除的缓存数据
* allEntries = true 清空缓存中所有数据
* beforeInvocation=false :缓存的清除是否在方法之前执行
* 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
* beforeInvocation=true
* * 默认代表缓存清除操作是在方法执行之前执行;无聊是否出现异常,缓存都清除
*/
@CacheEvict(value = "emp",key = "#id")//allEntries = true 清空缓存中所有数据
public void deleteEmp(Integer id){
System.out.println("deleteEmp"+id);
//employeeMapper.delEmp(id);
}
//@CachePut这个注解是在方法执行之后执行的,所以被注解的方法一定会执行
@Caching(
cacheable = {
@Cacheable(value = "emp",key = "#lastName")
},
put = {
@CachePut(value = "emp",key="#result.id"),
@CachePut(value = "emp",key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName){
return employeeMapper.getEmpByLastName(lastName);
}
}
package com.itguigu.springbootcache.controller;
import com.itguigu.springbootcache.bean.Employee;
import com.itguigu.springbootcache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@GetMapping("/emp/{id}")
public Employee getEmployee(@PathVariable("id") Integer id){
Employee emp = employeeService.getEmp(id);
return emp;
}
@GetMapping("/emp")
public Employee updateEmp(Employee employee){
Employee employee1 = employeeService.updateEmp(employee);
return employee1;
}
@GetMapping("/del")
public String delEmp(Integer id){
employeeService.deleteEmp(id);
return "success";
}
@GetMapping("/emp/lastName/{lastName}")
public Employee getEmpByLastName(@PathVariable("lastName") String lastName){
return employeeService.getEmpByLastName(lastName);
}
}
redis:
redis:0>append msg hello
"5"
redis:0>append msg world
"10"
redis:0>get msg
"helloworld"
redis:0>lpush mylist 1 2 3 4 5
"5"
redis:0>lpop mylist
"5"
redis:0>rpop mylist
"1"
redis:0>sadd myset zhangsan lisi
"2"
redis:0>sadd myset lisi
"0"
redis:0>smembers myset
1) "lisi"
2) "zhangsan"
redis:0>sismember myset wangwu 判断元素在不在myset集合中
"0"
* 三:整合redis作为缓存
* Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以作为数据库,缓存和消息中间件
* 1.安装redis,使用docker :docker pull registry.docker-cn.com/library/redis
* 2.引入redis的starter
* 3.配置redis
* 4.测试缓存
* 原理:CacheManager创建了cache缓存组件,缓存组件来实际给缓存中进行存取数据
* 1.引入redis的starter,容器中保存的是RedisCacheManager
* 2.RedisCacheManager 帮我们创建RedisCache来作为缓存组件,RedisCache通过操作redis缓存数据
* 3.默认保存数据k-v都是object:利用序列化来保存:如何保存为json
* 1.引入了redis的starter,cacheManager变为RedisCacheManager
* 2.默认创建的RedisCacheManager操作redis的时候使用的是 RedisTemplate