一,SpringBoot简介
一,Spring的生态圈
Spring狭义上指的是Spring FrameWork,广义上指的是整个Spring生态圈
Spring的七大应用场景:
- Microservices:微服务开发
- Reactive:响应式编程(用少量的CPU和内存资源构建高吞吐量的应用)
- Cloud:分布式云开发
- Web apps:WEB应用开发
- Serverless:无服务开发(函数级服务开发)
- Event Driven:事件驱动
- Batch:批处理开发
- ……
Spring也为我们提供了一整套开发解决方案:
- Spring Framework:一套核心机制,IOC和AOP……
- SpringBoot:微服务解决方案,一整套应用整合的最优解
- SpringData:数据访问解决方案,无论是SQL数据库还NoSQL都能解决数据访问问题
- SpringCould:解决分布式下的所有问题,比如:负载均衡,服务注册发现,网关,熔断……
- SpringSecurity:解决应用的安全控制问题
- SpringSeesion:解决分布式情况下的会话问题
- SpringAMQP:解决消息队列问题
- SpringBatch:解决批处理应用问题
- SpringShell:做命令行程序开发
- SpringWeb:web开发
- SpringWEB Flow:WEB的函数式(流式)开发
二,SpringBoot的概述
SpringBoot能快速构建出一套生产级别的Spring应用
在没有使用SpringBoot时,项目构建一般用的是SSM,它的配置相等麻烦,比如:Transaction事务配置,DisPatchServlet核心控制器配置,DataSource的配置,Mybatis整合配置配置,Spring和SpringMVC的整合配置,下载外部Tomcat……这些过于繁琐,而SpringBoot很好的解决这些问题
SpringBoot的两大特性:
- 依赖管理
- 自动配置
SpringBoot的优点:
- 可以很简单的构建一套独立的Spring应用
- 内嵌了web服务器
- 自动Starter场景启动器,简化构建配置
- 自动配置Spring以及第三方功能
- 提供了生产级别的监控,健康检查及外部化配置
- 无代码生成,无需编写XML
SpringBoot的缺点:
- 版本迭代较快,需要不到的关注版本变化,不同的主动性学习
- 封装较深,内部原理复杂,不容易精通
三,SpringBoot官方文档的使用
SpringBoot官方文档的地址:https://spring.io/
Spring文档类型:
文档目录结构:
四,SpringBoot的入门
SpringBoot的使用步骤:
- 创建Maven工程
- 引入POM依赖
- 创建主程序
- 编写业务类
- 测试
- 简化配置
- 简化部署
① 创建Maven工程(略)
② 映入POM依赖
SpringBoot的pom依赖主要由两部分组成:
- parent父工程
- starter场景启动器
<!--SpringBoot父工程--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> </parent> <dependencies> <!--web场景启动器,不需要声明版本,所有启动器的版本号和父工程一样--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
③ 创建主程序
//@SpringBootApplication标记为主程序类,固定写法 @SpringBootApplication public class springbootapplication { public static void main(String[] args) { SpringApplication.run(springbootapplication.class,args); } }
④ 编写业务类
@Controller public class json_control { @ResponseBody @RequestMapping("/home") public String json(){ return "SpringBoot 启动成功!"; } }
⑤ 测试(启动主程序并访问)
⑥ 简化配置
#例:修改springboot启动的端口号 server.port=8888
⑦ 简化部署(可执行Jar包)
SpringBoot内嵌了一个Tomcat,打成的Jar包有一整套运行环境,不需要将程序单独打成jar或者war包,放在外部web服务器上运行,可以直接运行jar即可访问
打包插件:
<!--添加SpringBoot打包插件,这样打出来的jar才是可执行的,不然会报没有主清单属性的错误--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
运行jar包:
java -jar jar包路径
二,SpringBoot依赖管理
SpringBoot的依赖管理包含四大部分:
- 依赖管理
- starter场景启动器
- 自动版本仲裁
- 可以修改版本号
SpringBoot依赖管理的实现依赖于Maven的继承,它的所有依赖管理,版本仲裁,场景启动器都基于Maven的继承关系
在Maven继承有两个部分:SpringBoot也是基于这两点实现的
- 在父工程中声明要被继承的依赖
- 在子工程中通过parent继承父工程中的依赖
一,SpringBoot父工程的源文件
在SpringBoot中父工程的定义以及被封装好了,直接通过中央的maven仓库下载到本地即可,不需要自己定义或者配置
源文件主要包含四个部分:
- 项目描述
- 公共属性
- 声明被继承依赖
- 声明被继承插件
这个SpringBoot的maven父工程spring-boot-dependencies源文件(因为太多了重复的有删减)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.5.0</version> <!--打包类型,pom为父工程--> <packaging>pom</packaging> <!--项目的名称--> <name>spring-boot-dependencies</name> <!--项目的描述信息,在生产项目文档时有用--> <description>Spring Boot Dependencies</description> <!--哪个网站可以找到这个项目,提示如果 Maven 资源列表没有,可以直接上该网站寻找, Maven 产生的文档用--> <url>https://spring.io/projects/spring-boot</url> <!--该元素描述了项目所有License列表--> <licenses> <!--描述了项目的license,用于生成项目的web站点的license页面,其他一些报表和validation也会用到该元素--> <license> <!--license用于法律上的名称--> <name>Apache License, Version 2.0</name> <!--官方的license正文页面的URL--> <url>https://www.apache.org/licenses/LICENSE-2.0</url> </license> </licenses> <!--开发者信息,下面可以描述N个开发者--> <developers> <!--具体开发信息--> <developer> <!--开发者姓名--> <name>Pivotal</name> <!--开发者邮箱--> <email>info@pivotal.io</email> <!--项目开发者所属组织--> <organization>Pivotal Software, Inc.</organization> <!--项目开发者所属组织的URL--> <organizationUrl>https://www.spring.io</organizationUrl> </developer> </developers> <!--声明项目资源存放--> <scm> <!--项目资源存放地址,这里是github--> <url>https://github.com/spring-projects/spring-boot</url> </scm> <!--定义maven公共属性,这些属性可以在该文件任何地方被调用,主要是封装了各个依赖版本号--> <properties> <activemq.version>5.16.2</activemq.version> <antlr2.version>2.7.7</antlr2.version> <appengine-sdk.version>1.9.88</appengine-sdk.version> <artemis.version>2.17.0</artemis.version> <aspectj.version>1.9.6</aspectj.version> <jaybird.version>4.0.3.java8</jaybird.version> <jboss-logging.version>3.4.1.Final</jboss-logging.version> <jboss-transaction-spi.version>7.6.1.Final</jboss-transaction-spi.version> <jdom2.version>2.0.6</jdom2.version> <jedis.version>3.6.0</jedis.version> </properties> <!--dependencyManagement定义需要被继承的依赖--> <dependencyManagement> <!--依赖项,下面可以包含N个具体依赖--> <dependencies> <!--具体的依赖,版本都是通过${}调用属性值--> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-blueprint</artifactId> <version>${activemq.version}</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-console</artifactId> <version>${activemq.version}</version> <!--exclusions声明排除依赖,这个这个排除的依赖部分不好被子工程继承--> <exclusions> <!--具体的排除依赖--> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </dependencyManagement> <!--构建--> <build> <!--pluginManagement声明需要继承的插件--> <pluginManagement> <!--插件项,可以包含N个具体插件--> <plugins> <!--具体的插件--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>${maven-failsafe-plugin.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-help-plugin</artifactId> <version>${maven-help-plugin.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-install-plugin</artifactId> <version>${maven-install-plugin.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-invoker-plugin</artifactId> <version>${maven-invoker-plugin.version}</version> </plugin> </plugins> </pluginManagement> </build> </project>
二,子工程继承父工程
子工程通过Parent继承父工程的依赖和插件等信息
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> </parent>
继承父工程后,子工程引用父工程中的依赖时,不需要加版本号,默认使用父工程的版本号
自定义版本:因为在源父工程中通过properties属性封装了各个依赖的版本,自定义时,只需要重写属性即可
注:重写版本号时,必须属性名跟父工程的属性名一致
例:修改子工程中lombok的版本
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>SpringBoot-dome</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> </parent> <properties> <!----重写父工程中的属性,属性名称一定要跟父工程的一致,否则重写失败> <lombok.version>1.15.1</lombok.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
三,SpringBoot的Starter场景启动器
Starter是一个很实用的功能,它底层就是一种简单的封装,把每一个场景所需要的组件都封装在一起,构成场景启动器,这样解决了原始SMM项目导入依赖时的麻烦问题
场景启动器的作用:解决了导入依赖时的繁琐细节,比如依赖包需要单个导入,包版本冲突等
例:以spring-boot-starter-web启动器为例
① 这个启动器源码文件又封装了json,tomcat,spring-boot场景启动器和spring-web和spring-webmvc依赖(源码的头文件和描述文件被我删减了)
<dependencies> <!--Spring-boot场景启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.5.0</version> <scope>compile</scope> </dependency> <!--json场景启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.5.0</version> <scope>compile</scope> </dependency> <!--tomcat场景启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.5.0</version> <scope>compile</scope> </dependency> <!--spring-web依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.7</version> <scope>compile</scope> </dependency> <!--spring-webmvc依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.7</version> <scope>compile</scope> </dependency> </dependencies>
② 在进入spring-boot-starter-tomcat启动器看它的源文件(源码的头文件和描述文件被我删减了)
这个启动器包含各个依赖组件:tomcat-embed-core,tomcat-embed-el,tomcat-embed-websocket,org.apache.tomcat
<dependencies> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>1.3.5</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>9.0.46</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>tomcat-annotations-api</artifactId> <groupId>org.apache.tomcat</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> <version>9.0.46</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>9.0.46</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>tomcat-annotations-api</artifactId> <groupId>org.apache.tomcat</groupId> </exclusion> </exclusions> </dependency> </dependencies>
③ 整体封装的结构(通过IDEA的Diagrams看它的封装关系图)
三,SpringBoot的自动配置
一,SpringBoot自动配置的简单概述
SpringBoot是相比较于传统Spring和SpringMVC最大的一个亮点,它可以自动配置组件,不需要手动加入容器
以SpringMVC组件为例的自动配置过程:
1,导入web场景启动器,映入全部web依赖包括SpringMVC
2,自动配置SpringMVC
- 引入SpringMVC全部组件
- 自动配置好SpringMVC常用组件
3,自动配置web常用功能,如:编解码
- SpringBoot帮我们配置好了所有web开发的场景
4,默认包结构
- 默认主程序所在的包下面的所有子包都被扫描,无需手动配置以前@ComponentScan包扫描
- 也可以改变默认的扫描路径通过@SpringBootApplication(scanBasePackages="包的位置扫描",scanBasePackageClasses="类的位置扫描")的两个参数进行指定
5,各种配置拥有默认值
- 默认配置最终都有对应的映射文件
- 配置文件的值最终会绑定每一个类中,这个类会在容器中创建对象
6,按需加载所有配置项
- SpringBoot有非常多的starter场景启动器
- 默认引入了哪些场景,自动配置才会开启
- SpringBoot所有的自动配置功能都在spring-boot-autoconfigure包
- 通过@ConditionalOnClass,@ConditionalOnBean等注解判断是否开启配置
二,SpringBoot自动配置底层实现
一,SpringBoot自动配置的核心注解和底层实现
@SpringBootApplication:这个注解是SpringBoot中最根本的注解,他实现了自动配置等功能,它是个组合注解,底层有三个注解共同实现
1,SpringBootApplication的组合注解:
- @SpringBootConfiguration:标明为SpringBoot配置类
- @EnableAutoConfiguration:
- @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }):包扫描,并指定了两个自定义过滤器
@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) }) public @interface SpringBootApplication {……}
2,SpringBootConfiguration的底层注解:
- @Configuration:标注表明是一个配置类
- @Indexed: 它可以为 Spring 的模式注解添加索引,以提升应用启动性能
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration @Indexed public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true; }
3,EnableAutoConfiguration的底层注解:
- @AutoConfigurationPackage:自动配置包,底层是@import注解
- @Import(AutoConfigurationImportSelector.class):导入AutoConfigurationImportSelector.class类
4,AutoConfigurationPackage注解的底层实现:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {……}
AutoConfigurationPackages.Registrar.class类的作用
作用:利用Registrar批量给容器中导入组件
源码:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); } }
new PackageImports(metadata).getPackageNames().toArray(new String[0])):获取当前启动类的包名并把当前的子包名保持导数组中,进行子包批量导入
5,AutoConfigurationImportSelector.class类
- 利用getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)方法给容器中批量导入组件
- 使用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取需要导入的组件路径名
- 利用工厂加载List<String> configurations = SpringFactoriesLoader.loadFactoryNames得到所有组件
- 从META-INF/spring.factories位置加载一个文件,默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
注:在spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面有主要加载路径META-INF/spring.factories文件,并不是所有的jar包都有META-INF/spring.factories
默认加载的META-INF/spring.factories文件,这里面的场景路径在springboot启动时全部会被加载(131个)
org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition 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,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\ org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ ……………………………………………………………………………………
二,按需配置
在SpringBoot默认会加载两个地方的文件:
- 当前包路路径下的所有子包或者指定CompentScan扫描路径下的包
- 批量导入META-INF/spring.factories文件中的所有场景的自动配置(131个自动配置)
虽然在springboot基本上导入了所有配置,但它有一个开启策略,系统组件只有被开启了才能使用,它使用@Conditiona作为底层注解按条件激活加载组件
常见的按需配置加载注解:
- @ConditionalOnClass(value=):是否有该类,包含了才开启
- @ConditionalOnBean():容器中是否包含该类型的组件
- @ConditionalOnMissingBean(value=):容器是否不包含该类
- @ConditionalOnProperty():在Spring配置文件中是否配置
- ……
如:
@Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true) public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher, JobExplorer jobExplorer, JobRepository jobRepository, BatchProperties properties) { JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository); String jobNames = properties.getJob().getNames(); if (StringUtils.hasText(jobNames)) { runner.setJobNames(jobNames); } return runner; }
- SpringBoot先加载所有的自动配置类,SpringBoot默认启动时就加载了所有的自动配置类
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值,xxxxproperties里面拿,xxxproperties和配置文件进行绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置,用户可以自己定义替换底层的bean,用户看这个组件是获取的配置文件什么值去修改
xxxxAutoConfigation ---> 组件 ---> xxxxProperties里面拿值 ---> application.properties
@EnableConfigurationProperties(CacheProperties.class)注解将properties配置文件和类相绑定再取值
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109