Spring IoC深刻理解

本文相关代码(来自官方源码spring-test模块)请参见spring-demysify org.springframework.mylearntest包下。

三种注入方式

1.构造方法注入java

public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) 10{
	this.newsListener = newsListner;
	this.newPersistener = newsPersister;
}

构造方法注入。 这种注入方式的优势就是,对象在构造完成以后,即已进入就绪状态,能够立刻使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而经过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。并且在Java中,构造方法没法被继承,没法设置默认值。对于非必须的依赖处理,可能须要引入多个构造方法,而参数数量的变更可能形成维护上的不便。mysql

2.setter 方法注入git

public class FXNewsProvider {
	private IFXNewsListener newsListener;
	private IFXNewsPersister newPersistener;
	
	public IFXNewsListener getNewsListener() {
	return newsListener;
	}
	public void setNewsListener(IFXNewsListener newsListener) {
	this.newsListener = newsListener;
	}
	
	public IFXNewsPersister getNewPersistener() {
	return newPersistener;
	}
	public void setNewPersistener(IFXNewsPersister newPersistener) {
	this.newPersistener = newPersistener;
	}
}

setter方法注入。由于方法能够命名, 因此setter方法注入在描述性上要比构造方法注入好一些。 另外, setter方法能够被继承,容许设置默认值,并且有良好的IDE支持。缺点固然就是对象没法在构造完成后立刻进入就绪状态。github

  1. 接口注入
    FXNewsProvider为了让IoC Service Provider为其注入所依赖的IFXNewsListener,首先须要实现IFXNewsListenerCallable接口,这个接口会声明一个injectNewsListner方法(方法名随意),该方法的参数,就是所依赖对象的类型。这样, InjectionServiceContainer对象,即对应的IoCService Provider就能够经过这个接口方法将依赖对象注入到被注入对象FXNewsProvider当中。

接口注入。从注入方式的使用上来讲,接口注入是如今不甚提倡的一种方式,基本处于“退役状态”。由于它强制被注入对象实现没必要要的接口,带有侵入性。而构造方法注入和setter方法注入则不须要如此。spring

IoC Service Provider的职责

业务对象的构建管理:
在IoC场景中,业务对象无需关心所依赖对象如何构建如何得到,但这部分工做始终须要有人来作。因此,IoC Service Provider须要将对象的构建逻辑从客户端那里剥离出来,以避免这部分逻辑污染业务对象的实现。
业务对象间的依赖绑定:
对于IoC Service Provider来讲,这个职责是最艰巨也是最重要的,这是它的最终使命之所在。若是不能完成这个职责,那么,不管业务对象如何的“呼喊”,也不会获得依赖对象的任何反应(最多见的却是会收到一个NullPointerException)。IoC Service Provider 经过结合以前构建和管理的全部业务对象,以及各个业务对象间能够识别依赖关系,将这些对象所依赖的对象注绑定,从而保证每一个业务对象在使用的时候,能够处于就绪状态。sql

IoC Service Provider 如何管理对象间的依赖关系

如何记录对象之间的依赖关系:apache

  • 它能够经过最基本的文本文件来记录被注入对象和其依赖对象之间的对应关系;
  • 它也能够经过描述性较强的XML文件格式来记录对应信息;
  • 它还能够经过编写代码的方式来注册这些对应信息;
  • 甚至,若是愿意,它也能够经过语音方式来记录对象间的依赖注入关系(“嗨,它要一个这种类型的对象,拿这个给它”)
  1. 直接编码式
    当前大部分的IoC容器都应该支持直接编码方式,好比PicoContainer、 Spring、 Avalon等。
IoContainer container = ...;
container.register(FXNewsProvider.class,new FXNewsProvider());
container.register(IFXNewsListener.class,new DowJonesNewsListener());
...
container.bind(IFXNewsListenerCallable.class, container.get(IFXNewsListener.class));
...
FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class);
newProvider.getAndPersistNews();

经过bind方法将“被注入对象”(由IFXNewsListenerCallable接口添加标志)所依赖的对象,绑定为容器中注册过的IFXNewsListener类型的对象实例。容器在返回FXNewsProvider对象实例以前,会根据这个绑定信息,将IFXNewsListener注册到容器中的对象实例注入到“被注入对象”——FXNewsProvider中,并最终返回已经组装完毕的FXNewsProvider对象。数组

  1. 配置文件方式
<bean id="newsProvider" class="..FXNewsProvider">
<property name="newsListener">
<ref bean="djNewsListener"/>
</property>
<property name="newPersistener">
<ref bean="djNewsPersister"/>
</property>
</bean>

<bean id="djNewsListener"
class="..impl.DowJonesNewsListener">
</bean>

<bean id="djNewsPersister"
class="..impl.DowJonesNewsPersister">
</bean>

3.元数据方式(使用Guice)安全

public class FXNewsProvider { 
	private IFXNewsListener newsListener;
	private IFXNewsPersister newPersistener;
	
	@Inject
	public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister) {
	this.newsListener = listener;
	this.newPersistener = persister;
	} 
	...
}

经过@Inject,咱们指明须要IoC Service Provider经过构造方法注入方式,为FXNewsProvider注入其所依赖的对象。至于余下的依赖相关信息,在Guice中是由相应的Module来提供的,代码清单3-7给出了FXNewsProvider所使用的Module实现。服务器

public class NewsBindingModule extends AbstractModule {
	@Override
	protected void configure() {
	bind(IFXNewsListener.class).to(DowJonesNewsListener.class).in(Scopes.SINGLETON);
	bind(IFXNewsPersister.class).to(DowJonesNewsPersister.class).in(Scopes.SINGLETON);
	} 
}

经过Module指定进一步的依赖注入相关信息以后,咱们就能够直接从Guice那里取得最终已经注入完毕,并直接可用的对象了。

Injector injector = Guice.createInjector(new NewsBindingModule());
FXNewsProvider newsProvider = injector.getInstance(FXNewsProvider.class);
newsProvider.getAndPersistNews();

Spring IoC容器 和 IoC Service Provider之间的关系

Spring的IoC容器是一个IoC Service Provider,可是,这只是它被冠以IoC之名的部分缘由,咱们不能忽略的是“容器”。 Spring的IoC容器是一个提供IoC支持的轻量级容器,除了基本的IoC支持,它做为轻量级容器还提供了IoC以外的支持。如在Spring的IoC容器之上, Spring还提供了相应的AOP框架支持、企业级服务集成等服务。


IoC容器和Provider的 关系

Spring提供了BeanFactory 和 ApplicationContext

BeanFactory

  • 基础类型IoC容器,提供完整的IoC服务支持。若是没有特殊指定,默认采用延迟初始化策略( lazy-load)。只有当客户端对象须要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操做。因此,相对来讲,容器启动初期速度较快,所须要的资源有限。对于资源有限,而且功能要求不是很严格的场景, BeanFactory是比较合适的IoC容器选择。

ApplicationContext

  • ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的全部支持, ApplicationContext还提供了其余高级特性,好比事件发布、国际化信息支持等,这些会在后面详述。 ApplicationContext所管理的对象,在该类型容器启动以后,默认所有初始化并绑定完成。因此,相对于BeanFactory来讲, ApplicationContext要求更多的系统资源,同时,由于在启动时就完成全部初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,而且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。
    关系图

做为Spring提供的基本的IoC容器,BeanFactory能够完成做为IoC Service Provider的全部职责,包括业务对象的注册和对象间依赖关系的绑定。

public interface BeanFactory {

	String FACTORY_BEAN_PREFIX = "&";

	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	boolean containsBean(String name);

	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);

}

BeanFactory的对象注册与依赖绑定方式

// 1-设计FXNewsProvider类用于广泛的新闻处理
public class FXNewsProvider
{
...
}
// 2-设计IFXNewsListener接口抽象各个新闻社不一样的新闻获取方式,并给出相应实现类
public interface IFXNewsListener
{
...
}
// 以及
public class DowJonesNewsListener implements IFXNewsListener {
...
}
// 3-设计IFXNewsPersister接口抽象不一样数据访问方式,并实现相应的实现类 2
public interface IFXNewsPersister {
...
} 
// 以及
public class DowJonesNewsPersister implements IFXNewsPersister { 
...
}
  1. 直接编码方式
    BeanFactoryFX
