自动配置机制 根注解@SpringBootApplication
Posted 为人师表好少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自动配置机制 根注解@SpringBootApplication相关的知识,希望对你有一定的参考价值。
前言
之前介绍到了把启动类
封装成BeanDefinition
注入进IOC容器,那么这个启动类就会跟普通的bean
一样在refresh()
中被实例化,那么显而易见作为启动类这个实例化并不简单,肯定会存在一些特殊处理,那么就需要研究一下其注解@SpringBootApplication
一、@SpringBootApplication
根注解概述
@SpringBootApplication
是SpringBoot实现自动配置的入口,它里面包含另外3个注解:
@SpringBootConfiguration
: 将启动类标注为@Configuration
,毕竟只有被标记为配置类,才能被扫描进IOC容器@EnableAutoConfiguration
: 自动配置机制,包括:引入自动配置包 + 扫描自动配置机制用到的类@ComponentScan
:扫描bean,默认会扫描启动类同级包和子包下开发人员写的bean
@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
// 自动配置类需要手动排除的
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default ;
// 自动配置类需要手动排除的
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default ;
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default ;
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default ;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
根注解组成部分一:@SpringBootConfiguration
【核心功能】:把启动类标记为@Configuration
,这样才能被Spring
框架扫描到
@SpringBootConfiguration
这个注解包含了@Configuration
,@Configuration
里面又包含了一个@Component
注解
之前介绍我们需要把启动类
注入到IOC容器中,那它也需要被@Component
标记
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 只是标注启动类同样为配置类
@Indexed
public @interface SpringBootConfiguration
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
根注解组成部分二:@EnableAutoConfiguration
该注解负责的功能也很简单,通过名字就可以看出来,开启自动配置机制
,那么如何开启?
它由@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
两部分组成:
@Import(AutoConfigurationImportSelector.class)
引入类负责按需加载自动配置类@EnableAutoConfiguration
允许自动配置类扫描开发人员写的类
这里只写了几句话介绍的很简单,下面会分开详细介绍
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包规则的注解
@Import(AutoConfigurationImportSelector.class) // 引入的类
public @interface EnableAutoConfiguration
/**
* Environment property that can be used to override when auto-configuration is
* enabled.
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default ;
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default ;
1.加载自动配置类
1) 功能介绍
在SpringBoot
出现之后,我们使用某个功能,无须在经历引入jar包、写配置文件这样的路径,直接把对应的spring-boot-starter-xxx
这样的依赖引入就可以了,那么想一下,其中肯定有对应的自动配置类来负责实现,而且每个依赖肯定有各自的自动配置类,只有这些自动配置类被执行,我们才能调用对应的功能
那么引入的这个类AutoConfigurationImportSelector
就是负责读取每个依赖中的自动配置类
2) 获取自动配置类
上面看到了引入类AutoConfigurationImportSelector
的类结构,实现了ImportSelector
,那么肯定就要看selectImports()
这个方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata)
// <1>. 判断自动装配开关是否打开
if (!isEnabled(annotationMetadata))
return NO_IMPORTS;
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// <2>. 获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
// <3> 转换成数组返回
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
<1> 自动配置类加载入口
// 按需加载自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata)
// 判断自动装配开关是否打开,默认spring.boot.enableautoconfiguration=true
if (!isEnabled(annotationMetadata))
return EMPTY_ENTRY;
// 获取EnableAutoConfiguration注解的exclude和excludeName属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// <1> 获取spring.factories中EnableAutoConfiguration的配置值
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重,原因是上面会读取所有spring-boot-starter的META-INF/spring.factories文件,可能会存在重复的,需要保证唯一
configurations = removeDuplicates(configurations);
// 获取限制候选配置的所有排除项(找到不希望自动装配的配置类)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常
checkExcludedClasses(configurations, exclusions);
// 移除exclusions
configurations.removeAll(exclusions);
// <2> 过滤出需要导入的配置类
configurations = filter(configurations, autoConfigurationMetadata);
// 配置监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
<2> 加载全部自动配置类
由上面getAutoConfigurationEntry()
进入到该getCandidateConfigurations()
,负责获取全部的自动配置的类,
下面显示了3个方法,进行了很多方法的调用
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
// getSpringFactoriesLoaderFactoryClass() 就是获取@EnableAutoConfiguration注解类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
loadSpringFactories()
由上面loadFactoryNames()
调用到方法loadSpringFactories()
,该方法就负责读取配置文件,方法代码没有展示,知道做了什么就可以
读取的配置文件路径:META-INF/spring.factories
,下面截图只展示了一个配置文件,实际不只是这一个配置文件,下面会继续介绍
通过上面截图了解到会读取配置文件,那么配置文件中有好多内容,怎么知道读取的就是org.springframework.boot.autoconfigure.EnableAutoConfiguration
在下面截图中会看到,其实很简单,这个路径就是@EnableAutoConfiguration
这个注解的路径
通过Debug,可以看到读取到的配置类有一百多个
【注意】
读取到配置类不光是这个依赖下的META-INF/spring.factories
被读取到,所有spring-boot-starter-xxx
下的META-INF/spring.factories
都会被读取到。
所以,你可以清楚滴看到,druid
数据库连接池的SpringBoot Starter
就创建了META-INF/spring.factories
文件。
如果,我们自己要创建一个spring-boot-starter
,这一步是必不可少的。
<3> 筛选自动配置类
@Conditional 条件注解
spring.factories
中这么多配置,每次启动都要全部加载么?很明显,这是不现实的。我们Debug
到后面你会发现,configurations
的值变小了。
这是因为有了这一步的筛选,才能保证按需加载
,那么如何实现,想想都知道肯定是类似于if判断的筛选,具体的实现是借助SpringBoot
提供的条件注解
- @ConditionalOnBean:当容器里有指定
Bean
的条件下 - @ConditionalOnMissingBean:当容器里没有指定
Bean
的情况下 - @ConditionalOnSingleCandidate:当指定
Bean
在容器中只有一个,或者虽然有多个但是指定首选Bean
- @ConditionalOnClass:当类路径下有指定类的条件下
- @ConditionalOnMissingClass:当类路径下没有指定类的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnExpression:基于
SpEL
表达式作为判断条件 - @ConditionalOnJava:基于
Java
版本作为判断条件 - @ConditionalOnJndi:在
JNDI
存在的条件下差在指定的位置 - @ConditionalOnNotWebApplication:当前项目不是
Web
项目的条件下 - @ConditionalOnWebApplication:当前项目是
Web
项目的条件下
示例1:SpringMVC自动配置
如下为了适应SpringMVC
,需要满足下面的条件
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET) // 是Servlet原生式Web
@ConditionalOnClass(DispatcherServlet.class) // 存在DispatcherServlet这个类
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration
/**
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/**
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
// 这里会从WebMvcProperties.class获取配置
// WebMvcProperties.class又和配置文件application.properties绑定
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties)
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
// 这个比较有意思
// 当存在这个组件,并且没有名字 然后方法就是直接返回 其实它就是为了保证注入的文件上传组件必须是按照这个名字,防止乱改名字
@Bean
@ConditionalOnBean(MultipartResolver.class) // 存在MultipartResolver这个Bean
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)// 当没有这个multipartResolver名字的Bean
public MultipartResolver multipartResolver(MultipartResolver resolver)
// Detect if the user has created a MultipartResolver but named it incorrectly
// 直接返回
return resolver;
// 省略代码...
示例2:Aop自动配置
package org.springframework.boot.autoconfigure.aop;
import org.aspectj.weaver.Advice;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration(proxyBeanMethods = false)
// 必须存在spring.aop这个值才可以
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class) // 必须包含Advice这个组件
static class AspectJAutoProxyingConfiguration
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying()
return (beanFactory) ->
if (beanFactory instanceof BeanDefinitionRegistry)
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
;
筛选执行 filter()
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata)
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 获取筛选器,也是从META-INF/spring.factories中获取,会获取到3个
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters())
invokeAwareMethods(filter);
// 这里会调用接口的match()方法进行匹配
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++)
if (!match[i])
skip[i] = true;
candidates[i] = null;
skipped = true;
if (!skipped)
return configurations;
// 获取筛选后的自动配置类
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++)
if (!skip[i])
result.add(candidates[i]);
if (logger.isTraceEnabled())
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " +(超详解)SpringBoot高级部分-自动配置+监听机制+监控+项目部署
文章目录
✨✨SpringBoot高级部分✨✨
该文章参考:黑马SpringBoot
基础部分的连接:
(超详解)SpringBoot初级部分-概述-01点这里
(超详解)SpringBoot初级部分-快速入门-02点这里
(超详解)SpringBoot初级部分-配置-03点这里
(超详解)SpringBoot初级部分-整合其他框架-04点这里
SpringBoot高级部分-自动配置-01
1.Condition
Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
思考:
SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如何知道要创建RedisTemplate的?
1.1引出问题
看一个栗子:
当我们没导入redis-start时,会报错
@SpringBootApplication
public class SpringbootDemo01Application {
public static void main(String[] args) {
//启动SpringBoot应用,返回Spring的IOC容器
ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemo01Application.class, args);
//获取Bean,redisTemplate
Object redisTemplate = run.getBean("redisTemplate");
System.out.println(redisTemplate);
}
}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'redisTemplate' available
当导入redis起步依赖后
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
org.springframework.data.redis.core.RedisTemplate@b7ff25
问题:
SpringBoot是怎么知道我们导入redis坐标的呢?
1.2案例
需求:
在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
- 导入Jedis坐标后,加载该Bean,没导入,则不加载。
- 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。
自定义注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
@Configuration
public class UserConfig {
@Bean
//@Conditional(ClassCondition.class)
@ConditionOnClass("redis.clients.jedis.Jedis")
public User user(){
return new User();
}
}
public class ClassCondition implements Condition {
/**
* @param context 上下文对象,用于获取环境,ClassLoader对象
* @param metadata 注解的元对象,可以用于注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
//1.需求:导入指定坐标后创建Bean
//思路:判断指定坐标文件是否存在
//获取注解属性值 value
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
String[] value = (String[]) map.get("value");
boolean flag = true;
try {
for (String className : value) {
Class<?> cls = Class.forName(className);
}
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
1.3总结
-
自定义条件:
① 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。 matches 方法两个参数:
context
:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。metadata
:元数据对象,用于获取注解属性。
② 判断条件:在初始化Bean时,使用 @Conditional(条件类.class)
注解
-
SpringBoot 提供的常用条件注解:
-
ConditionalOnProperty
:判断配置文件中是否有对应属性和值才初始化Bean
@Bean
@ConditionalOnProperty(name = "itcast",havingValue = "itheima")
public User user1(){
return new User();
}
itcast=itheima
-
ConditionalOnClass
:判断环境中是否有对应字节码文件才初始化Bean
-
ConditionalOnMissingBean
:判断环境中没有对应Bean才初始化Bean
2.切换内置web服务器
SpringBoot的web环境中默认使用tomcat
作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可 以很方便的进行切换。
默认:
如果要使用其他的服务器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除tomcat依赖-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入相应服务器的依赖-->
<dependency>
<artifactId>spring-boot-starter-jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
3.@Enable*注解
SpringBoot
中提供了很多Enable
开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import
注 解导入一些配置类,实现Bean
的动态加载。
提问:SpringBoot 工程是否可以直接获取jar包中定义的Bean?
答:不可以
案例:两个子模块,①子模块要得到②子模块的User类的bean(这里用编号表示)
①导入②的依赖:
<dependency>
<groupId>com.itheima</groupId>
<artifactId>springboot-embal</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
①的引导类
/**
* @ComponentScan扫描范围:当前引导类所在包及其子包
* //1.使用@ComponentScan扫描com.itheima.springbooyembal包
* //2.可以使用@Import注解,加载类,这些类都会被Spring创建,并放入IOC容器。
* //3.可以对@Import注解进行封装
*/
@SpringBootApplication
//@ComponentScan("com.itheima.springbooyembal")
//@Import(UserConfig.class)
@EnableUser
public class SpringbootDemo01Application {
public static void main(String[] args) {
//启动SpringBoot应用,返回Spring的IOC容器
ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemo01Application.class, args);
Object user = run.getBean("user");
System.out.println(user);
}
}
②的配置类
@Configuration
public class UserConfig {
@Bean
public User user(){
return new User();
}
}
②的自定义@EnableUser
注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}
4.@Import注解
@Enable*
底层依赖于@Import
注解导入一些类,使用@Import
导入的类会被Spring加载到IOC容器中。而@Import
提供4中用法:
-
导入Bean 【@Import(User.classs)
】
-
导入配置类 【@Import(UserConfig.class)
】
-
导入 ImportSelector
实现类。一般用于加载配置文件中的类
@Import(MyImportSelector.class)
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.itheima.springbooyembal.domain.User","com.itheima.springbooyembal.domain.Role"};
}
}
-
导入 ImportBeanDefinitionRegistrar
实现类。
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user",beanDefinition);
}
}
5.@EnableAutoConfiguration
@EnableAutoConfiguration
注解内部使用 @Import(AutoConfigurationImportSelector.class)
来加载配置类。- 配置文件位置:
META-INF/spring.factories
,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载 这些配置类,初始化Bean - 并不是所有的Bean都会被初始化,在配置类中使用
Condition
来加载满足条件的Bean
6.案例
需求
自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean
实现步骤
① 创建 redis-spring-boot-autoconfigure
模块
<!--引入jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
② 创建 redis-spring-boot-starter
模块吗,依赖 redis-spring-boot-autoconfigure
的模块
<!--引入自定义的autocongifure-->
<dependency>
<groupId>com.ithiema</groupId>
<artifactId>redis-spring-boot-autocongifure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
③ 在 redis-spring-boot-autoconfigure
模块中初始化 Jedis 的 Bean。并定义META-INF/spring.factories
文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
com.ithiema.redis.config.RedisAutoConfiguration
@ConfigurationProperties(prefix = "redis")
@Data
public class RedisProperties {
private String host="localhost";
private Integer port=6379;
}
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {
/**
*提供Jedis的bean
*/
@Bean
@ConditionalOnMissingBean(name="jedis")
public Jedis jedis(RedisProperties redisProperties){
System.out.println("xppmzz");
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
④ 在测试模块中引入自定义的 redis-starter 依赖,测试获取 Jedis 的Bean,操作 redis。
<dependency>
<groupId>com.itheima</groupId>
<artifactId>redis-sping-boot-start</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
@SpringBootApplication
public class SpringbootEnablrApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnablrApplication.class, args);
Jedis jedis = run.getBean(Jedis.class);
jedis.set("name11", "xppmzz");
System.out.println(jedis);
}
}
SpringBoot高级部分-监听机制-02
1.Java监听机制
SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。
Java中的事件监听机制定义了以下几个角色:
① 事件:Event,继承 java.util.EventObject 类的对象
② 事件源:Source ,任意对象Object
③ 监听器:Listener,实现 java.util.EventListener 接口的对象
2.SpringBoot监听机制
SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成 一些操作。 ApplicationContextInitializer
、SpringApplicationRunListener
、CommandLineRunner
、ApplicationRunner
-
ApplicationContextInitializer
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize");
}
}
-
SpringApplicationRunListener
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication application,String[] args) {
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
SpringApplicationRunListener.super.starting(bootstrapContext);
}
@Override
public void starting() {
System.out.println("starting...项目启动中");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("environmentPrepared...环境对象开始准备");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("contextPrepared...上下文对象开始准备");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("contextLoaded...上下文对象开始加载");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("started...上下文对象加载完成");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("running...项目启动完成,开始运行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("failed...项目启动失败");
}
}
-
CommandLineRunner
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run");
System.out.println(Arrays.asList(args));
}
}
-
ApplicationRunner
/**
* 当项目启动后执行run方法:可以用于提前加载缓存redis
*/
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run");
System.out.println(Arrays.asList(args.getSourceArgs()));
}
}
其中CommandLineRunner
和ApplicationRunner
的实现不需要配置文件里配置东西:
要想输出的内容不为[]
可以在Edit Configurations...
配置:
要想ApplicationContextInitializer
和SpringApplicationRunListener
可以实现,必须在resources
目录下的META-INF
文件夹下的配置spring.factories
(名字必须一致,这样spring才能扫描到):
org.springframework.context.ApplicationContextInitializer=com.itheima.springbootlistener.listener.MyApplicationContextInitializer
#所继承类的全类名=实现类的全类名
org.springframework.boot.SpringApplicationRunListener=com.itheima.springbootlistener.listener.MySpringApplicationRunListener
SpringBoot高级部分-监控-03
SpringBoot自带监控功能Actuator
,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性 、日志信息等。
1.使用步骤
① 导入依赖坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
② 访问http://localhost:8080/actuator
2.SpringBoot 监控使用
路径 描述 /beans 描述应用程序上下文里全部的Bean,以及它们的关系 /env 获取全部环境属性 /env/{name} 根据名称获取特定的环境属性值 /health 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 /info 获取应用程序的定制信息,这些信息由info打头的属性提供 /mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 /metrics 报告各种应用程序度量信息,比如内以上是关于自动配置机制 根注解@SpringBootApplication的主要内容,如果未能解决你的问题,请参考以下文章