本篇文章给大家分享的是有关Spring中IoC源码怎么写,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。
Spring为IoC提供了2种方式,一种是基于xml,另一种是基于注解。
<Bean>标签来定义bean,进行管理。
@Bean注解来定义bean,进行管理。
本次文章我们就来分析下基于注解的IoC原理,在看文章之前我们可以带一些疑问,这样有助于我们更好的理解。
@Bean是干什么用的?
@Controller、@Service又是干啥的?
@CompoentScan注解是怎么起作用的?
Spring是怎么发现@Bean、@Controller、@Service这些注解修饰的类的?
发现之后是怎么注册到IOC容器中的?
IOC容器到底是个啥?
首先看下段代码:
AnnotationConfigApplicationContext aac = new AnnotationConfigApplicationContext("com.mydemo");
AnnotationConfigApplicationContext可以实现基于Java的配置类(包括各种注解)加载Spring的应用上下文。避免使用application.xml进行配置。相比XML配置,更加便捷。
主要类或接口说明:
GenericApplicationContext——通用应用上下文,内部持有一个DefaultListableBeanFactory实例,这个类实现了BeanDefinitionRegistry接口,可以在它身上使用任意的bean definition读取器。典型的使用案例是:通过BeanFactoryRegistry接口注册bean definitions,然后调用refresh()方法来初始化那些带有应用上下文语义(org.springframework.context.ApplicationContextAware)的bean,自动探测org.springframework.beans.factory.config.BeanFactoryPostProcessor等。
BeanDefinitionRegistry——用于持有像RootBeanDefinition和 ChildBeanDefinition实例的bean definitions的注册表接口。DefaultListableBeanFactory实现了这个接口,因此可以通过相应的方法向beanFactory里面注册bean。GenericApplicationContext内置一个DefaultListableBeanFactory实例,它对这个接口的实现实际上是通过调用这个实例的相应方法实现的。
AbstractApplicationContext——ApplicationContext接口的抽象实现,没有强制规定配置的存储类型,仅仅实现了通用的上下文功能。这个实现用到了模板方法设计模式,需要具体的子类来实现其抽象方法。自动通过registerBeanPostProcessors()方法注册BeanFactoryPostProcessor, BeanPostProcessor和ApplicationListener的实例用来探测bean factory里的特殊bean——对比1分析
AnnotationConfigRegistry——注解配置注册表。用于注解配置应用上下文的通用接口,拥有一个注册配置类和扫描配置类的方法。
//默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register() //方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程 public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } //最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean自动注册到容器中 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { //调用无参构造函数,初始化AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner this(); register(annotatedClasses); refresh(); } //该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其注册到容器中 public AnnotationConfigApplicationContext(String... basePackages) { //初始化ClassPathBeanDefinitionScanner和AnnotatedBeanDefinitionReader this();//step1 //扫描包、注册bean scan(basePackages);//step2 refresh();//step3 }
主要属性:
AnnotatedBeanDefinitionReader——BeanDefinition解析器用来解析带注解的bean
ClassPathBeanDefinitionScanner——bean的扫描器 用来扫描类
注册解析传入的配置类(使用类配置的方式进行解析)
调用容器的refresh方法初始化容器
这里我们用的是最后一种构造函数,即传入一个包路径。
首先看step1,调用了本类的无参构造函数:
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
然后初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
我们来看下ClassPathBeanDefinitionScanner的构造函数
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); }
继续跟踪下去,最后调用的是这个方法:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); //为容器设置加载Bean定义的注册器 this.registry = registry; //是否使用默认过滤规则 if (useDefaultFilters) { registerDefaultFilters(); } //设置环境 setEnvironment(environment); //为容器设置资源加载器 setResourceLoader(resourceLoader); }
这里面最主要的是registerDefaultFilters()方法,初始化spring扫描默认过滤规则,对应@ComponentScan注解,如果没有自定义规则,就初始化默认过滤规则。
这里调用的是ClassPathScanningCandidateComponentProvider类中的registerDefaultFilters()方法:
//向容器注册过滤规则 @SuppressWarnings("unchecked") protected void registerDefaultFilters() { //向要包含的过滤规则中添加@Component注解类 //@Service和@Controller都是Component,因为这些注解都添加了@Component注解 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); //获取当前类的类加载器 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { //向要包含的过滤规则添加JavaEE6的@ManagedBean注解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { //向要包含的过滤规则添加@Named注解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
这里面有两个关键变量:
private final List<TypeFilter> includeFilters = new LinkedList<>();
private final List<TypeFilter> excludeFilters = new LinkedList<>();
includeFilters表示要包含的注解,即只有包含includeFilters中的注解,才会被扫描
excludeFilters表示要排除的注解,即包含excludeFilters中的注解不会被扫描
在这个方法中,includeFilters集合中添加了@Component、JavaEE6的@ManagedBean和JSR-330的@Named注解
而excludeFilters集合没做任何变动,即没有要排除的注解
总结:
所以默认规则就是,只要包含了@Component、JavaEE6的@ManagedBean和JSR-330的@Named这3个注解中的任意一个,就会被扫描
以上就是Spring中IoC源码怎么写,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。