package org.springframework.mylearntest.beanf;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.mylearntest.before.FXNewsProvider;

/**
 * 经过编码方式使用BeanFactory实现FX新闻相关类的注册及绑定
 * 本内容来自书籍Spring揭密
 * 代码搬运于此
 */
public class BeanFactoryFX {
	public static void main(String[] args) {
		DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
		BeanFactory container = bindViaCode(beanRegistry);
		FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
		newsProvider.getAndPersistNews();
	}

	// 由于传入的DefaultListableBeanFactory同
	// 时实现了BeanFactory和BeanDefinitionRegistry接口,因此,这样作强制类型转换不会出
	// 现问题。但须要注意的是,单纯的BeanDefinitionRegistry是没法强制转换到BeanFactory
	// 类型的!
	public static BeanFactory bindViaCode(BeanDefinitionRegistry registry) {
		AbstractBeanDefinition newsProvider = new RootBeanDefinition(FXNewsProvider.class, 0, true);
		AbstractBeanDefinition newsListener = new RootBeanDefinition(DowJonesNewsListener.class,0, true);
		AbstractBeanDefinition newsPersister = new RootBeanDefinition(DowJonesNewsPersister.class, 0,true);
		// 将bean定义注册到容器中
		registry.registerBeanDefinition("djNewsProvider", newsProvider);
		registry.registerBeanDefinition("djListener", newsListener);
		registry.registerBeanDefinition("djPersister", newsPersister);
		// 指定依赖关系
		// 1. 能够经过构造方法注入方式
		/*ConstructorArgumentValues argValues = new ConstructorArgumentValues();
		argValues.addIndexedArgumentValue(0, newsListener);
		argValues.addIndexedArgumentValue(1, newsPersister);
		newsProvider.setConstructorArgumentValues(argValues);*/
		// 2. 或者经过setter方法注入方式
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.addPropertyValue(new PropertyValue("newsListener",newsListener));
		propertyValues.addPropertyValue(new PropertyValue("newPersistener",newsPersister));
		newsProvider.setPropertyValues(propertyValues);
		// 绑定完成 2
		return (BeanFactory)registry;
	}
}

DowJonesNewsListener

package org.springframework.mylearntest.beanf;

import org.springframework.mylearntest.before.FXNewsBean;
import org.springframework.mylearntest.before.IFXNewsListener;

public class DowJonesNewsListener implements IFXNewsListener {
	@Override
	public String[] getAvailableNewsIds() {
		return new String[0];
	}

	@Override
	public FXNewsBean getNewsByPK(String newsId) {
		return null;
	}

	@Override
	public void postProcessIfNecessary(String newsId) {

	}
}

DowJonesNewsPersister

package org.springframework.mylearntest.beanf;

import org.springframework.mylearntest.before.FXNewsBean;
import org.springframework.mylearntest.before.IFXNewsPersister;

public class DowJonesNewsPersister implements IFXNewsPersister {
	@Override
	public void persistNews(FXNewsBean newsBean) {

	}
}

IFXNewsListener

package org.springframework.mylearntest.before;

public interface IFXNewsListener {
	String[] getAvailableNewsIds();

	FXNewsBean getNewsByPK(String newsId);

	void postProcessIfNecessary(String newsId);
}

IFXNewsPersister

package org.springframework.mylearntest.before;

public interface IFXNewsPersister {
	void persistNews(FXNewsBean newsBean);
}

FXNewsProvider

package org.springframework.mylearntest.before;

import org.apache.commons.lang3.ArrayUtils;


public class FXNewsProvider {
	private IFXNewsListener newsListener;
	private IFXNewsPersister newPersistener;
	public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) {
		this.newsListener = newsListner;
		this.newPersistener = newsPersister;
	}

	public IFXNewsListener getNewsListener() {
		return newsListener;
	}

	public void setNewsListener(IFXNewsListener newsListener) {
		this.newsListener = newsListener;
	}

	public IFXNewsPersister getNewPersistener() {
		return newPersistener;
	}

	public void setNewPersistener(IFXNewsPersister newPersistener) {
		this.newPersistener = newPersistener;
	}

	public FXNewsProvider() {
	}

	public void getAndPersistNews() {
		String[] newsIds = newsListener.getAvailableNewsIds();
		if (ArrayUtils.isEmpty(newsIds)) {
			return;
		}
		for (String newsId : newsIds) {
			FXNewsBean newsBean = newsListener.getNewsByPK(newsId);
			newPersistener.persistNews(newsBean);
			newsListener.postProcessIfNecessary(newsId);
		}
	}
}

FXNewsBean

package org.springframework.mylearntest.before;

public class FXNewsBean {
}
  1. 外部配置文件方式
    一般状况下,须要根据不一样的外部配置文件格式,给出相应的BeanDefinitionReader实现类,由BeanDefinitionReader的相应实现类负责将相应的配置文件内容读取并映射到BeanDefinition,而后将映射后的BeanDefinition注册到一个BeanDefinitionRegistry,以后, BeanDefinitionRegistry即完成Bean的注册和加载。
    大部分工做,包括解析文件格式、装配BeanDefinition之类的工做,都是由BeanDefinitionReader的相应实现类来作的, BeanDefinitionRegistry只不过负责保管而已。
    1. properties
djNewsProvider.(class)=..FXNewsProvider
# ----------经过构造方法注入的时候-------------
djNewsProvider.$0(ref)=djListener
djNewsProvider.$1(ref)=djPersister
# ----------经过setter方法注入的时候---------
# djNewsProvider.newsListener(ref)=djListener
# djNewsProvider.newPersistener(ref)=djPersister
djListener.(class)=..impl.DowJonesNewsListener
djPersister.(class)=..impl.DowJonesNewsPersister
package org.springframework.mylearntest.directcode;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;

public class PropConfigTest {
	public static void main(String[] args) {
		// todo Caused by: java.lang.IllegalStateException: No bean class specified on bean definition
		DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
		BeanFactory container = bindViaPropertiesFile(beanRegistry);
		FXNewsProvider newsProvider =
				(FXNewsProvider)container.getBean("djNewsProvider");
		newsProvider.getAndPersistNews();
	}

	public static BeanFactory bindViaPropertiesFile(BeanDefinitionRegistry registry) {
		PropertiesBeanDefinitionReader reader =
				new PropertiesBeanDefinitionReader(registry);
		reader.loadBeanDefinitions("classpath:binding-config.properties");
		return (BeanFactory)registry;
	}
}
  1. xml

local parent 和 bean的区别

local、 parent和bean的区别在于:

  • local只能指定与当前配置的对象在同一个配置文件的对象定义的名称(能够得到XML解析器的id约束验证支持);

  • parent则只能指定位于当前容器的父容器中定义的对象引用;

  • bean则基本上通吃,因此,一般状况下,直接使用bean来指定对象引用就能够了

  • 但这种场合下,使用idref才是最为合适的。由于使用idref,容器在解析配置的时候就能够帮 你检查这个beanName究竟是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在。毕竟,输错名字的问题很常见。如下代码演示了idref的使用:

<property name="newsListenerBeanName">
<idref bean="djNewsListener"/>
</property>

byType byName No Autowired Autodetect

autodetect

  • 这种模式是byType和constructor模式的结合体,若是对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式。不然,会使用constructor模式。固然,若是经过构造方法注入绑定后还有其余属性没有绑定,容器也会使用byType对剩余的对象属性进行自动绑定。

自动绑定和手动绑定的区别

