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

    前言

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

    依赖注入 - 分类

    因为自己是基于小马哥的脉络来学习,并且很认可小马哥梳理的分类方式,下面按照小马哥思想为【依赖注入】分类:

    1. Setter方法注入
    2. 构造器注入
    3. 字段注入
    4. 方法注入
    5. 接口回调注入

    1. Setter方法注入

    1.1 手动模式

    1.1.1 XML配置
        <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
            <property name="user" ref="superUser" />
        </bean>
    

    Spring容器里面有另外一个bean,名字是superUser。
    这里user bean的注入就是使用的setter注入。可以在set方法上打断点验证。
    后续我们会进行源码分析。

    1.1.2 Java注解配置
        @Bean
        public UserHolder userHolder(User user) {
            UserHolder userHolder = new UserHolder();
            userHolder.setUser(user);
            return userHolder;
        }
    
    1.1.3 API配置
        /**
         * 为 UserHolder 生成 BeanDefinition
         */
        private static BeanDefinition createUserHolderBeanDefinition() {
            BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
            definitionBuilder.addPropertyReference("user", "superUser");
            return definitionBuilder.getBeanDefinition();
        }
    

    1.2 自动模式

    1.2.1 byName
        <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder" autowire="byName">
        </bean>
    

    自动装配默认是byType,不是byName

    1.2.2 byType
        <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder" autowire="byType">
        </bean>
    

    2. 构造器注入

    2.1 手动模式

    2.1.1 XML配置
        <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
            <constructor-arg name="user" ref="superUser" />
        </bean>
    

    Spring容器里面有另外一个bean,名字是superUser。

    2.1.2 Java注解配置
        @Bean
        public UserHolder userHolder(User user) {
            return new UserHolder(user);
        }
    
    2.1.3 API配置
    
        /**
         * 为 UserHolder 生成 BeanDefinition
         */
        private static BeanDefinition createUserHolderBeanDefinition() {
            BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
            definitionBuilder.addConstructorArgReference("superUser");
            return definitionBuilder.getBeanDefinition();
        }
    

    2.2 自动模式

    2.2.1 constructor
        <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder"
              autowire="constructor">
        </bean>
    

    这里的UserHolder类中存在如下构造器:

        public UserHolder(User user) {
            this.user = user;
        }
    

    构造器自动注入的时候会去spring容器中获取type为User的bean,自动注入。

    3. 字段注入

    字段注入只有手动模式,并且只支持Java注解配置。
    @Autowired
    @Resource
    @Inject(可选)

        @Autowired
        private UserHolder userHolder;
        @Resource(name = "xxxx")
        private UserHolder userHolder2;
        @Inject
        private UserHolder userHolder2;
    

    4. 方法注入

    方法注入只有手动模式,并且只支持Java注解配置。
    @Autowired
    @Resource
    @Inject(可选)
    @Bean

        @Autowired
        public void init1(UserHolder userHolder) {
            this.userHolder = userHolder;
        }
        @Resource
        public void init2(UserHolder userHolder2) {
            this.userHolder2 = userHolder2;
        }
        @Bean
        public UserHolder userHolder(User user) {
            return new UserHolder(user);
        }
    

    项目中有使用到如下的一种方法注入:

    @Autowired
    public MyClass(Map<String, MyApplicationEvent> eventMap) {
           //any logic
    }
    

    这里是自己的bean的构造器,参数eventMap会被容器自动构建一个map,所有MyApplicationEvent类型的bean都会被装载,map的key是bean的名字。

    5. 接口回调注入Aware

    public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware, ApplicationContextAware {
    
        private static BeanFactory beanFactory;
    
        private static ApplicationContext applicationContext;
    
    
        public static void main(String[] args) {
    
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            // 注册 Configuration Class(配置类) -> Spring Bean
            context.register(AwareInterfaceDependencyInjectionDemo.class);
    
            // 启动 Spring 应用上下文
            context.refresh();
    
            System.out.println(beanFactory == context.getBeanFactory());
            System.out.println(applicationContext == context);
    
            // 显示地关闭 Spring 应用上下文
            context.close();
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            AwareInterfaceDependencyInjectionDemo.applicationContext = applicationContext;
        }
    }
    

    之前倒是熟悉这个接口项目中也在使用,但是没想到分类居然是依赖注入相关的。


    各种注入方式的比较

    上面介绍来大部分注入方式,其实在我自己的项目中用得最多的自然就是字段注入。相信大部分Java开发都是,但是熟悉其他注入方式对spring应用的扩展与理解有更好的帮助。

    注入方式 适合于
    构造器注入 低依赖
    Setter 多依赖
    字段注入 便利性
    方法注入 声明类

    依赖注入 - 特殊注入方式说明

    基础类型注入
    集合类型注入
    限定注入
    延迟依赖注入

    依赖处理过程

    依赖处理过程基础知识

    入口:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
    依赖描述符:org.springframework.beans.factory.config.DependencyDescriptor
    自动绑定候选对象处理器:org.springframework.beans.factory.support.AutowireCandidateResolver

    依赖处理过程解析

    我们先来分析一下DefaultListableBeanFactory.resolveDependency()的方法:

    点击查看代码
    /**
     * 参数1:DependencyDescriptor是依赖描述符
     * 参数2:requestingBeanName,待注入的bean名称
     */
    @Override
    @Nullable
    public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
    		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
    	descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    	//注入类型如果是Optional,特殊处理。
    	if (Optional.class == descriptor.getDependencyType()) {
    		return createOptionalDependency(descriptor, requestingBeanName);
    	}
    	//注入类型如果是ObjectFactory或ObjectProvider,特殊处理。
    	else if (ObjectFactory.class == descriptor.getDependencyType() ||
    			ObjectProvider.class == descriptor.getDependencyType()) {
    		return new DependencyObjectProvider(descriptor, requestingBeanName);
    	}
    	//注入类型如果是javax.inject.Provider,特殊处理。
    	else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
    		return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    	}
    	else {
    	    //常规依赖处理,使用[自动绑定候选对象处理器]处理一个事情,先放着。。。
    		Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    				descriptor, requestingBeanName);
    		if (result == null) {
    		    //真正的处理依赖在这里
    			result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    		}
    		return result;
    	}
    }
    

    然后是:DefaultListableBeanFactory.doResolveDependency

    点击查看代码
    
    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
    		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
        //处理嵌套注入时候的一个保护点,获取上次注入点(InjectionPoint),
    	//内部使用的是ThreadLocal去存储InjectionPoint,所以这里获取的是单线程内的上次的注入点
    	InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    	try {
    	    //获取描述符的shortcut
    		Object shortcut = descriptor.resolveShortcut(this);
    		if (shortcut != null) {
    			return shortcut;
    		}
            //获取注入类型,未深入
    		Class<?> type = descriptor.getDependencyType();
    		//获取建议值,未深入
    		Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    		if (value != null) {
    			if (value instanceof String) {
    				String strVal = resolveEmbeddedValue((String) value);
    				BeanDefinition bd = (beanName != null && containsBean(beanName) ?
    						getMergedBeanDefinition(beanName) : null);
    				value = evaluateBeanDefinitionString(strVal, bd);
    			}
    			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
    			try {
    				return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
    			}
    			catch (UnsupportedOperationException ex) {
    				// A custom TypeConverter which does not support TypeDescriptor resolution...
    				return (descriptor.getField() != null ?
    						converter.convertIfNecessary(value, type, descriptor.getField()) :
    						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
    			}
    		}
            //判断是不是处理多个bean,比如Map,Array,List的注入就是在这里处理。
    		Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    		if (multipleBeans != null) {
    			return multipleBeans;
    		}
            //重点:根据要注入的描述符,bean的名字,获取可以注入的容器上下文里面可以注入的候选bean
    		//20210926分析到这里,接下来进入该方法分析
    		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    		if (matchingBeans.isEmpty()) {
    			if (isRequired(descriptor)) {
    				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    			}
    			return null;
    		}
    
    		String autowiredBeanName;
    		Object instanceCandidate;
    
    		if (matchingBeans.size() > 1) {
    			autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    			if (autowiredBeanName == null) {
    				if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
    					return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
    				}
    				else {
    					// In case of an optional Collection/Map, silently ignore a non-unique case:
    					// possibly it was meant to be an empty collection of multiple regular beans
    					// (before 4.3 in particular when we didn't even look for collection beans).
    					return null;
    				}
    			}
    			instanceCandidate = matchingBeans.get(autowiredBeanName);
    		}
    		else {
    			// We have exactly one match.
    			Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
    			autowiredBeanName = entry.getKey();
    			instanceCandidate = entry.getValue();
    		}
    
    		if (autowiredBeanNames != null) {
    			autowiredBeanNames.add(autowiredBeanName);
    		}
    		if (instanceCandidate instanceof Class) {
    			instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
    		}
    		Object result = instanceCandidate;
    		if (result instanceof NullBean) {
    			if (isRequired(descriptor)) {
    				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    			}
    			result = null;
    		}
    		if (!ClassUtils.isAssignableValue(type, result)) {
    			throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
    		}
    		return result;
    	}
    	finally {
    		ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    	}
    }
    

    依赖描述符DependencyDescriptor类定义

    点击查看代码
    /**
     * Descriptor for a specific dependency that is about to be injected.
     * Wraps a constructor parameter, a method parameter or a field,
     * allowing unified access to their metadata.
     * (一个特定被注入的依赖的描述符。包含一个构造器、方法或者字段。包装构造函数参数、方法参数或字段,允许对其元数据进行统一访问。)
     * @author Juergen Hoeller
     * @since 2.5
     */
    public class DependencyDescriptor extends InjectionPoint implements Serializable {
    

    注入原理

    • @Autowired注入
    • @Inject注入
    • @Resource注入
    • 自定义依赖注入
  • 相关阅读:
    php操作redis cluster集群成功实例
    js算法-快速排序(Quicksort)
    如何编写自己的虚拟DOM
    一文带你看透kubernetes 容器编排系统
    SpringBoot AOP控制Redis自动缓存和更新
    机器学习中的numpy库
    时间戳转自定义日期格式和刚刚、十分钟前等友好日期格式
    Python数据处理实例
    Spark1.x和2.x如何读取和写入csv文件
    掌握这个Python小技巧,轻松构建cytoscape导入文件
  • 原文地址:https://www.cnblogs.com/1626ace/p/15172642.html
Copyright © 2011-2022 走看看