前言:
现在小伙伴们对“动态注入教程怎么做出来的”大概比较注重,小伙伴们都想要了解一些“动态注入教程怎么做出来的”的相关资讯。那么小编也在网摘上搜集了一些对于“动态注入教程怎么做出来的””的相关资讯,希望兄弟们能喜欢,朋友们一起来学习一下吧!一、开篇
在平时的开发过程中用的最多的莫属springboot了,都知道springboot中有自动注入的功能,在面试过程中也会问到自动注入,你知道自动注入是怎么回事吗,springboot是如何做到自动注入的,自动注入背后的原理是什么,今天来分析下springboot的自动注入,希望这篇文章可以解除大家心中的疑惑。
二、详述2.1、什么是自动注入
天天讲自动注入,你真正明白自动注入是怎么回事吗?举个例子来说,我们要在springboot中使用mybatis,之前的做法是什么?
1、引入依赖;
2、在配置文件中配置配置类;
3、写mybatis的配置文件或注解;
在springboot中这个步骤就减少了,减少的是第二步,不用再写一堆配置类了,步骤简化为:
1、引入依赖;
2、写mybatis的配置文件或注解;
也就是说无需再搞配置类了,就比如之前的”SqlSessionFactoryBean“,现在不用配置了,springboot为我们做了这些工作,现在看springboot引入mybatis需要加入的依赖,
<!--mybatis的依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--mysql的驱动程序--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency>
我们加入mybatis和数据库的驱动依赖,因为mybatis要使用数据库连接,所以这里少不了mysql的数据库驱动。重点看mybatis的这个依赖和之前的是不一样的,这个是”mybatis-spring-boot-starter“,再看这个依赖中都有哪些jar,
除了常见的mybatis及mybatis-spring还有一个mybatis-spring-boot-autoconfigure,这个就是今天的主角。
2.2、springboot读取spring.facotries文件(可跳过该节)
前边说到今天的主角是”mybatis-spring-boot-autoconfigure“,其实还有很多这样的依赖,大多数第三方自己实现的都会有这样一个依赖比如,前边自己实现的starter中就有这样一个”customer-spring-boot-autoconfigurer“,还有很多都是springboot自己实现的,所以无需这样的依赖。
要想知道springboot是如何进行自动注入的,唯一的方式是debug,现在开始debug之旅吧。
2.2.1、SpringApplication构造方法
springboot的启动很简单,就是下面这样一行代码
SpringApplication.run(BootServer.class);
要跟着这样一行代码走下去,追踪到了这样一句,
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
可以看的会new一个SpringApplication的实例,然后再调用其run方法,先看下new方法做了什么,最终调用的是下面的构造方法,
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); //设置初始化器,很重要 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置监听器,很重要 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
我在上面 做了注释,重点看注释部分的代码;
2.2.2、setInitializers()方法
该方法从方法名上看是要设置初始化器,其中getSpringFactoriesInstances(ApplicationContextInitializer.class)是重点。其方法定义如下,
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //SpringFactoriesLoader.loadFactoryNames是重点 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
看SpringFactoriesLoader.loadFactoryNames方法,
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); //loadSpringFactories(classLoader)方法是重点 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
把断点放在loadSpringFactroies方法内,
从上面的debug结果可以看到使用AppClassLoader读取”FACTORIES_RESOURCE_LOCATION“处的资源,AppClassLoader大家都很熟悉,就说应用类加载器,常量”FACTORIES_RESOURCE_LOCATION“指的是,
/** * The location to look for factories. * <p>Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
jar下的”META-INF/spring.factories“文件,也就是说要读取项目中jar包中的”META-INF/spring.factories“文件的内容,我在spring-boot-2.3.3.RELEASE.jar中找到这样一个文件,仅截个图,详细内容可以自己查看,
可以看到是一些列的键值对,我们看下loadSpringFactories方法最后的返回值,
这个返回值是,项目中所有jar下META-INF/spring.factories文件中的键值对组成的map。回到loadFactoryNames方法处
该方法需要的是key为”org.springframework.context.ApplicationContextInitializer“的value,该value的值有这样7个
这样我们把setInitializers方法就分析完了,其主要就是从jar包中的META-INF/spring.factories文件中获取org.springframework.context.ApplicationContextInitializer对应的值。下面看setListeners方法
2.2.3、setListeners()方法
该方法和setInitializers方法是类似的,
重点是其参数不一样,该方法的参数是ApplicationListener.class,也就是要找出org.springframework.context.ApplicationListener在spring.factories中的配置,
本人核实过这些的确是从spring.factories文件中读取的,和其内容是一致的。
写到这里其实和自动注入没有关系,如果说有关系的话是,这里认识了一个关键的类”SpringFactoriesLoader“,该类的作用就是读取jar包中META-INF/spring.facotries文件的内容。在后边的自动注入中还会出现该类的影子。继续向前。
2.3、自动注入的原理2.3.1、@SpringBootApplication注解
在启动springboot程序的时候在程序的入口都会有写上@SpringBootApplication的注解,
package com.my.template;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * 启动类 * @date 2022/6/3 21:32 */@SpringBootApplicationpublic class BootServer { public static void main(String[] args) { try { SpringApplication.run(BootServer.class); }catch (Exception e){ e.printStackTrace(); } }}
看下该注解的定义,
在该注解上还有@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解,今天重点看@EnableAutoConfiguration注解。
2.3.2、@EnableAutoConfiguration注解
该注解便是自动注入的核心注解,
重点是该注解上的下面这句话,
@Import(AutoConfigurationImportSelector.class)
看下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; }
很属性的SpringFactoriesLoader类又出现了,还是很熟悉的loadFactoryNames方法,这次的方法参数是getSpringFactoriesLoaderFactoryClass()方法,
/** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
所以SpringFactoriesLoader.loadFactoryNames是要从META-INF/spring.factories中获取key为”org.springframework.boot.autoconfigure.EnableAutoConfiguration“的value,这里可以看到有很多,从中还可以找到我自定义的和myatis的。
也就是说要把这些配置类加到spring的容器中。现在有个问题这些配置都会生效吗?
2.3.3、这些配置类都会生效吗?
上面说到自动配置会加载很多的配置类,但是这些类都会生效吗?答案是不会的,只会在特定情况下生效,以MybatisAutoConfiguration为例,
可以看的该类上有很多注解,
@ConditionalOnClass,当类路径中存在某个类标识该注解的类才会生效,也就是只有存在SqlSessionFactory、SqlSessionFactoryBean才会解析MybatisAutoConfiguration类。换句话说,要有mybatis、mybatis-spring的jar包。
@ConditionaleOnSigleCanidate,需要一个单例bean
@EnableConfigurationProperties 读取配置文件,也就是application.properites
@AutoConfigureAfter 自动配置在某个类之后
现在我们知道了一个XXAutoConfiguration类是否会生效还要看其上面的注解是怎么定义的。
三、总结
本文主要分析了springboot的自动注入原理,
1、注解@SpringBootApplication中含有三个注解,其中@EnabelAutoConfiguration和自动配置有关;
2、@EnableAutoConfiguration会读取所有jar下META-INF/spring.factories文件的内容,获取”org.springframework.boot.autoconfigure.EnableAutoConfiguration“的配置,把这些配置注入到容器;
3、@EnableAutoConfiguration注入的类是否生效,需要看其上面的注解,主要配合@ConditionaleXXX注解使用;
原创技术文章,欢迎转发、关注。
推荐阅读
你会自定义starter吗,学会这项技能完爆springboot
springboot引入mybatis的那些坑,你也会遇到
还在为不同环境不同配置而烦恼吗,一招解决
搞了这些年开发,今天总算弄懂了这三种常用的变量
标签: #动态注入教程怎么做出来的