自动绑定和手动明确绑定各有利弊。自动绑定的优势有以下两点。

  • (1) 某种程度上能够有效减小手动敲入配置信息的工做量。
  • (2) 某些状况下,即便为当前对象增长了新的依赖关系,但只要容器中存在相应的依赖对象,就不须要更改任何配置信息。
    自动绑定的缺点有以下几点。
  • (1) 自动绑定不如明确依赖关系一目了然。咱们能够根据明确的依赖关系对整个系统有一个明确的认识,但使用自动绑定的话,就可能须要在类定义以及配置文件之间,甚至各个配置文件之间来回转换以取得相应的信息。
  • (2) 某些状况下,自动绑定没法知足系统须要,甚至致使系统行为异常或者不可预知。根据类型( byType)匹配进行的自动绑定,若是系统中增长了另外一个相同类型的bean定义,那么整个系统就会崩溃;根据名字( byName)匹配进行的自动绑定,若是把原来系统中相同名称的bean定义类型给换掉,就会形成问题,而这些可能都是在不经意间发生的。
  • (3) 使用自动绑定,咱们可能没法得到某些工具的良好支持,好比Spring IDE。与BeanFactory不一样, ApplicationContext在容器启动的时候,就会立刻对全部的“ singleton的bean定义” 进行实例化操做

懒加载配置了是否必定会生效?

仅指定lazy-init-bean的lazy-init为true,并不意味着容器就必定会延迟初始化该bean的实例。若是某个非延迟初始化的bean定义依赖于lazy-init-bean,那么毫无疑问,按照依赖决计的顺序,容器仍是会首先实例化lazy-init-bean,而后再实例化后者,以下代码演示了这种相互牵连致使延迟初始化失败的状况:

<bean id="lazy-init-bean" class="..." lazy-init="true"/> 
<bean id="not-lazy-init-bean" class="...">
<property name="propName">
<ref bean="lazy-init-bean"/> 
</property>
</bean>

如何简化xml配置中property属性的编写

代码清单4-27 使用模板化配置形式配置FXNewsProvider和SpecificFXNewsProvider

<bean id="newsProviderTemplate" abstract="true">
<property name="newPersistener">
<ref bean="djNewsPersister"/> 
</property>
</bean>
<bean id="superNewsProvider" parent="newsProviderTemplate" 
class="..FXNewsProvider">
<property name="newsListener">
</property> <ref bean="djNewsListener"/> 7
</bean>
<bean id="subNewsProvider" parent="newsProviderTemplate" 
class="..SpecificFXNewsProvider">
<property name="newsListener">
<ref bean="specificNewsListener"/>
</property> 
</bean>

abstract属性的使用

若是你不想容器在初始化的时候实例化某些对象,那么能够将其abstract属性赋值true,以免容器将其实例化。对于ApplicationContext容器尤为如此,由于默认状况下, ApplicationContext会在容器启动的时候就对其管理的全部bean进行实例化,只有标志为abstract的bean除外。

scope 定义以及 scope 的几种类型

scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope以前,生成并装配这些对象,在该对象再也不处于这些scope的限定以后,容器一般会销毁这些对象。

Spring容器最初提供了两种bean的scope类型: singleton和prototype,但发布2.0以后,又引入了另外三种scope类型,即request、 session和global session类型。不过这三种类型有所限制,只能在Web应用中使用。也就是说,只有在支持Web应用的ApplicationContext中使用这三个scope才是合理的。

global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的 session。若是在普通的基于servlet的Web应用中使用了这个类scope,容器会将其做为普通的session类型的scope对待。

方法注入

Spring容器提出了一种叫作方法注入( Method Injection)的方式,能够帮助咱们解决上述问题。咱们所要作的很简单,只要让getNewsBean方法声明符合规定的格式,并在配置文件中通知容器,当该方法被调用的时候,每次返回指定类型的对象实例便可。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="newsBean" class="org.springframework.mylearntest.directcode.FXNewsBean" scope="prototype">
	</bean>
	<bean id="mockPersister" class="org.springframework.mylearntest.mthdinject.MockNewsPersister">
		<property name="newsBean">
			<ref bean="newsBean"/>
		</property>
	</bean>
</beans>
package org.springframework.mylearntest.mthdinject;

import org.springframework.mylearntest.directcode.FXNewsBean;
import org.springframework.mylearntest.directcode.IFXNewsPersister;

public class MockNewsPersister implements IFXNewsPersister {
	private FXNewsBean newsBean;
	public void persistNews(FXNewsBean bean) {
		persistNews();
	}
	public void persistNews() {
		System.out.println("persist bean:"+getNewsBean());
	}
	public FXNewsBean getNewsBean() {
		return newsBean;
	}

	public void setNewsBean(FXNewsBean newsBean) {
		this.newsBean = newsBean;
	}
}
package org.springframework.mylearntest.mthdinject;


import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4MockNewsPersister {
	public static void main(String[] args) {
		BeanFactory container = new ClassPathXmlApplicationContext("mthdinject.xml");
		MockNewsPersister persister = (MockNewsPersister)container.getBean("mockPersister");
		persister.persistNews();
		persister.persistNews();
	}
}

输出结果为

persist bean:org.springframework.mylearntest.directcode.FXNewsBean@5be6e01c
persist bean:org.springframework.mylearntest.directcode.FXNewsBean@5be6e01c

使用方法注入后

<bean id="newsBean" class="..domain.FXNewsBean" singleton="prototype">
</bean>
<bean id="mockPersister" class="..impl.MockNewsPersister">
<lookup-method name="getNewsBean" bean="newsBean"/>
</bean>

经过 的name属性指定须要注入的方法名, bean属性指定须要注入的对象,当getNewsBean方法被调用的时候,容器能够每次返回一个新的FXNewsBean类型的实例。

经过实现BeanFactoryAware

即便没有方法注入, 只要在实现getNewsBean()方法的时候,可以保证每次调用BeanFactory的getBean("newsBean"),就一样能够每次都取得新的FXNewsBean对象实例

package org.springframework.mylearntest.beanfactorywareinject;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.mylearntest.directcode.FXNewsBean;
import org.springframework.mylearntest.directcode.IFXNewsPersister;

public class MockNewsPersister1 implements IFXNewsPersister, BeanFactoryAware {
	private BeanFactory beanFactory;

	public void setBeanFactory(BeanFactory bf) throws BeansException {
		this.beanFactory = bf;
	}

	public void persistNews(FXNewsBean bean) {
		persistNews();
	}

	public void persistNews() {
		System.out.println("persist bean:" + getNewsBean());
	}

