zoukankan      html  css  js  c++  java
  • 【Spring】IoC容器

    前言

    上一篇文章已经学习了【依赖注入】相关的知识,这里详细的介绍一下【依赖来源】。

    依赖来源

    我们把依赖来源分为依赖查找的来源和依赖注入的来源分别讨论。

    依赖查找的来源

    1. Spring BeanDefinition

    这里的Spring BeanDefinition又可以细分为:

    • 1.1 客户端自建的Spring Bean:
      如 UserService等等bean
    • 1.2 Spring容器启动时辅助的bean,可以称为Spring内建bean:
      如 org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      这里的Spring内建bean是在AnnotationConfigUtils.registerAnnotationConfigProcessors()静态方法中所注册的,即注册到BeanDefinitionMap中。
    点击查看[源码片段]
    	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
                //省略。。。
    		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
                //省略。。。
    	}
    

    2. 单例对象

    单例对象是指Spring容器启动时创建的一些单例bean,如容器中的Environment对象:
    这里的Spring内建bean是在AbstractApplicationContext.prepareBeanFactory()中,添加到BeanFactory的。

    点击查看[源码片段]
    	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
                    //省略...
    		// Register default environment beans.
    		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    		}
                    //省略...
    	}
    

    其中1.2【Spring内建bean】以及2【单例对象】中的Spring内建bean都是在 依赖查找 章节中介绍的【Spring内建依赖】中的依赖。

    依赖注入的来源

    1. Spring BeanDefinition

    同上述【依赖查找的来源】。

    2. 单例对象

    同上述【依赖查找的来源】。

    3. 非Spring容器管理对象(ResolvableDependency、非推广对象、游离对象)

    这里的【非Spring容器管理对象】是在单例对象那个方法里面一起注册的:
    AbstractApplicationContext.prepareBeanFactory()中:

    点击查看[源码片段]
    	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
                    //省略...
    		// BeanFactory interface not registered as resolvable type in a plain factory.
    		// MessageSource registered (and found for autowiring) as a bean.
    		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
                    //省略...
    	}
    

    那这几个游离对象是怎么运用的呢?
    在上一章依赖注入章节中的源码分析中,我们知道在解析注入的依赖的时候:有一个方法DefaultListableBeanFactory.doResolveDependency
    在该方法中,调用了:DefaultListableBeanFactory.findAutowireCandidates(),内部会处理我们这4个特殊的游离对象的注入。
    这也就是为什么Spring框架中,我们可以直接用如下代码注入BeanFactory或者ApplicationContext:

    @Autowired
    ApplicationContext applicationContext;
    

    4. 外部化配置

    因为到此为止还没有系统整理以及学习到外部化配置章节,并且基于学习者都是有spring基础的前提下,下例默认在META-INF/default.properties中拥有部分配置。

    点击查看[示例代码]
    @Configuration
    @PropertySource(value = "META-INF/default.properties",encoding="UTF-8")
    public class ExternalConfigurationDependencySourceDemo {
    
        @Value("${user.id:-1}")
        private Long id;
    
        @Value("${usr.name}")
        private String name;
    
        @Value("${user.resource:classpath://default.properties}")
        private Resource resource;
    
        public static void main(String[] args) {
    
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration Class(配置类) -> Spring Bean
            applicationContext.register(ExternalConfigurationDependencySourceDemo.class);
    
            // 启动 Spring 应用上下文
            applicationContext.refresh();
    
            // 依赖查找 ExternalConfigurationDependencySourceDemo Bean
            ExternalConfigurationDependencySourceDemo demo = applicationContext.getBean(ExternalConfigurationDependencySourceDemo.class);
    
            System.out.println("demo.id = " + demo.id);
            System.out.println("demo.name = " + demo.name);
            System.out.println("demo.resource = " + demo.resource);
    
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    }
    

    依赖查找的来源 VS 依赖注入的来源

    点击查看[示例代码]
    public class DependencySourceDemo {
    
        // 注入在 postProcessProperties 方法执行,早于 setter注入,也早于 @PostConstruct
        @Autowired
        private BeanFactory beanFactory;
        @Autowired
        private ResourceLoader resourceLoader;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private ApplicationEventPublisher applicationEventPublisher;
    
        @PostConstruct
        public void initByInjection() {
            System.out.println("beanFactory == applicationContext " + (beanFactory == applicationContext));
            System.out.println("beanFactory == applicationContext.getBeanFactory() " + (beanFactory == applicationContext.getAutowireCapableBeanFactory()));
            System.out.println("resourceLoader == applicationContext " + (resourceLoader == applicationContext));
            System.out.println("ApplicationEventPublisher == applicationContext " + (applicationEventPublisher == applicationContext));
        }
    
        @PostConstruct
        public void initByLookup() {
            getBean(BeanFactory.class);
            getBean(ApplicationContext.class);
            getBean(ResourceLoader.class);
            getBean(ApplicationEventPublisher.class);
        }
    
        private <T> T getBean(Class<T> beanType) {
            try {
                return beanFactory.getBean(beanType);
            } catch (NoSuchBeanDefinitionException e) {
                System.err.println("当前类型" + beanType.getName() + " 无法在 BeanFactory 中查找!");
            }
            return null;
        }
    
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration Class(配置类) -> Spring Bean
            applicationContext.register(DependencySourceDemo.class);
            // 启动 Spring 应用上下文
            applicationContext.refresh();
            // 依赖查找 DependencySourceDemo Bean
            DependencySourceDemo demo = applicationContext.getBean(DependencySourceDemo.class);
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    

    运行结果:

    beanFactory == applicationContext false
    beanFactory == applicationContext.getBeanFactory() true
    resourceLoader == applicationContext true
    ApplicationEventPublisher == applicationContext true
    当前类型org.springframework.beans.factory.BeanFactory 无法在 BeanFactory 中查找!
    当前类型org.springframework.context.ApplicationContext 无法在 BeanFactory 中查找!
    当前类型org.springframework.core.io.ResourceLoader 无法在 BeanFactory 中查找!
    当前类型org.springframework.context.ApplicationEventPublisher 无法在 BeanFactory 中查找!
    

    这里补充一下上图缺少的[外部化配置]的特点:

    类型:非常规的Spring对象依赖来源
    有无生命周期管理:无
    能否延迟初始化:不能
    能否依赖查找:不能

    Spring BeanDefinition 注册的源码位置

    BeanDefinitionRegistry
    DefaultListableBeanFactory#registerBeanDefinition
    BeanDefinitionBuilder

    单例对象注册到Spring IOC 容器的过程 以及 在依赖查找过程中的所充当的角色

    单例对象注册到Spring IOC 容器的过程
    SingletonBeanRegistry
    DefaultListableBeanFactory#registerSingleton

    在依赖查找过程中的所充当的角色:
    AbstractBeanFactory#getBean(java.lang.String)

    非Spring容器管理对象的应用

    public class ResolvableDependencySourceDemo {
    
        @Autowired
        private String value;
    
        @PostConstruct
        public void init() {
            System.out.println(value);
        }
    
        public static void main(String[] args) {
    
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            // 注册 Configuration Class(配置类) -> Spring Bean
            applicationContext.register(ResolvableDependencySourceDemo.class);
    
            applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
                // 注册 Resolvable Dependency,必须在refresh()方法之后执行,但是又必须在aitowired(bean初始化)之前执行
                beanFactory.registerResolvableDependency(String.class, "Hello,World");
            });
    
            // 启动 Spring 应用上下文
            applicationContext.refresh();
    
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    }
    

    注:游离对象可以被应用人员随意注册到spring中,但是需要保证游离对象只能被依赖注入,不能被依赖查找。

    外部化配置在spring启动过程中的源码位置

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
    org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

    DefaultListableBeanFactory#doResolveDependency源码片段

                            //下面的type在上例中第一个获取到java.lang.Long
    			Class<?> type = descriptor.getDependencyType();
                            //下面的getAutowireCandidateResolver()方法会得到一个ContextAnnotationAutowireCandidateResolver解析类
                            //该解析类会在下面的getSuggestedValue()方法中解析value的值,此时还是占位符
    			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    			if (value != null) {
    				if (value instanceof String) {
                                            //这里会把@Value注解的默认值解析出来
    					String strVal = resolveEmbeddedValue((String) value);
    

    ContextAnnotationAutowireCandidateResolver.super.getSuggestedValue(DependencyDescriptor descriptor)源码:

    	public Object getSuggestedValue(DependencyDescriptor descriptor) {
    		Object value = findValue(descriptor.getAnnotations());
    		if (value == null) {
    			MethodParameter methodParam = descriptor.getMethodParameter();
    			if (methodParam != null) {
    				value = findValue(methodParam.getMethodAnnotations());
    			}
    		}
    		return value;
    	}
    
  • 相关阅读:
    java图片裁剪原理
    代码整洁之道
    vue.js devtools安装
    Convert DateTime To Varchar with multiple Styles
    Sql Server为数值变量添加删除前导后缀(翻译)
    asp.net ckeditor 3.6.2 + ckfinder 2.1 上传图片
    转摘 IE6 动态创建 iframe 无法显示的 bug
    sql server output parameter
    windows7 无法删除文件夹 提示需要SYSTEM权限
    Kooboo 全文索引研究
  • 原文地址:https://www.cnblogs.com/1626ace/p/15349612.html
Copyright © 2011-2022 走看看