Spring Ioc容器依赖注入

1、时序图

IOC容器的依赖注入是建立在数据BeanDefinition准备好的前提之下的。依赖注入的发生有两种情况:系统第一次向容器索要bean的时候;bean在配置的时候设置了Lazy-init属性,该属性会让容器完成预实例化,预实例化就是一个依赖注入的过程。Bean的依赖注入看下DefaultListableBeanFactory的基类AbstractBeanFactory的getBean里面的实现来看下bean依赖注入的全流程。IOC依赖注入的时序图如下所示:


图10、spring ioc容器bean注入时序图

在时序图中可以看到依赖注入主要有两个阶段:instantiate即bean的实例化;populateBean即对bean所依赖的引用以及属性进行初始化。

2、instantiate

<pre name="code" class="java">public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (beanDefinition.getMethodOverrides().isEmpty()) {
			Constructor<?> constructorToUse;
			synchronized (beanDefinition.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class clazz = beanDefinition.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
								public Constructor run() throws Exception {
									return clazz.getDeclaredConstructor((Class[]) null);
								}
							});
						}
						else {
							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
						}
						beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Exception ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(beanDefinition, beanName, owner);
		}
	}


 

代码层次比较清晰,beanDefinition是从之前载入的BeanDefinition中获取的根节点数据,一种是使用BeanUtils进行实例化,其实就是反射机制进行实例化,找出类的构造函数,再使用构造函数ctor.newInstance(args)实例化一个对象。另外一种就是使用CGLIB进行实例化,CGLIB构造一个实例的基本实现流程如下:

public Object instantiate(Constructor ctor, Object[] args) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(this.beanDefinition.getBeanClass());
			enhancer.setCallbackFilter(new CallbackFilterImpl());
			enhancer.setCallbacks(new Callback[] {
					NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(),
					new ReplaceOverrideMethodInterceptor()
			});
			return (ctor == null) ? 
					enhancer.create() : 
					enhancer.create(ctor.getParameterTypes(), args);
		}

首先生成一个Enhancer对象,在使用enhancer对基类、回调方法进行设置,最后使用cglib的create生成一个bean对象。这里简单阐述一下反射进行实例化的一个流程。

ClassInstance ci05 = null;
        //额外的思考 在第二种类实例化的方式中有没有一种方法实现有参数的构造方式
        //获得类的构造信息
        Constructor[] ctor = Class.forName("ClassInstance").getDeclaredConstructors();
        //找到我们需要的构造方法
        for(int i=0;i<ctor.length;i++ ){
            Class[] cl = ctor[i].getParameterTypes();
            if(cl.length == 1){
                //实例化对象
                ci05 = (ClassInstance) Class.forName("ClassInstance").getConstructor(cl).newInstance(new Object[]{"05"});
            }
        }
        ci05.fun();

基本流程就是上述。类实例化都是在jvm里面进行的,所以实例化的步骤是如下的:

1、  类加载到jvm;

2、  Jvm对类进行链接;

3、  Jvm实例化对象。

上述代码流程里面Class.forName("ClassInstance")这个的作用就是前两步功能;第三步就是ctor. newInstance(args),这样就是完成了一个类实例化的过程,一般我们在系统里面new关键字就已经将上述三个步骤全部涵盖了。

在Spring IOC实例化的过程中还有一个地方需要注意的:

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
		....
		}

值得注意的地方就是if (sharedInstance != null && args == null) 这段代码的判断是看这个bean是从beanFactory里面获取(false)还是从factoryBean来生产(true)FactoryBean.getObject(),FactoryBean是一个工厂模式。Beanfactory是所有bean的出处,即使FactoryBean也是从Beanfactory来的,那么FactoryBean是做什么的呢?我们看下它类的继承关系就比较容易理解:


图11、FacotryBean的类关系图

也就是说FactoryBean其实是各类特定类型beanfactory的工厂,说起来有些拗口,其实就是里面出来的都是一个factory,例如RMI,JNDI,PROXY等等类型的factory。就可以由其生产。它的设计模式是一个工厂方法。

 

3、populateBean

populateBean就是bean相关属性以及依赖类的加载,这里会触发相关bean的依赖注入。

在这个过程中也有两个阶段,如下代码所示:
List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				Object originalValue = pv.getValue();
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}
				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}

第一个阶段就是resolveValueIfNecessary,这个就是将改类相关的依赖类找出来,这个有很多类型,list,set,map以及object等等,最终模式都是回归到resolveReference上来,这个功能就是最终使用beanFacotry.getBean()来完成bean的依赖实例化。

第二个阶段就是setPropertyValues,这个过程就是将bean的set和get方法读出来,将这些类set进去或是read出来。对于在xml中手动配置的bean ref这个比较容易理解,而对于系统自动依赖注入的方式这里简单介绍下:

Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。

  @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

  @Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常

  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常

  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常

4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

@Autowired 与@Resource的区别:

1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

@Autowired()

@Qualifier("baseDao")

private BaseDaobaseDao;

3、@Resource(这个注解属于J2EE的),默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

@Resource(name="baseDao")

private BaseDao baseDao;

4、IOC依赖注入设计模式

在上面讲到FactoryBean采用的是工厂方法,我们在这里讲述下改设计模式:

在FactoryBean的设计中Create就是FactoryBean,而ConcreateCreate就相当于HttpInvokerProxyFactoryBean,FactoryMethod就是getObject(),获取到的object其实就是一个产品,例如ProxyFactory,而这个proxyFacory就可以生产中各类的Proxy出来,这个ProxyFactory就相当于ConcreateProduct了。

工厂方法的优点:

首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,减少模块间的耦合。 

其次,工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。

再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不表,系统中的上层模块就不要发生变化,因为产品类的实例化工作是由工厂类负责,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySql切换到Oracle,需要改动地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。

最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特原则,我不需要的就不要去交流;也符合依赖倒转原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类。




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值