	public FXNewsBean getNewsBean() {
		return (FXNewsBean) beanFactory.getBean("newsBean");
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="newsBean" class="org.springframework.mylearntest.directcode.FXNewsBean" scope="prototype">
	</bean>
	<bean id="mockPersister1" class="org.springframework.mylearntest.beanfactorywareinject.MockNewsPersister1">
	</bean>

</beans>
package org.springframework.mylearntest.beanfactorywareinject;


import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mylearntest.mthdinject.MockNewsPersister;

public class Test4MockNewsPersister1 {
	public static void main(String[] args) {
		BeanFactory container = new ClassPathXmlApplicationContext("beanfactoryawareinject.xml");
		MockNewsPersister1 persister = (MockNewsPersister1)container.getBean("mockPersister1");
		persister.persistNews();
		persister.persistNews();
	}
}

经过ObjectFactory

ObjectFactoryCreatingFactoryBean是 Spring 提 供 的 一 个 FactoryBean实 现 ,它 返 回 一 个ObjectFactory实例。ObjectFactoryCreatingFactoryBean返回的这个ObjectFactory实例能够为咱们返回容器管理的相关对象。实际上,ObjectFactoryCreatingFactoryBean实现BeanFactoryAware接口,它返回ObjectFactory实例只是特定于与Spring容器进行交互的一个实现而已。使用它的好处就是,隔离了客户端对象对BeanFactory的直接引用。

package org.springframework.mylearntest.objectfactoryinj;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.mylearntest.directcode.FXNewsBean;
import org.springframework.mylearntest.directcode.IFXNewsPersister;

@SuppressWarnings({"rawtypes" })
public class MockNewsPersister2 implements IFXNewsPersister {
	private ObjectFactory newsBeanFactory;
	public void persistNews(FXNewsBean bean) {
		persistNews();
	}
	public void persistNews() {
		System.out.println("persist bean:"+getNewsBean());
	}
	public FXNewsBean getNewsBean() {
		return (FXNewsBean) newsBeanFactory.getObject();
	}
	public void setNewsBeanFactory(ObjectFactory newsBeanFactory) {
		this.newsBeanFactory = newsBeanFactory;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="newsBean" class="org.springframework.mylearntest.directcode.FXNewsBean" scope="prototype">
	</bean>
	<bean id="newsBeanFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
		<property name="targetBeanName">
			<idref bean="newsBean"/>
		</property>
	</bean>

	<bean id="mockPersister2" class="org.springframework.mylearntest.objectfactoryinj.MockNewsPersister2">
		<property name="newsBeanFactory">
			<ref bean="newsBeanFactory"/>
		</property>
	</bean>
</beans>
package org.springframework.mylearntest.objectfactoryinj;


import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4MockNewsPersister2 {
	public static void main(String[] args) {
		BeanFactory container = new ClassPathXmlApplicationContext("objectfactoryinj.xml");
		MockNewsPersister2 persister = (MockNewsPersister2)container.getBean("mockPersister2");
		persister.persistNews();
		persister.persistNews();
	}
}

方法替换

使用FXNewsProviderMethodReplacer替换FXNewsProvider中的getAndPersistNews()方法

package org.springframework.mylearntest.methodreplacer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.support.MethodReplacer;

import java.lang.reflect.Method;

public class FXNewsProviderMethodReplacer implements MethodReplacer {

	private static final transient Log logger =
			LogFactory.getLog(FXNewsProviderMethodReplacer.class);

	public Object reimplement(Object target, Method method, Object[] args)
			throws Throwable {
		logger.info("before executing method["+method.getName()+
				"] on Object["+target.getClass().getName()+"].");
		System.out.println("sorry,We will do nothing this time.");
		logger.info("end of executing method["+method.getName()+
				"] on Object["+target.getClass().getName()+"].");
		return null;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="FXNewsProvider" class="org.springframework.mylearntest.propconfig.FXNewsProvider">
		<constructor-arg index="0">
			<ref bean="djNewsListener"/>
		</constructor-arg>
		<constructor-arg index="1">
			<ref bean="djNewsPersister"/>
		</constructor-arg>
		<replaced-method name="getAndPersistNews" replacer="providerReplacer">
		</replaced-method>
	</bean>

	<bean id="djNewsListener" class="org.springframework.mylearntest.propconfig.DjNewsListener"/>
	<bean id="djNewsPersister" class="org.springframework.mylearntest.propconfig.DjNewsPersister"/>
	<bean id="providerReplacer" class="org.springframework.mylearntest.methodreplacer.FXNewsProviderMethodReplacer">
	</bean>
</beans>
package org.springframework.mylearntest.methodreplacer;


import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mylearntest.propconfig.FXNewsProvider;

public class Test4FXNewsProviderMethodReplacer {
	public static void main(String[] args) {
		BeanFactory container = new ClassPathXmlApplicationContext("methodreplacer.xml");
		FXNewsProvider fxNewsProvider = (FXNewsProvider)container.getBean("FXNewsProvider");
		fxNewsProvider.getAndPersistNews();
	}
}

IoC容器背后的秘密

  1. 容器启动阶段
    容器启动开始,首先会经过某种途径加载Configuration MetaData。除了代码方式比较直接,在大部分状况下,容器须要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工做就完成了。

  2. Bean实例化阶段
    通过第一阶段,如今全部的bean定义信息都经过BeanDefinition的方式注册到了BeanDefinitionRegistry中。当某个请求方经过容器的getBean方法明确地请求某个对象时,或者因依赖关系容器须要隐式地调用getBean方法时,就会触发第二阶段的活动。

该阶段,容器会首先检查所请求的对象以前是否已经初始化。若是没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。若是该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕以后,容器会当即将其返回请求方使用。若是说第一阶段只是根据图纸装配生产线的话,那么第二阶段就是使用装配好的生产线来生产具体的产品了。

BeanFactoryPostProcessor

Spring提供了一种叫作BeanFactoryPostProcessor的容器扩展机制。该机制容许咱们在容器实例化相应对象以前,对注册到容器的BeanDefinition所保存的信息作相应的修改。这就至关于在容器实现的第一阶段最后加入一道工序,让咱们对最终BeanDefinition作一些额外的操做,好比修改其中bean定义的某些属性,为bean定义增长其余信息等。

若是要自定义实现BeanFactoryPostProcessor,一般咱们须要实现org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。这个时候可能须要实现类同时实现Spring的org.springframework.core.Ordered接口,以保证各个BeanFactoryPostProcessor能够按照预先设定的顺序执行(若是顺序紧要的话)。

其中,org.springframework.beans.factory.config.PropertyPlaceholderConfigurer和org.springframework.beans.factory.config.Property OverrideConfigurer是两个比较经常使用的BeanFactoryPostProcessor。

  1. PropertyPlaceholderConfigurer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!--	使用的BeanFactoryPostProcessor-->
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>conf/jdbc.properties</value>
				<value>conf/mail.properties</value>
			</list>
		</property>
	</bean>

	<!--	使用占位符的数据源配置-->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="url">
			<value>${jdbc.url}</value>
		</property>
		<property name="driverClassName">
			<value>${jdbc.driver}</value>
		</property>
		<property name="username">
			<value>${jdbc.username}</value>
		</property>
		<property name="password">
			<value>${jdbc.password}</value>
		</property>
		<property name="testOnBorrow">
			<value>true</value>
		</property>
		<property name="testOnReturn">
			<value>true</value>
		</property>
		<property name="testWhileIdle">
			<value>true</value>
		</property>
		<property name="minEvictableIdleTimeMillis">
			<value>180000</value>
		</property>
		<property name="timeBetweenEvictionRunsMillis">
			<value>360000</value>
		</property>
		<property name="validationQuery">
			<value>SELECT 1</value>
		</property>
		<property name="maxOpenPreparedStatements">
			<value>100</value>
		</property>
	</bean>
</beans>
jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932&failOverReadOnly=false&roundRobinLoadBalance=true
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root

若是org.apache.commons.dbcp2.BasicDataSource报错,请加入依赖
compile(group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.1.1')

基本机制就是以前所说的那样。当BeanFactory在第一阶段加载完成全部配置信息时, BeanFactory中保存的对象的属性信息还只是以占位符的形式存在,如${jdbc.url}、 ${jdbc.driver}。当PropertyPlaceholderConfigurer做为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换相应BeanDefinition中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化bean时, bean定义中的属性值就是最终替换完成的了。

PropertyPlaceholderConfigurer不单会从其配置的properties文件中加载配置项,同时还会检查Java的System类中的Properties,能够经过setSystemPropertiesMode()或者setSystemPropertiesModeName()来控制是否加载或者覆盖System相应Properties的行为。
PropertyPlaceholderConfigurer提供了SYSTEM_PROPERTIES_MODE_FALLBACK、 SYSTEM_PROPERTIES_MODE_NEVER和SYSTEM_PROPERTIES_MODE_OVERRIDE三种模式。默认采用的是SYSTEM_PROPERTIES_ MODE_FALLBACK,果properties文件中找不到相应配置项,则到System的Properties中查找,咱们还能够选择不检查System的Properties或者覆盖它。

  1. PropertyOverrideConfigurer
    配置在properties文件中的信息一般都以明文表示,PropertyOverrideConfigurer的父类PropertyResourceConfigurer提供了一个protected类型的方法convertPropertyValue,容许子类覆盖这个方法对相应的置项进行转换,如对加密后的字符串解密以后再覆盖到相应的bean定义中。固然,既然PropertyPlaceholderConfigurer也一样继承了PropertyResourceConfigurer,咱们也能够针对PropertyPlaceholderConfigurer应用相似的功能。
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="location" value="pool-adjustment.properties"/>
</bean>
dataSource.minEvictableIdleTimeMillis=1000
dataSource.maxOpenPreparedStatements=50
  1. CustomEditorConfigurer
    CustomEditorConfigurer是另外一种类型的BeanFactoryPostProcessor实现,它只是辅助性地将后期会用到的信息注册到容器,对BeanDefinition没有作任何变更。

Spring提供的部分PropertyEditor:
StringArrayPropertyEditor。该PropertyEditor会将符合CSV格式的字符串转换成String[]数组的形式,默认是以逗号(,)分隔的字符串,但能够指定自定义的字符串分隔符。ByteArrayPropertyEditor、CharArrayPropertyEditor等都属于相似功能的PropertyEditor,参照Javadoc能够取得相应的详细信息。

  • ClassEditor。根据String类型的class名称,直接将其转换成相应的Class对象,至关于经过Class.forName(String)完成的功效。能够经过String[]数组的形式传入需转换的值,以达到与提供ClassArrayEditor一样的目的。

  • FileEditor。 Spring提供的对应java.io.File类型的PropertyEditor。同属于对资源进行定位的PropertyEditor还有InputStreamEditor、 URLEditor等。

  • LocaleEditor。针对java.util.Locale类型的PropertyEditor,格式能够参照LocaleEditor和Locale的Javadoc说明。

  • PatternEditor。针对Java SE 1.4以后才引入的java.util.regex.Pattern的PropertyEditor,格式能够参照java.util.regex.Pattern类的Javadoc。

以上这些PropertyEditor,容器一般会默认加载使用,因此,即便咱们不告诉容器应该如何对这些类型进行转换,容器一样能够正确地完成工做。但当咱们须要指定的类型没有包含在以上所提到PropertyEditor之列的时候,就须要给出针对这种类型的PropertyEditor实现,并经过CustomEditorConfigurer告知容器,以便容器在适当的时机使用到适当的PropertyEditor。

自定义PropertyEditor
对于Date类型,不一样的Locale、不一样的系统在表现形式上存在不一样的需求。如系统这个部分须要以yyyy-MM-dd的形式表现日期,系统那个部分可能又须要以yyyyMMdd的形式对日期进行转换。

package org.springframework.mylearntest.beanfactorypostprocessor;

import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class DatePropertyEditor extends PropertyEditorSupport {
	private String datePattern;

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(getDatePattern());
		LocalDate dateValue = LocalDate.parse(text,dateTimeFormatter);
		setValue(dateValue);
	}

	@Override
	public String getAsText() {
		return super.getAsText();
	}

	public String getDatePattern() {
		return datePattern;
	}

	public void setDatePattern(String datePattern) {
		this.datePattern = datePattern;
	}
}

若是仅仅是支持单向的从String到相应对象类型的转换,只要覆写方法setAsText(String)便可。若是想支持双向转换,须要同时考虑getAsText()方法的覆写。

package org.springframework.mylearntest.beanfactorypostprocessor;

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

import java.beans.PropertyEditor;

public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
	private PropertyEditor propertyEditor;

	public PropertyEditor getPropertyEditor() {
		return propertyEditor;
	}

	public void setPropertyEditor(PropertyEditor propertyEditor) {
		this.propertyEditor = propertyEditor;
	}

	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		registry.registerCustomEditor(java.util.Date.class,getPropertyEditor());
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
	   xmlns:aop="http://www.springframework.org/schema/aop">

	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="propertyEditorRegistrars">
			<list>
				<ref bean="datePropertyEditorRegistrar"/>
			</list>
		</property>
	</bean>

	<bean id="datePropertyEditorRegistrar" class="org.springframework.mylearntest.beanfactorypostprocessor.DatePropertyEditorRegistrar">
		<property name="propertyEditor">
			<ref bean="datePropertyEditor"/>
		</property>
	</bean>

	<bean id="datePropertyEditor" class="org.springframework.mylearntest.beanfactorypostprocessor.DatePropertyEditor">
		<property name="datePattern">
			<value>yyyy/MM/dd</value>
		</property>
	</bean>
</beans>
package org.springframework.mylearntest.beanfactorypostprocessor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4DateProp {
	public static void main(String[] args) {
		// applicationContext
		ApplicationContext context = new ClassPathXmlApplicationContext("datepropertyeditor2.xml");
		DatePropertyEditor datePropertyEditor = (DatePropertyEditor) context.getBean("datePropertyEditor");
		datePropertyEditor.setAsText("2020/06/21");
	}
}

bean的构造

容器启动以后,并不会立刻就实例化相应的bean定义。咱们知道,容器如今仅仅拥有全部对象的BeanDefinition来保存实例化阶段将要用的必要信息。只有当请求方经过BeanFactory的getBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动。 BeanFactory的getBe法能够被客户端对象显式调用,也能够在容器内部隐式地被调用。隐式调用有以下两种状况。

  1. 对于BeanFactory来讲,对象实例化默认采用延迟初始化。一般状况下,当对象A被请求而须要第一次实例化的时候,若是它所依赖的对象B以前一样没有被实例化,那么容器会先实例化对象A所依赖的对象。这时容器内部就会首先实例化对象B,以及对象A依赖的其余尚未实例化的对象。这种状况是容器内部调用getBean(),对于本次请求的请求方是隐式的。
  2. ApplicationContext启动以后会实例化全部的bean定义,这个特性在本书中已经屡次提到。但ApplicationContext在实现的过程当中依然遵循Spring容器实现流程的两个阶段,只不过它会在启动阶段的活动完成以后,紧接着调用注册到该容器的全部bean定义的实例化方法getBean()。这就是为何当你获得ApplicationContext类型的容器引用时,容器内全部对象已经被所有实例化完成。不信你查一下类org.AbstractApplicationContext的refresh()方法。

bean实例化过程

  1. Bean的实例化与BeanWrapper
    第一步:获取BeanWrapper
    • 容器在内部实现的时候,采用“策略模式(Strategy Pattern)”来决定采用何种方式初始化bean实例。一般,能够经过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。org.springframework.beans.factory.support.InstantiationStrategy定义是实例化策略的抽象接口,其直接子类SimpleInstantiationStrategy实现了简单的对象实例化功能,能够经过反射来实例化对象实例,但不支持方法注入方式的对象实例化。 CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy的以反射方式实例化对象的功能,而且经过CGLIB的动态字节码生成功能,该策略实现类能够动态生成某个类的子类,进而知足了方法注入所需的对象实例化求。默认状况下,容器内部采用的是CglibSubclassingInstantiationStrategy。

    • 容器只要根据相应bean定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不一样的bean定义类型,就能够返回实例化完成的对象实例。可是,返回方式上有些“点缀”。不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。

第二部:设置Bean的相应属性

  • BeanWrapper接口一般在Spring框架内部使用,它有一个实现类org.springframework.beans.BeanWrapperImpl。其做用是对某个bean进行“包裹”,而后对这个“包裹”的bean进行操做,好比设置或者获取bean的相应属性值。而在第一步结束后返回BeanWrapper实例而不是原先的对象实例,就是为了第二步“设置对象属性”。

  • BeanWrapper定义继承了org.springframework.beans.PropertyAccessor接口,能够以统一的方式对对象属性进行访问; BeanWrapper定义同时又直接或者间接继承了PropertyEditorRegistry和TypeConverter接口。不知你是否还记得CustomEditorConfigurer?当把各类PropertyEditor注册给容器时,知道后面谁用到这些PropertyEditor吗?对,就是BeanWrapper!在第一步构造完成对象以后, Spring会根据对象实例构造一个BeanWrapperImpl实例,而后将以前CustomEditorConfigurer注册的PropertyEditor复制一份给BeanWrapperImpl例(这就是BeanWrapper同时又是PropertyEditorRegistry的缘由)。这样,当BeanWrapper转换类型、设置对象属性值时,就不会无从下手了。

// 使用BeanWrapper操做对象
Object provider = Class.forName("package.name.FXNewsProvider").newInstance(); 
Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance();
Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance();
BeanWrapper newsProvider = new BeanWrapperImpl(provider); 
newsProvider.setPropertyValue("newsListener", listener);
newsProvider.setPropertyValue("newPersistener", persister);

assertTrue(newsProvider.getWrappedInstance() instanceof FXNewsProvider);
assertSame(provider, newsProvider.getWrappedInstance());
assertSame(listener, newsProvider.getPropertyValue("newsListener"));
assertSame(persister, newsProvider.getPropertyValue("newPersistener"));
// 使用Java反射API操做对象
Object provider = Class.forName("package.name.FXNewsProvider").newInstance();
Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance();
Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance();

Class providerClazz = provider.getClass();
Field listenerField = providerClazz.getField("newsListener");
listenerField.set(provider, listener);
Field persisterField = providerClazz.getField("newsListener");
persisterField.set(provider, persister);
assertSame(listener, listenerField.get(provider));
assertSame(persister, persisterField.get(provider));
  1. 各类Aware接口
    当对象实例化完成而且相关属性以及依赖设置完成以后, Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。若是是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。这些Aware接口为以下几个。
    针对BeanFactory容器而言
    • org.springframework.beans.factory.BeanNameAware。若是Spring容器检测到当前对象实例实现了该接口,会将该对象实例的bean定义对应的beanName设置到当前对象实例。

    • org.springframework.beans.factory.BeanClassLoaderAware。若是容器检测到当前对象实例实现了该接口,会将对应加载当前bean的Classloader注入当前对象实例。默认会使用加载org.springframework.util.ClassUtils类的Classloader。

    • org.springframework.beans.factory.BeanFactoryAware。在介绍方法注入的时候,咱们提到过使用该接口以便每次获取prototype类型bean的不一样实例。若是对象声明实现了BeanFactoryAware接口, BeanFactory容器会将自身设置到当前对象实例。这样,当前对象实例就拥有了一个BeanFactory容器的引用,而且能够对这个容器内容许访问的对象按照须要进行访问。

对于ApplicationContext类型容器,使用BeanPostProcessor处理

  • org.springframework.context.ResourceLoaderAware 。 ApplicationContext 实现了Spring的ResourceLoader接口(后面会说起详细信息)。当容器检测到当前对象实例实现了ResourceLoaderAware接口以后,会将当前ApplicationContext自身设置到对象实例,这样当前对象实例就拥有了其所在ApplicationContext容器的一个引用。

  • org.springframework.context.ApplicationEventPublisherAware。 ApplicationContext做为一个容器,同时还实现了ApplicationEventPublisher接口,这样,它就能够做为ApplicationEventPublisher来使用。因此,当前ApplicationContext容器若是检测到当前实例化的对象实例声明了ApplicationEventPublisherAware接口,则会将自身注入当前对象。

  • org.springframework.context.MessageSourceAware。 ApplicationContext经过MessageSource接口提供国际化的信息支持,即I18n( Internationalization)。它自身就实现了MessageSource接口,因此当检测到当前对象实例实现了MessageSourceAware接口,则会将自身注入当前对象实例。

  • org.springframework.context.ApplicationContextAware。 若是ApplicationContext容器检测到当前对象实现了ApplicationContextAware接口,则会将自身注入当前对象实例。

BeanPostProcessor

只要记住BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段。

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

自定义BeanPostProcessor须要实现BeanPostProcessor

假设系统中全部的IFXNewsListener实现类须要从某个位置取得相应的服务器链接密码,并且系统中保存的密码是加密的,那么在IFXNewsListener发送这个密码给新闻服务器进行链接验证的时候,首先须要对系统中取得的密码进行解密,而后才能发送。

  • (1) 标注须要进行解密的实现类
    为了可以识别那些须要对服务器链接密码进行解密的IFXNewsListener实现,咱们声明了接口PasswordDecodable,并要求相关IFXNewsListener实现类实现该接口。
package org.springframework.mylearntest.beanpostprocessor;


public interface PasswordDecodable {
	String getEncodedPassword();
	void setDecodedPassword(String password);
}
package org.springframework.mylearntest.beanpostprocessor;

import org.springframework.mylearntest.directcode.FXNewsBean;
import org.springframework.mylearntest.directcode.IFXNewsListener;

public class DowJonesNewsListener implements IFXNewsListener,PasswordDecodable {
	private String password;
	public String[] getAvailableNewsIds() {
		// 省略
		return new String[0];
	}
	public FXNewsBean getNewsByPK(String newsId) {
		// 省略
		return null;
	}
	public void postProcessIfNecessary(String newsId) {
		// 省略
	}
	public String getEncodedPassword() {
		return this.password;
	}
	public void setDecodedPassword(String password) {
		this.password = password;
	}
}
  • (2) 实现相应的BeanPostProcessor对符合条件的Bean实例进行处理
    咱们经过PasswordDecodable接口声明来区分将要处理的对象实例,当检查到当前对象实例实现了该接口以后,就会从当前对象实例取得加密后的密码,并对其解密。而后将解密后的密码设置回当前对象实例。
package org.springframework.mylearntest.beanpostprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class PasswordDecodePostProcessor implements BeanPostProcessor {
	public Object postProcessAfterInitialization(Object object, String beanName)
			throws BeansException {
		return object;
	}
	public Object postProcessBeforeInitialization(Object object, String beanName)
			throws BeansException {
		if(object instanceof PasswordDecodable){
			String encodedPassword = ((PasswordDecodable)object).getEncodedPassword();
			String decodedPassword = decodePassword(encodedPassword);
			((PasswordDecodable)object).setDecodedPassword(decodedPassword);
		}
		return object;
	}
	private String decodePassword(String encodedPassword) {
		// 实现解码逻辑
		encodedPassword = encodedPassword + "2mingwen";
		return encodedPassword;
	}
}
  • (3) 将自定义的BeanPostProcessor注册到容器
    将PasswordDecodePostProcessor注入到容器中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
	   xmlns:aop="http://www.springframework.org/schema/aop">
	<bean id="dowJonesNewsListener" class="org.springframework.mylearntest.beanpostprocessor.DowJonesNewsListener">
		<property name="decodedPassword" value="123sjfg@LL"></property>
	</bean>

	<bean id="passwordDecodePostProcessor" class="org.springframework.mylearntest.beanpostprocessor.PasswordDecodePostProcessor">
	</bean>
</beans>
// 测试类
package org.springframework.mylearntest.beanpostprocessor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4BeanPostProcessor {
	public static void main(String[] args) {
		ApplicationContext beanFactory = new ClassPathXmlApplicationContext("beanpostprocessor/beanpostprocessor.xml");
		DowJonesNewsListener dowJonesNewsListener = (DowJonesNewsListener) beanFactory.getBean("dowJonesNewsListener");
		String encodedPassword = dowJonesNewsListener.getEncodedPassword();
		System.out.println("encodedPassword = " + encodedPassword);// encodedPassword = 123sjfg@LL2mingwen
	}
}

实际上,有一种特殊类型的BeanPostProcessor咱们没有提到,它的执行时机与一般的BeanPostProcessor不一样。org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口能够在对象的实例化过程当中致使某种相似于电路“短路”的效果。实际上,并不是全部注册到Spring容器内的bean定义都是按照图4-10的流程实例化的。在全部的步骤以前,也就是实例化bean对象步骤以前,容器会首先检查容器中是否注册有InstantiationAwareBeanPostProcessor类型的BeanPostProcessor。若是有,首先使用相应的InstantiationAwareBeanPostProcessor来构造对象实例。构形成功后直接返回造完成的对象实例,而不会按照“正规的流程”继续执行。这就是它可能形成“短路”的缘由。

InitializingBean 和 init-method

org.springframework.beans.factory.InitializingBean是容器内部普遍使用的一个对象生命周期标识接口。

public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}

其做用在于,在对象实例化过程调用过“BeanPostProcessor的前置处理”以后,会接着检测当前对象是否实现了InitializingBean接口,若是是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。好比,在有些状况下,某个业务对象实例化完成后,还不能处于可使用状态。这个时候就可让该业务对象实现该接口,并在方法afterPropertiesSet()中完成对该业务对象的后续处理。

若是系统开发过程当中规定:全部业务对象的自定义初始化操做都必须以init()命名,为了省去挨个 的设置init-method这样的烦琐,咱们还能够经过最顶层的 的default-init-method统一指定这一init()方法名。

统一资源加载策略

Spring提出了一套基于org.springframework.core.io.Resource和org.springframework.core.io.ResourceLoader接口的资源抽象和加载策略。

Resource:
Resource接口能够根据资源的不一样类型,或者资源所处的不一样场合,给出相应的具体实现。能够帮助咱们查询资源状态、访问资源内容,甚至根据当前资源建立新的相对资源。咱们能够继承org.springframework.core.io.AbstractResource抽象类。

ResourceLoader:
但如何去查找和定位这些资源,则应该是ResourceLoader的职责所在了。 org.springframework.core.io.ResourceLoader接口是资源查找定位策略的统一抽象,具体的资源查找定位策略则由相应的ResourceLoader实现类给出。

DefaultResourceLoader
ResourceLoader有一个默认的实现类,即org.springframework.core.io.DefaultResourceLoader,该类默认的资源查找处理逻辑以下。

  • (1) 首先检查资源路径是否以classpath:前缀打头,若是是,则尝试构造ClassPathResource类型资源并返回。
  • (2) 不然, (a) 尝试经过URL,根据资源路径来定位资源,若是没有抛出MalformedURLException,有则会构造UrlResource类型的资源并返回; (b)若是仍是没法根据资源路径定位指定的资源,则委派getResourceByPath(String) 方 法 来 定 位 , DefaultResourceLoader 的getResourceByPath(String)方法默认实现逻辑是,构造ClassPathResource类型的资源并返回。

Resource和ResourceLoader类层次图

AbstractApplicationContext做为ResourceLoader和ResourcePatternResolver

四种加载方式:

  1. 使用以ResourceLoader身份登场的ApplicationContext
    ResourceLoader resourceLoader = new ClassPathXmlApplicationContext("配置文件路径");

  2. ResourceLoader类型的注入

    • 2.1.0 依赖于ResourceLoader
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="resourceLoader" class="org.springframework.core.io.DefaultResourceLoader">
	</bean>

	<bean id="fooBar" class="org.springframework.mylearntest.resourceloader.FooBar">
		<property name="resourceLoader">
			<ref bean="resourceLoader"/>
		</property>
	</bean>
</beans>
  • 2.2.0 实现了ResourceLoaderAware或者ApplicationContextAware接口的实例类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="fooBar" class="org.springframework.mylearntest.resourceloader.FooBarImplApplicationContextAware">
	</bean>
</beans>
  1. Resource类型的注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="xMailer" class="org.springframework.mylearntest.resourceloader.XMailer">
		<property name="template" value="resourceloader/resources.default_template.vm"/>
	</bean>
</beans>
  1. 在特定状况下, ApplicationContext的Resource加载行为
    当ClassPathXmlApplicationContext在实例化的时候,即便没有指明classpath:或者classpath*:等前缀,它会默认从classpath中加载bean定义配置文件,
    而FileSystemXmlApplicationContext则有些 不一样 ,若是 咱们 像以下 代码 那样指 定conf/appContext.xml,它会尝试从文件系统中加载bean定义文件

Java SE 提供的国际化支持

  1. Locale
    不一样的Locale表明不一样的国家和地区,每一个国家和地区在Locale这里都有相应的简写代码表示,包括语言代码以及国家代码,这些代码是ISO标准代码。如,Locale.CHINA表明中国。

  2. ResourceBundle
    ResourceBundle用来保存特定于某个Locale的信息(能够是String类型信息,也能够是任何类型的对象)。一般, ResourceBundle管理一组信息序列,全部的信息序列有统一的一个basename,而后特定的Locale的信息,能够根据basename后追加的语言或者地区代码来区分。好比,咱们用一组properties文件来分别保存不一样国家地区的信息,能够像下面这样来命名相应的properties文件:

messages.properties
messages_zh.properties
messages_zh_CN.properties
messages_en.properties
messages_en_US.properties
...

其中,文件名中的messages部分称做ResourceBundle将加载的资源的basename,其余语言或地区的资源在basename的基础上追加Locale特定代码。

MessageSource类层次结构图

若是某个业务对象须要国际化的信息支持,那么最简单的办法就是让它实现MessageSourceAware接口,而后注册到ApplicationContext容器。不过这样一来,该业务对象对ApplicationContext容器的依赖性就太强了,显得容器具备较强的侵入性。而实际上, 若是真的某个业务对象须要依赖于MessageSource的话,直接经过构造方法注入或者setter方法注入的方式声明依赖就能够了。

容器内部事件发布

  1. 自定义事件发布
    给出自定义事件类型( define your own event object)。 为了针对具体场景能够区分具体的事件类型, 咱们须要给出本身的事件类型的定义,一般作法是扩展java.util.EventObject类来实现自定义的事件类型。
    • 1.1 定义事件类型
package org.springframework.mylearntest.eventpublication.event;

import java.util.EventObject;

/**
 * 自定义事件类型
 */
public class MethodExecutionEvent extends EventObject {
	private static final long serialVersionUID = -71960369269303337L;
	private String methodName;

	public MethodExecutionEvent(Object source) {
		super(source);
	}

	public MethodExecutionEvent(Object source, String methodName) {
		super(source);
		this.methodName = methodName;
	}

	public String getMethodName() {
		return methodName;
	}

	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}
}
  • 1.2 定义事件监听器接口以及实现类
package org.springframework.mylearntest.eventpublication.event;


import java.util.EventListener;

/**
 * 自定义事件监听器
 */
public interface MethodExecutionEventListener extends EventListener {
	/**
	 * 处理方法开始执行的时候发布的MethodExecutionEvent事件
	 */
	void onMethodBegin(MethodExecutionEvent evt);
	/**
	 * 处理方法执行将结束时候发布的MethodExecutionEvent事件
	 */
	void onMethodEnd(MethodExecutionEvent evt);
}
package org.springframework.mylearntest.eventpublication.event;

/**
 * 自定义事件监听器实现
 */
public class SimpleMethodExecutionEventListener implements MethodExecutionEventListener {

	public void onMethodBegin(MethodExecutionEvent evt) {
		String methodName = evt.getMethodName();
		System.out.println("start to execute the method[" + methodName + "].");
	}

	public void onMethodEnd(MethodExecutionEvent evt) {
		String methodName = evt.getMethodName();
		System.out.println("finished to execute the method[" + methodName + "].");
	}
}
  • 1.3 定义事状态枚举类以及事件发布者
package org.springframework.mylearntest.eventpublication.event;

public enum MethodExecutionStatus {
	BEGIN,END
}
package org.springframework.mylearntest.eventpublication.event;

import java.util.ArrayList;
import java.util.List;

public class MethodExeuctionEventPublisher {
	private List<MethodExecutionEventListener> listeners = new
			ArrayList<MethodExecutionEventListener>();

	public void methodToMonitor() {
		MethodExecutionEvent event2Publish =
				new MethodExecutionEvent(this, "methodToMonitor");
		publishEvent(MethodExecutionStatus.BEGIN, event2Publish);
		// 执行实际的方法逻辑
		// ...
		publishEvent(MethodExecutionStatus.END, event2Publish);
	}

	// 为了不事件处理期间事件监听器的注册或移除操做影响处理过程,咱们对事件发布时点的监听器列表进行了一个安全复制( safe-copy)
	protected void publishEvent(MethodExecutionStatus status,
								MethodExecutionEvent methodExecutionEvent) {
		List<MethodExecutionEventListener> copyListeners =
				new ArrayList<MethodExecutionEventListener>(listeners);
		for (MethodExecutionEventListener listener : copyListeners) {
			if (MethodExecutionStatus.BEGIN.equals(status))
				listener.onMethodBegin(methodExecutionEvent);
			else
				listener.onMethodEnd(methodExecutionEvent);
		}
	}

	public void addMethodExecutionEventListener(MethodExecutionEventListener listener) {
		this.listeners.add(listener);
	}

	public void removeListener(MethodExecutionEventListener listener) {
		if (this.listeners.contains(listener))
			this.listeners.remove(listener);
	}

	public void removeAllListeners() {
		this.listeners.clear();
	}
}
  • 1.4 测试类
package org.springframework.mylearntest.eventpublication.event;

public class Test4Event {
	public static void main(String[] args) {
		MethodExeuctionEventPublisher eventPublisher =
				new MethodExeuctionEventPublisher();
		eventPublisher.addMethodExecutionEventListener(new
				SimpleMethodExecutionEventListener());
		eventPublisher.methodToMonitor();
		eventPublisher.removeAllListeners();
	}
}

在实现中,须要注意到,为了不事件处理期间事件监听器的注册或移除操做影响处理过程,咱们对事件发布时点的监听器列表进行了一个安全复制( safe-copy)。另外,事件的发布是顺序执行,因此为了可以不影响处理性能,事件监听器的处理逻辑应该尽可能简短。

自定义事件结构图

  1. Spring 的容器内事件发布类结构分析
    Spring 的 ApplicationContext 容 器 内 部 允 许 以 org.springframework.context.ApplicationEvent的形式发布事件 ,容器内注册的org.springframework.context.ApplicationListener类型的bean定义会被ApplicationContext容器自动识别,它们负责监听容器内发布的全部ApplicationEvent类型的事件。

ApplicationEvent
Spring容器内自定义事件类型,继承自java.util.EventObject,它是一个抽象类,须要根据状况提供相应子类以区分不一样状况。默认状况下, Spring提供了三个实现。

  • ContextClosedEvent: ApplicationContext容器在即将关闭的时候发布的事件类型。
  • ContextRefreshedEvent: ApplicationContext容器在初始化或者刷新的时候发布的事件类
    型。
  • RequestHandledEvent: Web请求处理后发布的事件,其有一子类ServletRequestHandledEvent提供特定于Java EE的Servlet相关事件。
package org.springframework.mylearntest.eventpublication.applicationevent;

import org.springframework.context.ApplicationEvent;
import org.springframework.mylearntest.eventpublication.event.MethodExecutionStatus;

public class MethodExecutionEvent extends ApplicationEvent {
	private static final long serialVersionUID = -71960369269303337L;
	private String methodName;
	private MethodExecutionStatus methodExecutionStatus;

	public MethodExecutionEvent(Object source) {
		super(source);
	}

	public MethodExecutionEvent(Object source, String methodName,
								MethodExecutionStatus methodExecutionStatus) {
		super(source);
		this.methodName = methodName;
		this.methodExecutionStatus = methodExecutionStatus;
	}

	public String getMethodName() {
		return methodName;
	}

	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	public MethodExecutionStatus getMethodExecutionStatus() {
		return methodExecutionStatus;
	}

	public void setMethodExecutionStatus(MethodExecutionStatus methodExecutionStatus) {
		this.methodExecutionStatus = methodExecutionStatus;
	}
}

ApplicationListener

  • ApplicationContext容器内使用的自定义事件监听器接口定义,继承自java.util.EventListener。 ApplicationContext容器在启动时,会自动识别并加载EventListener类型bean定义,一旦容器内有事件发布,将通知这些注册到容器的EventListener。
package org.springframework.mylearntest.eventpublication.applicationevent;


import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

@SuppressWarnings("rawtypes")
public class MethodExecutionEventListener implements ApplicationListener {
	public void onApplicationEvent(ApplicationEvent evt) {
		if (evt instanceof MethodExecutionEvent) {
			// 执行处理逻辑
		}
	}
}

ApplicationContext

  • 还记得ApplicationContext的定义吧?除了以前的ResourceLoader和MessageSource, ApplicationContext接口定义还继承了ApplicationEventPublisher接口,该接口提供了void publishEvent(ApplicationEvent event)方法定义。不难看出, ApplicationContext容器如今担当的就是事件发布者的角色。ApplicationContext容器的具体实现类在实现事件的发布和事件监听器的注册方面,并没事必躬亲,而是把这些活儿转包给了一个称做org.springframework.context.event.ApplicationEventMulticaster的接口。
package org.springframework.mylearntest.eventpublication.applicationevent;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.mylearntest.eventpublication.event.MethodExecutionStatus;

public class MethodExeuctionEventPublisher implements ApplicationEventPublisherAware {
	private ApplicationEventPublisher eventPublisher;

	public void methodToMonitor() {
		MethodExecutionEvent beginEvt = new
				MethodExecutionEvent(this, "methodToMonitor", MethodExecutionStatus.BEGIN);
		this.eventPublisher.publishEvent(beginEvt);
		// 执行实际方法逻辑
		// ...
		MethodExecutionEvent endEvt = new
				MethodExecutionEvent(this, "methodToMonitor", MethodExecutionStatus.END);
		this.eventPublisher.publishEvent(endEvt);
	}

	public void setApplicationEventPublisher(ApplicationEventPublisher appCtx) {
		this.eventPublisher = appCtx;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="methodExecListener" class="org.springframework.mylearntest.eventpublication.applicationevent.MethodExecutionEventListener">
	</bean>
	<bean id="evtPublisher" class="org.springframework.mylearntest.eventpublication.applicationevent.MethodExeuctionEventPublisher">
	</bean>

</beans>
package org.springframework.mylearntest.eventpublication.applicationevent;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4AppEvent {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("eventpublication/applicationevent.xml");
		MethodExeuctionEventPublisher evtPublisher = (MethodExeuctionEventPublisher) context.getBean("evtPublisher");
		evtPublisher.methodToMonitor();
	}
}

ApplicationEventMulticaster有一抽象实现类——org.springframework.context.event.AbstractApplicationEventMulticaster,它实现了事件监听器的管理功能。事件的发布功能则委托给了其子类。 org.springframework.context.event.SimpleApplicationEventMulticaster。其默认使用了SyncTaskExecutor进行事件的发布。为了不这种方式可能存在的性能问题,咱们能够为其提供其余类型的TaskExecutor实现类。

容器启动开始,就会检查容器内是否存在名称为applicationEventMulticaster的ApplicationEventMulticaster对象实例。有的话就使用提供的实现,没有则默认初始化一个SimpleApplicationEventMulticaster做为将会使用的ApplicationEventMulticaster。

Spring容器内事件发部实现类图

IoC相关注解

看着依赖注入相关的信息,一半分散在Java源代码中( @Autowired标注的信息),一半依然留在XML配置文件里,有不少bean标签依然存在。
当使用@Autoware注解可以同时找到两个或者多个同一类型的对象实例,可使用@Qualifier对依赖注入的条件作进一步限定,指定具体是哪一个id。

<beans>
<bean class="org.springframework.beans.factory.annotation. ➥
AutowiredAnnotationBeanPostProcessor"/>
<bean id="newsProvider" class="..FXNewsProvider"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="reutersNewsListner" class="..ReutersNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
</beans>
public class FXNewsProvider {
	@Autowired
	@Qualifier("reutersNewsListner")// 此时注入id=reutersNewsListner
	private IFXNewsListener newsListener;
	@Autowired
	private IFXNewsPersister newPersistener;
	...
}
// @Qualifier注解位于参数上
public class FXNewsProvider{
	// ...
	@Autowired
	public void setUp(@Qualifier("reutersNewsListner") IFXNewsListener newsListener,IFXNewsPersister newPersistener) {
		this.newsListener = newsListener;
		this.newPersistener = newPersistener;
	}
	// ...
}

@Resource与@Autowired不一样,它遵循的是byName自动绑定形式的行为准则,也就是说, IoC容器将根据@Resource所指定的名称,到容器中查找beanName与之对应的实例,而后将查找到的对象实例注入给@Resource所标注的对象。
@PostConstruct和@PreDestroy不是服务于依赖注入的,它们主要用于标注对象生命周期管理相关方法,这与Spring的InitializingBean和DisposableBean接口,以及配置项中的init-method和destroy-method起到相似的做用。

就像@Autowired须要AutowiredAnnotationBeanPostProcessor为 它 与 IoC 容 器 牵 线 搭 桥 一 样 , JSR250 的 这 些 注 解 也 同 样 需 要 一 个BeanPostProcessor帮助它们实现自身的价值。 这个BeanPostProcessor就是org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,只有将CommonAnnotationBeanPostProcessor添加到容器, JSR250的相关注解才能发挥做用。
<beans>
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
<bean id="newsProvider" class="..FXNewsProvider"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
</beans>

<context:annotation-config> 不 但 帮 我 们 把 AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor注册到容器,同时还会把PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor一并进行注册,可谓一举四得啊!

使用相应的注解对组成应用程序的相关类进行标注以后, classpath-scanning功能能够从某一顶层包( base package)开始扫描。当扫描到某个类标注了相应的注解以后,就会提取该类的相关信息,构建对应的BeanDefinition,而后把构建完的BeanDefinition注册到容器。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="org.spring21"/>
</beans>

<context:component-scan>它同时将AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor一并注册到了容器中,因此,依赖注入的需求得以知足。

相关文章
相关标签/搜索