`

03-Spring3 依赖注入(DI)C_更多DI知识【转】

阅读更多

 

一、延迟初始化Bean

 

延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean

配置方式很简单只需在<bean>标签上指定lazy-init属性值为“true”即可延迟初始化Bean

Spring容器会在创建容器时提前初始化“singleton”作用域的Bean,“singleton”就是单例的意思即整个容器每个Bean只有一个实例,后边会详细介绍。Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。

延迟初始化的Bean通常会在第一次使用时被初始化;或者在被非延迟初始化Bean作为依赖对象注入时在会随着初始化该Bean时被初始化,因为在这时使用了延迟初始化Bean

容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的Bean定义上配置即可,比编程方式更简单,而且是无侵入代码的。

	<bean id="circleA" class="com.iflytek.demo.Demo" lazy-init="true"/>

 

二、使用depends-on

depends-on是指指定Bean初始化及销毁时的顺序,使用depends-on属性指定的Bean要先初始化完毕后才初始化当前Bean,由于只有“singletonBean能被Spring管理销毁,所以当指定的Bean都是“singleton”时,使用depends-on属性指定的Bean要在指定的Bean之后销毁。

注:文档中说销毁Bean的顺序:Dependent beans that define a depends-on relationship with a given bean aredestroyed first, prior to the given bean itself being destroyed. 

意思是:在depends-on属性中定义的“依赖Bean”要在定义该属性的Bean之前销毁。 

但实际是错误的,定义“depends-on”属性的Bean会首先被销毁,然后才是“depends-on”指定的Bean被销毁。大家可以试验一下。

<bean id="helloApi" class="com.iflytek.demo.HelloImpl"/>
<bean id="decorator" class="com.iflytek.demo.HelloApiDecorator"
    depends-on="helloApi">
    <property name="helloApi"><ref bean="helloApi"/></property>
</bean>

 

decorator”指定了“depends-on”属性为“helloApi”,所以在“decoratorBean初始化之前要先初始化“helloApi”,而在销毁“helloApi”之前先要销毁“decorator”,大家注意一下销毁顺序,与文档上的不符。

 depends-on”属性可以指定多个Bean,若指定多个Bean可以用“;”、“,”、空格分割。

那“depends-on”有什么好处呢?主要是给出明确的初始化及销毁顺序,比如要初始化“decorator”时要确保“helloApiBean的资源准备好了,否则使用“decorator”时会看不到准备的资源;而在销毁时要先在“decoratorBean的把对“helloApi”资源的引用释放掉才能销毁“helloApi”,否则可能销毁helloApi”时而“decorator”还保持着资源访问,造成资源不能释放或释放错误。

 

Demo

ResourceBean.java

package com.iflytek.demo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ResourceBean {
	private FileOutputStream fos;
	private File file;

	// 初始化方法
	public void init() {
		System.out.println("ResourceBean:========初始化");
		// 加载资源,在此只是演示
		System.out.println("ResourceBean:========加载资源,执行一些预操作");
		try {
			this.fos = new FileOutputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	// 销毁资源方法
	public void destroy() {
		System.out.println("ResourceBean:========销毁");
		// 释放资源
		System.out.println("ResourceBean:========释放资源,执行一些清理操作");
		try {
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public FileOutputStream getFos() {
		return fos;
	}

	public void setFile(File file) {
		this.file = file;
	}
}

 

DependentBean.java

package com.iflytek.demo;

import java.io.IOException;

public class DependentBean {
	ResourceBean resourceBean;

	public void write(String ss) throws IOException {
		System.out.println("DependentBean:=======写资源");
		resourceBean.getFos().write(ss.getBytes());
	}

	// 初始化方法
	public void init() throws IOException {
		System.out.println("DependentBean:=======初始化");
		resourceBean.getFos().write("DependentBean:=======初始化=====".getBytes());
	}

	// 销毁方法
	public void destroy() throws IOException {
		System.out.println("DependentBean:=======销毁");
		// 在销毁之前需要往文件中写销毁内容
		resourceBean.getFos().write("DependentBean:=======销毁=====".getBytes());
	}

	public void setResourceBean(ResourceBean resourceBean) {
		this.resourceBean = resourceBean;
	}
}

 

demors.xml

<bean id="resourceBean" class="com.iflytek.demo.ResourceBean"
		init-method="init" destroy-method="destroy">
		<!-- Spring容器能自动把字符串转换为java.io.File。 -->
		<property name="file" value="C:\Users\xdwang\Desktop\temp.txt" />
	</bean>
	<!-- init-method="init" : 指定初始化方法,在构造器注入和setter注入完毕后执行。 -->
	<!-- destroy-method="destroy":指定销毁方法,只有“singleton”作用域能销毁,“prototype”作用域的一定不能,其他作用域不一定能; -->
	<bean id="dependentBean" class="com.iflytek.demo.DependentBean"
		init-method="init" destroy-method="destroy" depends-on="resourceBean">
		<property name="resourceBean" ref="resourceBean" />
	</bean>

 

Test

	@Test
	public void testDependOn() throws IOException {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
				"demors.xml");
		// 一点要注册销毁回调,否则我们定义的销毁方法不执行
		context.registerShutdownHook();
		DependentBean dependentBean = context.getBean("dependentBean",
				DependentBean.class);
		dependentBean.write("aaa");
	}

 

输出结果:

ResourceBean:========初始化
ResourceBean:========加载资源,执行一些预操作
DependentBean:=======初始化
DependentBean:=======写资源
DependentBean:=======销毁
ResourceBean:========销毁
ResourceBean:========释放资源,执行一些清理操作

 

三、自动装配

自动装配就是指由Spring来自动地注入依赖对象,无需人工参与。

目前Spring3.0支持“no”、“byName ”、“byType”、“constructor”四种自动装配,默认是“no”指不支持自动装配的,其中Spring3.0已不推荐使用之前版本的“autodetect”自动装配,推荐使用Java 5+支持的(@Autowired)注解方式代替;如果想支持“autodetect”自动装配,请将schema改为“spring-beans-2.5.xsd”或去掉。

自动装配的好处是减少构造器注入和setter注入配置,减少配置文件的长度。自动装配通过配置<bean>标签的“autowire”属性来改变自动装配方式。接下来让我们挨着看下配置的含义。

1default:表示使用默认的自动装配,默认的自动装配需要在<beans>标签中使用default-autowire属性指定,其支持“no”、“byName ”、“byType”、“constructor”四种自动装配,如果需要覆盖默认自动装配,请继续往下看;

2no:意思是不支持自动装配,必须明确指定依赖。

3byName:通过设置Bean定义属性autowire="byName",意思是根据名字进行自动装配,只能用于setter注入。比如我们有方法“setHelloApi”,则“byName”方式Spring容器将查找名字为helloApiBean并注入,如果找不到指定的Bean,将什么也不注入。

 

例如如下Bean定义配置:

<bean id="helloApi" class="com.iflytek.demo.HelloImpl"/>
<bean id="bean" class="com.iflytek.demo.HelloApiDecorator" autowire="byName"/>

 

@Test  
public void testAutowireByName() throws IOException {  
	ClassPathXmlApplicationContext context =  
			new ClassPathXmlApplicationContext("autowire-byName.xml");  
    HelloApi helloApi = context.getBean("bean", HelloApi.class);  
    helloApi.sayHello();  
}  

 

是不是不要配置<property>了,如果一个bean有很多setter注入,通过“byName”方式是不是能减少很多<property>配置。此处注意了,在根据名字注入时,将把当前Bean自己排除在外:比如“helloBean类定义了“setHello”方法,则hello是不能注入到“setHello”的。

4、“byType”:通过设置Bean定义属性autowire="byType",意思是指根据类型注入,用于setter注入,比如如果指定自动装配方式为“byType”,而“setHelloApi”方法需要注入HelloApi类型数据,则Spring容器将查找HelloApi类型数据,如果找到一个则注入该Bean,如果找不到将什么也不注入,如果找到多个Bean将优先注入<bean>标签“primary”属性为trueBean,否则抛出异常来表明有个多个Bean发现但不知道使用哪个。让我们用例子来讲解一下这几种情况吧。

1)根据类型只找到一个Bean,此处注意了,在根据类型注入时,将把当前Bean自己排除在外,即如下配置中helloApibean都是HelloApi接口的实现,而“bean”通过类型进行注入“HelloApi”类型数据时自己是排除在外的,配置如下(具体测试请参考AutowireBeanTest.testAutowireByType1方法):

<bean class="com.iflytek.demo.HelloImpl"/>
<bean id="bean" class="com.iflytek.demo.HelloApiDecorator"
     autowire="byType"/>

 

2)根据类型找到多个Bean时,对于集合类型(如ListSet)将注入所有匹配的候选者,而对于其他类型遇到这种情况可能需要使用“autowire-candidate”属性为false来让指定的Bean放弃作为自动装配的候选者,或使用“primary”属性为true来指定某个Bean为首选Bean

       2.1)通过设置Bean定义的“autowire-candidate”属性为false来把指定Bean后自动装配候选者中移除:

<bean class="com.iflytek.demo.HelloImpl"/>
<!-- 从自动装配候选者中去除 -->
<bean class="com.iflytek.demo.HelloImpl"
autowire-candidate="false"/>
<bean id="bean1" class="com.iflytek.demo.HelloApiDecorator"
     autowire="byType"/>

 

 2.2)通过设置Bean定义的“primary”属性为false来把指定自动装配时候选者中首选Bean

<bean class="com.iflytek.demo.HelloImpl"/>
<!-- 自动装配候选者中的首选Bean-->
<bean class="com.iflytek.demo.HelloImpl" primary="true"/>
<bean id="bean" class="com.iflytek.demo.HelloApiDecorator"
     autowire="byType"/>

 

5、“constructor”:通过设置Bean定义属性autowire="constructor",功能和“byType”功能一样,根据类型注入构造器参数,只是用于构造器注入方式,直接看例子吧:

<bean class="com.iflytek.demo.HelloImpl"/>
<!-- 自动装配候选者中的首选Bean-->
<bean class="com.iflytek.demo.HelloImpl" primary="true"/>
<bean id="bean"
     class="com.iflytek.demo.HelloApiDecorator"
     autowire="constructor"/>

 

	@Test
	public void testAutowireByConstructor() throws IOException {
		ClassPathXmlApplicationContext context =
		 new ClassPathXmlApplicationContext("autowire-byConstructor.xml");
		HelloApi helloApi = context.getBean("bean", HelloApi.class);
		helloApi.sayHello();
	}

 

6autodetect:自动检测是使用“constructor”还是“byType”自动装配方式,已不推荐使用。如果Bean有空构造器那么将采用“byType”自动装配方式,否则使用“constructor”自动装配方式。此处要把3.0xsd替换为2.5xsd,否则会报错。

	<?xml version="1.0" encoding="UTF-8"?>
<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">
<bean class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
  <!-- 自动装配候选者中的首选Bean-->
  <bean class="cn.javass.spring.chapter2.helloworld.HelloImpl" primary="true"/>
  <bean id="bean"
        class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
        autowire="autodetect"/>
</beans>

 

可以采用在“<beans>”标签中通过“default-autowire”属性指定全局的自动装配方式,即如果default-autowire=byName”,将对所有Bean进行根据名字进行自动装配。

不是所有类型都能自动装配:

    不能自动装配的数据类型:Object、基本数据类型(DateCharSequenceNumberURIURLClassint)等;

    通过“<beans>”标签default-autowire-candidates属性指定的匹配模式,不匹配的将不能作为自动装配的候选者,例如指定“*Service*Dao”,将只把匹配这些模式的Bean作为候选者,而不匹配的不会作为候选者;

    通过将“<bean>”标签的autowire-candidate属性可被设为false,从而该Bean将不会作为依赖注入的候选者。

数组、集合、字典类型的根据类型自动装配和普通类型的自动装配是有区别的:

    数组类型、集合(SetCollectionList)接口类型:将根据泛型获取匹配的所有候选者并注入到数组或集合中,如“List<HelloApi> list”将选择所有的HelloApi类型Bean并注入到list中,而对于集合的具体类型将只选择一个候选者,“如 ArrayList<HelloApi> list”将选择一个类型为ArrayListBean注入,而不是选择所有的HelloApi类型Bean进行注入;

    字典(Map)接口类型:同样根据泛型信息注入,键必须为String类型的Bean名字,值根据泛型信息获取,如“Map<String, HelloApi> map将选择所有的HelloApi类型Bean并注入到map中,而对于具体字典类型如“HashMap<String, HelloApi> map”将只选择类型为HashMapBean注入,而不是选择所有的HelloApi类型Bean进行注入。

自动装配我们已经介绍完了,自动装配能带给我们什么好处呢?首先,自动装配确实减少了配置文件的量;其次,byType”自动装配能在相应的Bean更改了字段类型时自动更新,即修改Bean类不需要修改配置,确实简单了。

自动装配也是有缺点的,最重要的缺点就是没有了配置,在查找注入错误时非常麻烦,还有比如基本类型没法完成自动装配,所以可能经常发生一些莫名其妙的错误,在此我推荐大家不要使用该方式,最好是指定明确的注入方式,或者采用最新的Java5+注解注入方式。所以大家在使用自动装配时应该考虑自己负责项目的复杂度来进行衡量是否选择自动装配方式。

自动装配注入方式能和配置注入方式一同工作吗?当然可以,大家只需记住配置注入的数据会覆盖自动装配注入的数据。

大家是否注意到对于采用自动装配方式时如果没找到合适的的Bean时什么也不做,这样在程序中总会莫名其妙的发生一些空指针异常,而且是在程序运行期间才能发现,有没有办法能在提前发现这些错误呢?接下来就让我来看下依赖检查吧。

 

四、依赖检查

上一节介绍的自动装配,很可能发生没有匹配的Bean进行自动装配,如果此种情况发生,只有在程序运行过程中发生了空指针异常才能发现错误,如果能提前发现该多好啊,这就是依赖检查的作用。

依赖检查:用于检查Bean定义的属性都注入数据了,不管是自动装配的还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。

Spring3+也不推荐配置方式依赖检查了,建议采用Java5+ @Required注解方式,测试时请将XML schema降低为2.5版本的,和自动装配中“autodetect”配置方式的xsd一样。

 

<?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-2.5.xsd
</beans>

 

依赖检查有nonesimpleobjectall四种方式,接下来让我们详细介绍一下:

一、none:默认方式,表示不检查;

二、objects:检查除基本类型外的依赖对象,配置方式为:dependency-check="objects",此处我们为HelloApiDecorator添加一个String类型属性“message”,来测试如果有简单数据类型的属性为null,也不报错;

<bean id="helloApi" class="com.iflytek.demo.HelloImpl"/>
<!-- 注意我们没有注入helloApi,所以测试时会报错 -->
<bean id="bean"
     class="com.iflytek.demo.HelloApiDecorator"
     dependency-check="objects">
<property name="message" value="Haha"/>
</bean>

 

注意由于我们没有注入bean需要的依赖“helloApi”,所以应该抛出异常UnsatisfiedDependencyException,表示没有发现满足的依赖:

	@Test(expected = UnsatisfiedDependencyException.class)  
	public void testDependencyCheckByObject() throws IOException {  
		//将抛出异常  
	    new ClassPathXmlApplicationContext("dependency-check-object.xml");  
	}  

 

三、simple:对基本类型进行依赖检查,包括数组类型,其他依赖不报错;配置方式为:dependency-check="simple",以下配置中没有注入message属性,所以会抛出异常:

	<bean id="helloApi" class="com.iflytek.demo.HelloImpl"/>
	<!-- 注意我们没有注入message属性,所以测试时会报错 -->
	<bean id="bean" class="com.iflytek.demo.HelloApiDecorator"
	     dependency-check="simple">
	   <property name="helloApi" ref="helloApi"/>
	</bean>

 

四、all:对所以类型进行依赖检查,配置方式为:dependency-check="all",如下配置方式中如果两个属性其中一个没配置将报错。

	<bean id="helloApi" class="com.iflytek.demo.HelloImpl"/>
	<bean id="bean" class="com.iflytek.demo.HelloApiDecorator"
     dependency-check="all">
	  	<property name="helloApi" ref="helloApi"/>
		<property name="message" value="Haha"/>
	</bean>

 

 依赖检查也可以通过“<beans>”标签中default-dependency-check属性来指定全局依赖检查配置。

 

五、方法注入

所谓方法注入其实就是通过配置方式覆盖或拦截指定的方法,通常通过代理模式实现。Spring提供两种方法注入:查找方法注入和方法替换注入。

因为Spring是通过CGLIB动态代理方式实现方法注入,也就是通过动态修改类的字节码来实现的,本质就是生成需方法注入的类的子类方式实现。

在进行测试之前,我们需要确保将“com.springsource.cn.sf.cglib-2.2.0.jar”放到lib里并添加到“Java Build Path”中的Libararies中。否则报错,异常中包含“nested exception is java.lang.NoClassDefFoundError: cn/sf/cglib/proxy/CallbackFilter”。

 

传统方式和Spring容器管理方式唯一不同的是不需要我们手动生成子类,而是通过配置方式来实现;其中如果要替换createPrinter()方法的返回值就使用查找方法注入;如果想完全替换sayHello()方法体就使用方法替换注入。       接下来让我们看看具体实现吧。

 

一、查找方法注入:又称为Lookup方法注入,用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。使用<lookup-method name="方法名" bean="bean名字"/>配置;其中name属性指定方法名,bean属性指定方法需返回的Bean

 

方法定义格式:访问级别必须是publicprotected,保证能被子类重载,可以是抽象方法,必须有返回值,必须是无参数方法,查找方法的类和被重载的方法必须为非final

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

因为“singletonBean在容器中只有一个实例,而“prototypeBean是每次获取容器都返回一个全新的实例,所以如果“singletonBean在使用“prototype Bean情况时,那么“prototypeBean由于是“singletonBean的一个字段属性,所以获取的这个“prototypeBean就和它所在的“singletonBean具有同样的生命周期,所以不是我们所期待的结果。因此查找方法注入就是用于解决这个问题。

 

1  首先定义我们需要的类,Printer类是一个有状态的类,counter字段记录访问次数:

package com.iflytek.demo;
	public class Printer {
	    private int counter = 0;
	    public void print(String type) {
	        System.out.println(type + " printer: " + counter++);
	    }
	}

 

HelloImpl5类用于打印欢迎信息,其中包括setter注入和方法注入,此处特别需要注意的是该类是抽象的,充分说明了需要容器对其进行子类化处理,还定义了一个抽象方法createPrototypePrinter用于创建“prototypeBeancreateSingletonPrinter方法用于创建“singletonBean,此处注意方法会被Spring拦截,不会执行方法体代码:

package com.iflytek.dmeo;
	import com.iflytek.demo.HelloApi;
	import com.iflytek.demo.Printer;
	public abstract class HelloImpl5 implements HelloApi {
	    private Printer printer;
	    public void sayHello() {
	        printer.print("setter");
	        createPrototypePrinter().print("prototype");
	    }
	    public abstract Printer createPrototypePrinter();
	    public Printer createSingletonPrinter() {
	        System.out.println("该方法不会被执行,如果输出就错了");
	        return new Printer();
	    }
	    public void setPrinter(Printer printer) {
	        this.printer = printer;
	    }
	}

 

2  开始配置了,其中“prototypePrinter”是“prototypePrinter,“singletonPrinter”是“singletonPrinter,“helloApi1”是“singletonBean,而“helloApi2”注入了“prototypeBean

	<bean id="prototypePrinter" class="com.iflytek.demo.Printer"
		scope="prototype" />
	<bean id="singletonPrinter" class="com.iflytek.demo.Printer"
		scope="singleton" />
	<bean id="helloApi1" class="com.iflytek.demo.HelloImpl5" scope="singleton">
		<property name="printer" ref="prototypePrinter" />
		<lookup-method name="createPrototypePrinter" bean="prototypePrinter" />
		<lookup-method name="createSingletonPrinter" bean="singletonPrinter" />
	</bean>
	<bean id="helloApi2" class="com.iflytek.demo.HelloImpl5" scope="prototype">
		<property name="printer" ref="prototypePrinter" />
		<lookup-method name="createPrototypePrinter" bean="prototypePrinter" />
		<lookup-method name="createSingletonPrinter" bean="singletonPrinter" />
	</bean>

 

	@Test  
	public void testLookup() {  
		ClassPathXmlApplicationContext context =  
		new ClassPathXmlApplicationContext("lookupMethodInject.xml");  
		        System.out.println("=======singleton sayHello======");  
		        HelloApi helloApi1 = context.getBean("helloApi1", HelloApi.class);  
		        helloApi1.sayHello();  
		        helloApi1 = context.getBean("helloApi1", HelloApi.class);  
		        helloApi1.sayHello();  
		        System.out.println("=======prototype sayHello======");  
		        HelloApi helloApi2 = context.getBean("helloApi2", HelloApi.class);  
		        helloApi2.sayHello();  
		        helloApi2 = context.getBean("helloApi2", HelloApi.class);  
		        helloApi2.sayHello();  
	}

 

其中“helloApi1”测试中,其输出结果如下:

=======singleton sayHello======
setter printer: 0
prototype printer: 0
singleton printer: 0
setter printer: 1
prototype printer: 0
singleton printer: 1

 

首先“helloApi1”是“singleton”,通过setter注入的“printer”是“prototypePrinter”,所以它应该输出“setter printer:0”和“setter printer:1”;而“createPrototypePrinter”方法注入了“prototypePrinter”,所以应该输出两次“prototype printer:0”;而“createSingletonPrinter”注入了“singletonPrinter”,所以应该输出“singleton printer:0”和“singleton printer:1”。

而“helloApi2”测试中,其输出结果如下:

=======prototype sayHello======
setter printer: 0
prototype printer: 0
singleton printer: 2
setter printer: 0
prototype printer: 0
singleton printer: 3

 

首先“helloApi2”是“prototype”,通过setter注入的“printer”是“prototypePrinter”,所以它应该输出两次“setter printer:0”;而“createPrototypePrinter”方法注入了“prototypePrinter”,所以应该输出两次“prototype printer:0”;而“createSingletonPrinter”注入了“singletonPrinter”,所以应该输出“singleton printer:2”和“singleton printer:3”。

大家是否注意到“createSingletonPrinter”方法应该输出“该方法不会被执行,如果输出就错了”,而实际是没输出的,这说明Spring拦截了该方法并使用注入的Bean替换了返回结果。

方法注入主要用于处理“singleton”作用域的Bean需要其他作用域的Bean时,采用Spring查找方法注入方式无需修改任何代码即能获取需要的其他作用域的Bean

 

二、替换方法注入:也叫“MethodReplacer”注入,和查找注入方法不一样的是,他主要用来替换方法体。通过首先定义一个MethodReplacer接口实现,然后如下配置来实现:

<replaced-method name="方法名" replacer="MethodReplacer实现">  
<arg-type>参数类型</arg-type>  
</replaced-method>”

 

1)首先定义MethodReplacer实现,完全替换掉被替换方法的方法体及返回值,其中reimplement方法重定义方法功能,参数obj为被替换方法的对象,method为被替换方法,args为方法参数;最需要注意的是不能再通过“method.invoke(obj, new String[]{"hehe"});反射形式再去调用原来方法,这样会产生循环调用;如果返回值类型为Void,请在实现中返回null

package com.iflytek.demo.bean;
	import java.lang.reflect.Method;
	import org.springframework.beans.factory.support.MethodReplacer;
	public class PrinterReplacer implements MethodReplacer {
	    @Override
	    public Object reimplement(Object obj, Method method, Object[] args)   throws Throwable {
	        System.out.println("Print Replacer");
	        //注意此处不能再通过反射调用了,否则会产生循环调用,知道内存溢出
	        //method.invoke(obj, new String[]{"hehe"});
	        return null;
	    }
	}

 

2)配置如下,首先定义MethodReplacer实现,使用< replaced-method >标签来指定要进行替换方法,属性name指定替换的方法名字,replacer指定该方法的重新实现者,子标签< arg-type >用来指定原来方法参数的类型,必须指定否则找不到原方法:

<bean id="replacer" class="com.iflytek.demo.PrinterReplacer"/>
<bean id="printer" class="com.iflytek.demo.Printer">
<replaced-method name="print" replacer="replacer">
        <arg-type>java.lang.String</arg-type>
    </replaced-method>
</bean>

 

3)测试代码将输出“Print Replacer ”,说明方法体确实被替换了:

  @Test  
    public void testMethodReplacer() {  
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("methodReplacerInject.xml");  
        Printer printer = context.getBean("printer", Printer.class);  
        printer.print("我将被替换");  
    }  

 

转自:http://jinnianshilongnian.iteye.com/

  • 大小: 37.8 KB
分享到:
评论

相关推荐

    Spring学习笔记(5)----依赖注入的简单实现

    在Spring框架的学习中,依赖注入(Dependency Injection,简称DI)是一个核心概念,它极大地提高了代码的可测试性和可维护性。本篇笔记将探讨Spring如何实现依赖注入,并通过实例进行详细解析。 首先,理解依赖注入...

    Spring依赖注入DI.zip

    下面我们将深入探讨Spring依赖注入的概念、工作原理以及如何在实践中应用。 1. **依赖注入概念**: - 依赖:一个类对另一个类的使用称为依赖。 - 注入:将依赖的对象传递给需要它的类,而不是由类自己去创建或...

    java Spring DI依赖注入.rar

    在`chapter8`这个文件夹中,可能包含了关于Spring依赖注入的深入讲解,包括如何配置和使用,以及在实际项目中的应用示例。学习这部分内容可以帮助你理解如何利用Spring的DI特性来构建更优雅、更易于维护的Java应用。...

    Spring学习笔记(6)----编码剖析Spring依赖注入的原理

    本篇学习笔记将深入剖析Spring依赖注入的原理,通过源码分析帮助我们理解这一核心机制。 首先,依赖注入允许我们解耦组件之间的关系,使得各个组件可以独立地进行开发、测试和维护。在Spring中,DI主要通过两种方式...

    wangzhixuan-spring-shiro-training-master_java_

    1. **Spring框架**:Spring是Java EE应用的核心,它通过依赖注入(DI)和面向切面编程(AOP)来简化开发。DI允许外部容器管理对象的生命周期和依赖关系,AOP则允许在不修改代码的情况下实现横切关注点,如日志、事务...

    spring作业--转账案例.zip_protectionbkd_spring

    2. **依赖注入**:在转账案例中,Spring通过DI管理对象之间的依赖关系,使得代码更加灵活和可测试。开发者可以声明所依赖的组件,由Spring容器负责实例化、装配以及管理这些组件。 3. **Spring事务管理**:转账操作...

    Spring_依赖注入_面向接口编程

    依赖注入(DI)是Spring框架的核心特性之一,它允许我们把对象的依赖关系从代码中解耦出来,转而由Spring容器来管理。在传统的编程模式中,对象通常自行创建和管理它们所依赖的对象,但在Spring中,这些依赖是由外部...

    mybatis-spring-1.31.jar

    它实现了将MyBatis的SqlSession和Mapper接口与Spring的依赖注入(DI)和AOP特性相结合,从而简化了开发过程,提高了代码的可测试性和可维护性。 首先,MyBatis-Spring通过自动配置,可以将MyBatis的数据源、...

    spring-framework-master_spring_Framework_

    2. **IoC(控制反转)与DI(依赖注入)**:Spring的核心特性之一是IoC,它将对象的创建和管理权交给了Spring容器,通过XML配置或注解实现DI,使得代码更加灵活和解耦。 3. **AOP(面向切面编程)**:Spring提供了...

    Spring Ioc 注解 依赖注入

    其中,IoC和依赖注入(Dependency Injection, DI)是Spring框架最核心的部分之一。 #### 二、IoC容器概述 在传统的Java应用程序中,对象的创建和管理通常由程序员来完成。这种方式会导致代码之间存在高度耦合,难以...

    SPRING DI注入例子

    Spring框架的Dependency Injection(DI,依赖注入)是其核心特性之一,它允许开发者在运行时通过容器来管理对象及其依赖关系,而不是硬编码这些依赖。这样可以提高代码的可测试性和可维护性,降低了组件之间的耦合度...

    struts2-spring-plugin-2.3.4.jar

    在Java Web开发中,这两个框架经常一起使用,Spring 提供了依赖注入(DI)和面向切面编程(AOP)等功能,而 Struts 2 则是一个强大的MVC(Model-View-Controller)框架,负责处理用户请求和业务逻辑。 Struts 2 和 ...

    spring依赖注入底层详解

    Spring依赖注入是Spring框架的核心特性之一,它极大地简化了Java应用程序的开发,使得对象之间的依赖关系得以解耦,提高了代码的可测试性和可维护性。本文将深入探讨Spring依赖注入的底层实现机制。 首先,我们要...

    Spring_01_入门篇_依赖注入(控制反转)_ANN

    Spring的核心特性之一是依赖注入(Dependency Injection,简称DI),也称为控制反转(Inversion of Control,简称IoC)。 **依赖注入 (DI) 和 控制反转 (IoC)** 依赖注入是Spring的核心设计原则之一。它是一种设计...

    Spring_01_入门篇_依赖注入(控制反转)_XML

    在`Spring_01_入门篇_依赖注入(控制反转)_XML`中,我们可以看到一个或多个XML配置文件,它们定义了bean的定义,包括bean的类名、属性以及它们之间的依赖关系。例如: ```xml ``` 在这个例子中,`exampleBean`...

    spring依赖注入

    Spring依赖注入是Spring框架的核心特性之一,它使得对象之间的依赖关系得以解耦,增强了代码的可测试性和可维护性。依赖注入(Dependency Injection,简称DI)的理念是,一个对象不应该负责寻找并创建它所依赖的对象...

    spring2.0-reference_final_zh_cn.pdf

    Spring框架的核心特性包括依赖注入(Dependency Injection, DI)、面向切面编程(Aspect Oriented Programming, AOP)以及模块化设计等,这些特性极大地提高了代码的可重用性、灵活性和可测试性。 ### 依赖注入...

    Spring_Spring_教程8_注入_复杂类型的注入

    在Spring框架中,依赖注入(Dependency Injection,简称DI)是一种重要的设计模式,它使得对象之间的耦合度降低,提高了代码的可测试性和可维护性。本教程将专注于Spring中的复杂类型注入,包括集合、自定义类以及...

    spring依赖注入三种方式 测试源码

    通过阅读和理解这些源码,你可以更深入地了解Spring依赖注入的工作原理及其在项目中的具体使用。 在进行依赖注入时,Spring使用BeanFactory或ApplicationContext作为容器,负责创建、管理和装配Bean。容器读取配置...

    mybatis-spring-1.2.2

    MyBatis-Spring 的主要目标是将 MyBatis 的灵活性和 Spring 的依赖注入(DI)及事务管理能力结合起来。通过这个库,开发者可以轻松地在 Spring 应用中使用 MyBatis 的映射器和SqlSession。 2. **...

Global site tag (gtag.js) - Google Analytics