`

Spring IoC浅析

阅读更多

 

对象的三种注入方式:

1、接口注入:接口注入因为强制对象实现不必要的接口,带有侵入性 

2、构造注入:构造注入在同类型构造参数列表的情况下处理和维护会很困难,构造方法不能被继承且无法设置默认值 

3、setter方法注入:setter注入侵入性低,缺点是无法在对象构造后马上使用

 

 

Spring两种类型容器:

BeanFactory:基础类型IoC容器,提供完整的IoC服务,默认采用延迟加载策略,只有当客户端对象访问容器内对象时才会对客户端对象进行注入操作,容器启动较快,需要资源较少

ApplicationContext:在BeanFactory基础上构建,提供里事件发布、国际化等其它高级特性,在容器启动后默认全部初始化(Singleton)并绑定完成,相对于BeanFactory,启动时需要更对的资源

 

BeanFacotry和ApplicationContext继承关系如图:

 

BeanFactory作为Spring提供的基本IoC容器,可以完成对象的注册和对象间依赖关系的绑定,BeanFacotry接口有如下声明

public interface BeanFactory {
	/**
	 * 
	 */
	String FACTORY_BEAN_PREFIX = "&";
	/**
	 * 根据名称获取对象
	 */
	Object getBean(String name) throws BeansException;
	/**
	 * 根据名称和Class获取对象
	 */
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	/**
	 * 根据Class获取对象
	 */
	<T> T getBean(Class<T> requiredType) throws BeansException;
	/**
	 * 根据名称和参数获取对象
	 */
	Object getBean(String name, Object... args) throws BeansException;
	/**
	 * 查询对象是否存在与容器中
	 */
	boolean containsBean(String name);
	/**
	 * 查询对象是否是单例
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 查询对象是否是原型
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 是否匹配名称和类型
	 */
	boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
	/**
	 * 根据名称获得Class
	 */
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 获取别名
	 */
	String[] getAliases(String name);
}

原始方式:业务对象需要自己去pull所依赖的业务对象

IoC方式:业务对象需要依赖什么让BeanFacotry push过来

 

BeanFactory的注册与依赖绑定方式,编码实现

//POJO对象
public class Person {
	private String name;
	private Integer age;

	public Person() {
		super();
	}
	public Person( String name ) {
		super();
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	public Integer getAge() {
		return age;
	}
	public void setAge( Integer age ) {
		this.age = age;
	}
}
//BeanFactory注册/绑定测试
	@Test
	public void testDefaultListableBeanFactory() {
		//BeanFactotry定义了如何访问容器内管理的bean的API,具体实现类负责bean的注册及管理
		DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
		BeanFactory container = bindViaCode(beanRegistry);
		Person person = (Person) container.getBean("person");
		System.err.println(person);
	}

	public BeanFactory bindViaCode( BeanDefinitionRegistry registry ) {
	//创建Person对应的BeanDefinition,每一个受管理的对象在容器中都会有一个BeanDefinition实例与之对应
		//就像Java中每一个对象都是Class的实例一样,BeanDefinition实例负责保存对象的基本信息
		//如:对象的Class类型、是否是抽象类、构造方法参数、类中属性
		AbstractBeanDefinition personDefinition = new RootBeanDefinition(Person.class);
		//将Person对应的BeanDefinition注册到容器中,容器可以通过BeanDefinition中保存的信息构建Person
		registry.registerBeanDefinition("person", personDefinition);
		//通过构造方式为Person注入name属性
		ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
		argumentValues.addIndexedArgumentValue(0, "AAA");
		personDefinition.setConstructorArgumentValues(argumentValues);
		//通过setter方式为Person注入age注入
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.addPropertyValue(new PropertyValue("age", 11));
		personDefinition.setPropertyValues(propertyValues);
		return (BeanFactory) registry;
	}

 BeanFacotry、BeanDefinitionRegistry、DefaultListableBeanFactory关系如图

 

除了编码方式管理bean,SpringIoC容器支持Properties和XML配置文件方式管理bean的注册和依赖绑定,根据不同的BeanDefinitionReader(解析配置文件、装配BeanDefinition)实现类读取配置文件并映射到BeanDefinition,然后将映射后的BeanDefinition注册到BeanDefinitionRegistry中。PropertiesBeanDefinitionReader是读取properties文件,XmlBeanDefinitionReader读取xml配置文件。

在Spring2.5版本中增加了注解的方式,但需要JDK1.5以上版本才能支持,使用注解还需要配置包扫描

<context:component-scan base-package="..." />

 具体xml配置见Spring part 1:IoC和DI有详细的配置

Spring容器自动绑定方式:

no:容器默认方式,不采用任何形式的绑定,完全依赖手工明确配置

byName:类中声明的实例变量与xml配置文件中<bean>标签的id对应

byType:根据bean的类型寻找依赖对象, 根据byType装配时如果找到多个时无法区分具体需要哪种类型

constructor:byName和byType都是针对property的自动绑定,constructor是针对构造方法参数进行绑定,也是byType模式,找多个类型时也无法区分具体使用哪个

autodetect:对象拥有无参数的构造方法,容器会优先使用byType方式,否则使用contructor方式,如果通过constructor后还有对象没有被绑定,会对剩余对象属性进行byType方式绑定

注意事项:1、手工绑定覆盖自动绑定 2、自动绑定对String、Classes、数组、基本类型无效

 

Spring容器Bean的Scope:

singleton:在容器内保证singleton的bean只存在一个 共享实例,与Gof Singleton模式不同,该模式保证在同一个Classload中只能有一个bean实例

prototype:容器在接到该对象类型请求时会每次都重新创建新的返回给请求方,请求方需自己负责该对象后续的声明周期管理工作。

request:XmlWebApplicationContext会为每个http请求创建一个新的ReqeustProcessor供当前请求使用,请求结束后该对象声明周期结束

session:容器会为每个独立的session创建术语自己的对象

global:protlet中使用,在servlet中会被作为普通session处理

 

BeanFactoryPostProcessor实现

Spring容器加载bean的两个阶段:

1、容器启动阶段:加载配置文件、分析配置信息、装备到BeanDefinition、其它后处理

2、Bean实例化阶段:实例化对象、装配依赖、声明周期回调、对象其它处理、注册回调接口

Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制,允许在容器在启动阶段的最后,对容器内的BeanDefinition所保存的信息进行修改。

PropertyPlaceholderConfigurer:

允许在XML文件中使用Placeholder,并将这些Placeholder所代表的资源单独配置到properties文件中进行加载

	<bean id="placeholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:properties/jdbc.properties</value>
				<value>classpath:properties/config.properties</value>
			</list>
		</property>
	</bean>

当BeanFactory在第一阶段加载完所遇配置信息后,BeanFacotry中保存的对象的信息还是占位符形式,如${jdbc.url}、${jdbc.driver},当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用后,会使用properties中的信息替换BeanDefinition中的占位符所表示的属性值,在Bean实例化第二阶段完成替换。PropertyPlaceholderConfigurer不仅会从配置的properties文件中加载配置,同时还会检查Java的Properties,这也是PropertyPlaceholderConfigurer默认的加载方式

PropertyOverrideConfigurer:

对容器中任何你想处理的bean定义的property信息进行覆盖替换

CustomEditorConfigurer:

PropertyPlaceholderConfigurer、PropertyOverrideConfigurer这两个BeanFactoryPostPorcessor都是通过对BeanDefinition中的数据进行变更达到某种目的,CustomEditorConfigurer对BeanDefinition没有任何修改。不管对象是什么类型在通过xml或properties配置文件表示时都是String类型,需要通过程序把String类型转换成相对应的类型,要想完成String到具体类型的转换,都需要具体的转换规则,CustomEditorConfigurer帮我们传达了这种信息。

JDK中String内部使用java.beans.PropertyEditor来帮助String类进行转换工作,只要为每种类型都提供特定的PropertyEditor,就可以根据对象的类型取得String到特定对象的类型。

Spring也提供了部分PropertyEditor:

StringArrayPropertyEditor:将CVS格式的字符串根据“,”分割为String[],类似的还有ByteArrayPropertyEditor、CharArrayPropertyEditor

ClassEditor:根据String类型的class名称直接将起转换为Class对象,相当于Class.forName(String)

FileEditor:对java.io.File类型的支持,对资源进行定位的还有InputStreamEditor、 URLEditor

LocalEditor:针对java.lang.Local类型的PropertyEditor

PatternEditor:针对与正则的PropertyEditor

CustomDateEditor:针对与日期的PropertyEditor

以上这些PropertyEditor会默认被容器加载,如果我们想自定义PropertyEditor就需要通过CustomEditorConfigurer告知容器。

Spring2.0前提倡使用CustomEditorConfigurer的customEditors属性来配置

	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.util.Date" value-ref="自定义PropertyEditor"></entry>
			</map>
		</property>
	</bean>

 Srping2.0后提倡使用propertyEditorRegistrars属性配置

	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="propertyEditorRegistrars">
			<list>
				<ref bean="自定义PropertyEditor"/>
			</list>
		</property>
	</bean>

 

Bean的实例化过程如图

1、BeanWrapper

Spring采用策略设计模式为Bean实例化,通常有两种方式反射和CGLIB动态字节码。InstantiationStrategy定义了实例化Bean策略的接口,SimpleInstantiationStrategy实现类可以通过反射实例化bean,但不支持方法注入,CglibSubclassingInstantiationStrategy继承SimpleInstantiationStrategy通过CGLIB的动态字节码生成对象,但返回的是BeanWarrper包裹后的对象。

对比如下用BeanWrapper和Java反射分别操作对象的的方式,BeanWrapper更简单些,省去了很多异常处理的代码,Spring默认使用CglibSubclassingInstantiationStrategy。

		//BeanWrapper操作
		Object object = Class.forName("bean.Person").newInstance();
		BeanWrapper wrapper = new BeanWrapperImpl(object);
		wrapper.setPropertyValue("name", "AAA");
		wrapper.setPropertyValue("age", 11);
		System.out.println(wrapper.getWrappedInstance() instanceof Person);
		System.out.println(wrapper.getPropertyValue("name"));
		System.out.println(wrapper.getPropertyValue("age"));
		//反射操作
		Object object = Class.forName("bean.Person").newInstance();
		Class clazz = object.getClass();
		Field nameFiled = clazz.getDeclaredField("name");
		Field ageFiled = clazz.getDeclaredField("age");
		nameFiled.set(object, "aa");
		ageFiled.set(object, 11);
		System.out.println(nameFiled.get(object));
		System.out.println(ageFiled.get(object));
 2、Aware接口

 对象实例化完成并且设置相关属性及依赖后,Spring容器会检查该对象是否实现XXXAware接口,如果有将这些XXXAware接口中定义的依赖注入给当前对象

BeanNameAware:注入自己对象实例的Bean定义对应的beanName设置到当前对象实例

BeanClassLoaderAware:注入当前bean的Classloader。默认加载org.springframework.util.ClassUtils类的Classloader

BeanFactoryAware:BeanFactory容器会将自身注入到bean中

3、BeanPostProcessor

BeanPostProcessor用于容器对象实例化对象阶段,而BeanFactoryPostProcessor用于容器启动阶段,BeanFactoryPostProcessor处理的是BeanDefinition,BeanPostProcessor处理的是实例对象,BeanPostProcessor接口只有两个方法,在Bena实例化之前进行处理和Bean实例化之后进行处理

public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

 通过BeanPostProcessor给对象注入属性

//标记接口
public interface IStringMark {
	public String getString();
	public void setString( String string );
}
//实现
@Service
public class StringServiceImpl implements IStringMark {
	private String string;

	public String getString() {
		return string;
	}

	public void setString( String string ) {
		this.string = string;
	}
}
//BeanPostProcessor
@Component
public class StringBeanPostProcessor implements BeanPostProcessor {
	public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
		if ( bean instanceof IStringMark ) {
			IStringMark strMark = (IStringMark) bean;
			strMark.setString("AAAA");
		}
		return bean;
	}

	public Object postProcessAfterInitialization( Object bean, String beanName ) throws BeansException {
		return bean;
	}

}

SpringAOP更多的使用BeanPostProcessor生成代理对象,与ApplicationContext相关的Aware接口也是用BeanPostProcessor方式处理注入 

 4、InitializingBean和init-method

InitializingBean接口是个容器内部使用的一个对象声明周期标记接口,只有一个方法,执行时间点在容器实例化对象过程中调用BeanPostProcessor前置处理后会检测是否实现了InitializingBean接口,如果实现了该接口就调用afterPropertiesSet()。

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

如果业务对象实现该接口则显得Spring容器对业务对象具有侵入性,Spring提供了另外一种方式,XML配置<beam>标签中的init-method属性

 

统一资源加载策略

Spring使用Resource作为所有资源的抽象和访问接口,ResourceLoader作为资源的定位、加载。

Resource接口可以根据资源的不同类型或资源的不同位置给出不同的具体实现,这些实现类在org.springframework.core.io包下,也可以通过继承AbstractResource类来实现自定义的Resource接口。

ByteArrayResource:将byte数组提供的数据作为资源进行封装,比如功过InputStream访问的资源

ClassPathResource:从Java的Classpath中加载资源并进行封装,可以使用置顶的ClassLoader

FileSystemResource:对java.io.File类进行封装,以文件或URL形式对资源进行访问,只要能跟File打交道的资源几乎都能和FileSystemResource打交道

UrlResource:通过url进行资源的查找定位

InputStreamResource:将给定的InputStream视为一种资源

 

ResourceLoader接口可以查找和定位资源,相当于资源定位器,该接口有个默认实现DefaultResourceLoader

 

public interface ResourceLoader {
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
	Resource getResource(String location);
	ClassLoader getClassLoader();
}


 

ApplicationContext也继承了ResourcePatternResolver,所以间接实现了ResourceLoader接口,所以ApplicationContext实际也是一个ResourceLoader,这就是Spring内容易资源加载的逻辑。从实现的角度来看AbstractApplicationContext extends DefaultResourceLoader。


 有了这种实现ApplicatonContext加载任何Spring支持的Resource类型,当一个Bean需要ResourceLoader是可以使用Spring提供的Aware接口,比如ResourceLoaderAware、ApplicatonContextAware。

 

国际化信息支持

JavaSE提供的国际化支持使用Local代表不同国家和地区,比如代表中国

Locale.CHINA;
Locale.CHINESE;
Locale china = new Locale("zh", "CN");==Locale.CHINA;

ResourceBundle用于加载国家化properties文件

 

  • 大小: 13.1 KB
  • 大小: 81.5 KB
  • 大小: 95.2 KB
  • 大小: 55.6 KB
  • 大小: 153.9 KB
分享到:
评论

相关推荐

    浅析Java的Spring框架中IOC容器容器的应用

    Java的Spring框架是企业级应用开发的常用工具,它的核心组成部分是IoC(Inversion of Control,控制反转)容器,负责管理和协调应用程序的组件。在本文中,我们将深入探讨Spring框架中的IOC容器,特别是BeanFactory...

    浅析Spring配置文件

    首先,Spring配置文件是一个或多个XML文档,其主要作用是指导Spring IoC(Inversion of Control)容器生成Bean实例、处理Bean间的依赖关系以及控制Bean的生命周期。配置文件包含Bean的元数据,如Bean的类型、属性和...

    浅析springboot通过面向接口编程对控制反转IOC的理解

    浅析SpringBoot通过面向接口编程对控制反转IOC的理解 控制反转(IOC)是一种设计思想,而不是一种技术。它将对象的控制权交给容器,而不是传统的在对象内部直接控制。IOC的关键是要明确“谁控制谁,控制什么,为何...

    深入浅析Spring-boot-starter常用依赖模块

    深入浅析Spring-boot-starter常用依赖模块 Spring-boot-starter是一种基于Spring框架的依赖模块,它提供了许多实用的功能和配置,帮助开发者快速开发企业级应用程序。下面我们将详细介绍Spring-boot-starter常用...

    浅析Java企业开发.pdf

    "浅析Java企业开发" 本文将对Java企业开发的现状和发展趋势进行探讨,并对Struts、Hibernate、MyBatis和Spring这些开源框架进行分析。 首先, Java企业开发存在技术陈旧、保守,交付周期长,对新技术、新趋势的...

    浅析依赖注入框架Autofac的使用

     Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上也是很高的。 1)解压它的压缩包,主要看到Autofac.dll,Autofac.Configuration.dll,这也是本篇文章重点...

    轻松入门之struts2

    包括Struts2的核心概念、表单验证、国际化实现、拦截器、IOC模式探讨、Struts2标签的使用、OGNL表达式语言、文件上传下载功能、视图的浅析、与AJAX、Hibernate、Spring、iBatis、JQuery等技术的集成以及具体的应用...

    struts2的速成教程,充电的都来看了

    - **Spring与Struts2集成**:说明如何将Spring与Struts2相结合,以实现更为灵活的对象管理和事务控制。 #### 十五、集成iBatis - **iBatis简介**:介绍iBatis(现称为MyBatis)的基本概念及特点。 - **Struts2与...

    Struts2入门教程。包括jquery集成等。入门必看

    - **IoC容器**:介绍如何使用Struts2的IoC容器(如Spring)来管理对象的生命周期。 - **依赖注入**:讲解依赖注入的基本原理及其在Struts2中的应用。 #### 七、Struts2标签 - **标签库**:Struts2提供了一系列的...

    struts2 入门书籍

    为了构建复杂的企业级应用,Struts2通常会与其他框架如Hibernate(用于ORM)、Spring(用于IOC和AOP)集成,形成一套完整的开发解决方案。 #### 12. 实战项目示例 通过一系列实战项目,如投票管理系统、无纸化办公...

    基于BS架构微博系统任务书.docx

    Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)容器,用于简化企业级应用的开发;Spring MVC是Spring框架的一部分,主要用于构建Web应用;MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级...

    轻松入门之Struts2

    Struts2支持IOC模式,可以与Spring框架集成以更好地管理依赖关系。 #### 七、Struts2标签 Struts2提供了一系列标签库,这些标签可以简化JSP页面的编写工作。例如: - `&lt;s:textfield&gt;`:用于生成文本框。 - `...

    struts2经典入门教程

    7. IOC模式:Struts2整合了Spring框架,支持依赖注入(IOC)模式,这大大增强了框架的灵活性和可维护性。 8. Struts2标签:Struts2提供了一系列标签库,用于简化JSP页面的开发,包括数据的显示、表单的创建等。 9....

    struts2入门教程

    #### 七、探讨IOC模式 - **依赖注入**:介绍依赖注入的概念及其在Struts2中的应用。 - **Struts2与Spring集成**:讨论Struts2与Spring框架的整合方式,实现依赖管理。 #### 八、Struts2标签 - **常用标签**:列出并...

Global site tag (gtag.js) - Google Analytics