- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Springboot自动加载配置的原理解析由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
。
以下注解都在springboot的自动化配置包中:spring-boot-autoconfigure。读者朋友可以跟着一下步骤走一遍,应该对自动配置就有一定的认知了.
1.springboot程序的入口是在启动类,该类有个关键注解SpringBootApplication 。
@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.打开SpringBootApplication注解,上面有个关键注解EnableAutoConfiguration 。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration { //……}
3.EnableAutoConfiguration上有个@Import(AutoConfigurationImportSelector.class),注意AutoConfigurationImportSelector, 。
@Import作用创建一个AutoConfigurationImportSelector的bean对象,并且加入IoC容器 。
//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector//此处只贴了关键方法protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; }
4.AutoConfigurationImportSelector类中的getCandidateConfigurations方法代码如上,其调用了SpringFactoriesLoader的loadFactoryNames方法,来获取 。
configurations,此configurations列表其实就是要被自动花配置的类。SpringFactoriesLoader的两个重要方法如下:
//org.springframework.core.io.support.SpringFactoriesLoader//只贴了两个关键方法 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";//此方法返回的是即将要被自动化配置的类的全限定类名,是从META-INF/spring.factories配置的,配置文件中有个org.springframework.boot.autoconfigure.EnableAutoConfiguration 其后面可配置多个想被自动花配置的类 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullab等le ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));//META-INF/spring.factories result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
5.举例分析,我们在spring.factories中可以看到org.springframework.boot.autoconfigure.EnableAutoConfiguration后有一个org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,说明springboot希望redis能够自动化配置。接着我们打开RedisAutoConfiguration源码查看。此处我故意没复制源码,用的截图,可以看到截图直接有报错,编译错误,错误的原因是我们还没添加spring-boot-starter-data-redis的依赖。**这里有个问题,为什么明明代码都报错,Cannot resolve symbol xxx(未找到类),但是我们的项目依然可以启动?不信你建立一个简单的springboot项目,只添加web依赖,手动打开RedisAutoConfiguration,发现是报红错的,但是你启动项目,发现没任何问题,why??**这个问题后面再解答,先接着看自动配置的问题.
6.先把RedisAutoConfiguration源码复制出来方便我写注释,上面用截图主要是让大家看到报错 。
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RedisOperations.class)@EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }}
看源码可知RedisAutoConfiguration上有一个Configuration和ConditionalOnClass注解,先分析这两个。首先Configuration注解,代表这是个Java config配置类,和spring配置bean的xml文件是一个作用,都是用来实例化bean的,**但是注意还有个@ConditionalOnClass(RedisOperations.class)注解,这个注解的作用是当RedisOperations.class这个类被找到后才会生效,如果没找到此类,那么整个RedisAutoConfiguration就不会生效。**所以当我们引入了redis的依赖,springboot首先会通过RedisAutoConfiguration的方法redisTemplate给我们设置一个默认的redis配置,当然这个方法上也有个注解@ConditionalOnMissingBean(name = "redisTemplate"),就是当我们没有手动配redisTemplate这个bean它才会调用这个默认的方法,注入一个redisTemplate到IoC容器,所以一般情况我们都是手动配置这个redisTemplate,方便我们设置序列化器,如下:
@Configurationpublic class RedisConfig { /** * 设置 redisTemplate 的序列化设置 * * @param redisConnectionFactory * @return */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { // 1.创建 redisTemplate 模版 RedisTemplate<String, Object> template = new RedisTemplate<>(); // 2.关联 redisConnectionFactory template.setConnectionFactory(redisConnectionFactory); // 3.创建 序列化类 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 4.设置可见度 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 5.启动默认的类型 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // 6.序列化类,对象映射设置 jackson2JsonRedisSerializer.setObjectMapper(om); // 7.设置 value 的转化格式和 key 的转化格式 template.setValueSerializer(jackson2JsonRedisSerializer); template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; }}
RedisAutoConfiguration上还有一下两个注解,作用是从配置文件读取redis相关的信息,ip、端口、密码等 。
@EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
。
所有的@Condition注解(包括衍生的)其实都对应一个具体的实现,这个实现类里面有个判断方法叫做matches,返回的是个布尔类型判断值.
打开ConditionalOnClass源码如下,其Conditional注解传递的是个OnClassCondition.class,这就其对应的判断类,也就是说,当我们使用ConditionalOnClass注解时,其实际上调用的是OnClassCondition来判断的 。
@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Conditional(OnClassCondition.class)public @interface ConditionalOnClass { /** * The classes that must be present. Since this annotation is parsed by loading class * bytecode, it is safe to specify classes here that may ultimately not be on the * classpath, only if this annotation is directly on the affected component and * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to * use this annotation as a meta-annotation, only use the {@link #name} attribute. * @return the classes that must be present */ Class<?>[] value() default {}; /** * The classes names that must be present. * @return the class names that must be present. */ String[] name() default {};}
ConditionalOnClass类图如下,它继承了condition接口 。
打开Condition接口如下,查看注释,注释中有说明 **条件判断是在bean定义即将注册到容器之前进行的,**看过springIoC源码的同学应该知道,spring创建一个对象的过程是当服务启动后,先读取xml配置文件(或者通过注解),根据配置文件先定义一个BeanDefinition,然后把这个bean给放到容器(在spring中实际就是一个Map),然后在根据bean定义,通过反射创建真正的对象。反射会触发类加载,当condition条件不满足时,根据如下注释可知,bean定义后续都被拦截了,连注册都不行,所以自然就不可能通过反射创建对象,不反射自然不会触发类加载,不触发类加载那么RedisAutoConfiguration当然啊不会加载,它不加载,那么即使它里面引用了一个不存在的类也不会有啥问题.
上面说的很绕,表达的不是很好,要想看懂以上部分需要掌握两方面的知识:
。
spring-boot-autoconfigure.jar这个包中的RedisAutoConfiguration都报红色错误了,那么spring官方是怎么打包出来spring-boot-autoconfigure.jar的??怎么给我们提供了一个报错的包呢 。
//TODO 。
。
到此这篇关于Springboot自动加载配置原理的文章就介绍到这了,更多相关Springboot自动加载配置原理内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.csdn.net/chen462488588/article/details/120769282 。
最后此篇关于Springboot自动加载配置的原理解析的文章就讲到这里了,如果你想了解更多关于Springboot自动加载配置的原理解析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我一直在使用 AJAX 从我正在创建的网络服务中解析 JSON 数组时遇到问题。我的前端是一个简单的 ajax 和 jquery 组合,用于显示从我正在创建的网络服务返回的结果。 尽管知道我的数据库查
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我在尝试运行 Android 应用程序时遇到问题并收到以下错误 java.lang.NoClassDefFoundError: com.parse.Parse 当我尝试运行该应用时。 最佳答案 在这
有什么办法可以防止etree在解析HTML内容时解析HTML实体吗? html = etree.HTML('&') html.find('.//body').text 这给了我 '&' 但我想
我有一个有点疯狂的例子,但对于那些 JavaScript 函数作用域专家来说,它看起来是一个很好的练习: (function (global) { // our module number one
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 8 年前。 Improve th
我需要编写一个脚本来获取链接并解析链接页面的 HTML 以提取标题和其他一些数据,例如可能是简短的描述,就像您链接到 Facebook 上的内容一样。 当用户向站点添加链接时将调用它,因此在客户端启动
在 VS Code 中本地开发时,包解析为 C:/Users//AppData/Local/Microsoft/TypeScript/3.5/node_modules/@types//index而不是
我在将 json 从 php 解析为 javascript 时遇到问题 这是我的示例代码: //function MethodAjax = function (wsFile, param) {
我在将 json 从 php 解析为 javascript 时遇到问题 这是我的示例代码: //function MethodAjax = function (wsFile, param) {
我被赋予了将一种语言“翻译”成另一种语言的工作。对于使用正则表达式的简单逐行方法来说,源代码过于灵活(复杂)。我在哪里可以了解更多关于词法分析和解析器的信息? 最佳答案 如果你想对这个主题产生“情绪化
您好,我在解析此文本时遇到问题 { { { {[system1];1;1;0.612509325}; {[system2];1;
我正在为 adobe after effects 在 extendscript 中编写一些代码,最终变成了 javascript。 我有一个数组,我想只搜索单词“assemble”并返回整个 jc3_
我有这段代码: $(document).ready(function() { // }); 问题:FB_RequireFeatures block 外部的代码先于其内部的代码执行。因此 who
背景: netcore项目中有些服务是在通过中间件来通信的,比如orleans组件。它里面服务和客户端会指定网关和端口,我们只需要开放客户端给外界,服务端关闭端口。相当于去掉host,这样省掉了些
1.首先贴上我试验成功的代码 复制代码 代码如下: protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
什么是 XML? XML 指可扩展标记语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。 你可以通过本站学习 X
【PHP代码】 复制代码 代码如下: $stmt = mssql_init('P__Global_Test', $conn) or die("initialize sto
在SQL查询分析器执行以下代码就可以了。 复制代码代码如下: declare @t varchar(255),@c varchar(255) declare table_cursor curs
前言 最近练习了一些前端算法题,现在做个总结,以下题目都是个人写法,并不是标准答案,如有错误欢迎指出,有对某道题有新的想法的友友也可以在评论区发表想法,互相学习🤭 题目 题目一: 二维数组中的
我是一名优秀的程序员,十分优秀!