1. 程式人生 > >spring ioc---DI進階之方法的注入和替換

spring ioc---DI進階之方法的注入和替換

方法注入解決的需求:

若bean A依賴bean B,在兩者生命週期不同的情況下,若bean A每次使用bean B的例項的時候,都需要擁有不同狀態的bean B的例項的話,就需要使用方法注入的功能,來實現此需求.(需要使用cglib技術)

注意,bean A中定義依賴bean B的方法,要遵循以下的格式:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);
方式 說明
實現ApplicationContextAware介面

耦合度較高,不建議使用

實現其具體方法,自定義注入依賴bean的方法即可

使用標籤`lookup-method` 只需在該標籤內指定方法名和依賴的bean之間的對映即可
使用註解@Lookup 在bean內部自定義依賴bean的方法,配置檔案開啟註解掃描

官方文件中對使用方法注入(方法查詢)的提醒事項:spring4.3.20版本

  • For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
  • Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
  • Concrete methods are also necessary for component scanning which requires concrete classe to pick up.
  • A further key limitation is that lookup methods won’t work with factory methods and in particular not with @Bean methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.

方法替換解決的需求:

在使用某bean的時候,因原有bean中的某一行為,無法滿足需求,需要針對此行為進行自定製.此時可以使用方法替換的功能,來對原bean的某一行為進行重寫.(需要使用cglib技術)

重寫方法的類物件需要實現MethodReplacer介面,在介面規定的方法內重寫方法的過程即可.

配置檔案標籤或屬性 說明
replaced-method(標籤) 對原bean需要重寫的方法進行定義
name(屬性) 需要重寫原有bean中某方法的方法名
replacer(屬性) 實現MethodReplacer介面例項的bean的id或name值
arg-type(標籤) 重寫方法的引數型別

具有依賴關係的類物件

package siye;
public class Person
{
}
package siye;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class UserByImpl implements ApplicationContextAware
{
	private ApplicationContext applicationContext;
	public Person createPerson()
	{
		return this.applicationContext.getBean("person", Person.class);
	}
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException
	{
		this.applicationContext = applicationContext;
	}
}
package siye;
/*
 * 對於此類需要注意的事項.
 * 1,類和方法不能使用final修飾.
 * 2,要定義抽象的方法和類.
 * 3,look-up對註解@Bean的支援度不高.
 * 4,需要cglib,spring4.3已內建cglib.
 */
public abstract class UserByTag
{
	/*
	 * 此抽象方法有格式要求.
	 * 需使用如下的格式.
	 * <public|protected> [abstract] <return-type> theMethodName(no-arguments);
	 */
	public abstract Person createPerson();
}
package siye;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component("userByAnno")
public abstract class UserByAnno
{
	/*
	 * 註解中value值的說明:
	 * This annotation attribute may suggest a target bean name to look up.
	 * If not specified, the target bean will be resolved based on the
	 * annotated method's return type declaration.
	 */
	@Lookup("person")
	public abstract Person createPerson();
}

重寫bean某行為的類物件和實現類

package siye;
public class TargetObj
{
	public void work(int num)
	{
		System.out.println("work..." + num + 12);
	}
	public void eat()
	{
		System.out.println("eat");
	}
}
package siye;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.beans.factory.support.MethodReplacer;
public class TargetObjReplaceImpl implements MethodReplacer
{
	/*
	 * 此方法引數的說明.
	 * 
	 * param obj the instance we're reimplementing the method for
	 * param method the method to reimplement
	 * param args arguments to the method
	 * return return value for the method
	 */
	@Override
	public Object reimplement(Object obj, Method method, Object[] args)
			throws Throwable
	{
		System.out.println(obj);
		System.out.println(method.getName());
		System.out.println(Arrays.toString(args));
		return null;
	}
}

配置檔案,config.xml

<!--test_lookup -->                                              
                                                                 
<bean id="person" class="siye.Person" scope="prototype" />       
<bean id="userByImpl" class="siye.UserByImpl" />                 
                                                                 
<bean id="userByTag" class="siye.UserByTag">                     
	<lookup-method name="createPerson" bean="person" />          
</bean>                                                          
                                                                 
<context:component-scan base-package="siye" />                   
                                                                 
<!--test_methodReplace -->                                       
<bean id="targetObjReplace" class="siye.TargetObjReplaceImpl" /> 
<bean id="targetObj" class="siye.TargetObj">                     
	<replaced-method name="work" replacer="targetObjReplace">    
		<arg-type>java.lang.Integer</arg-type>                   
	</replaced-method>                                           
</bean>                                                          

測試類

package siye;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UnitTest
{
	private ClassPathXmlApplicationContext context;
	@Before
	public void prepare()
	{
		String path = "classpath:/siye/config.xml";
		context = new ClassPathXmlApplicationContext(path);
	}
	// 在beanA中每次需要beanB的時候,保證獲取的是新的例項beanB.
	/**
	 * 第一種方式,實現指定介面.(不推薦的方式)
	 * 實現ApplicationContextAware介面的方式
	 * 來進行方法的注入.
	 */
	@Test
	public void testByImpl()
	{
		UserByImpl obj = context.getBean("userByImpl", UserByImpl.class);
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
	}
	/**
	 * 第二種方式,使用標籤`look-up`來實現.
	 */
	@Test
	public void testByTag()
	{
		// 初始化的是cglib生成的子類,然後向上造型指向父類.
		UserByTag obj = context.getBean("userByTag", UserByTag.class);
		// [email protected]
		System.out.println(obj);
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
	}
	/**
	 * 第三種方式使用註解@Lookup來實現.
	 */
	@Test
	public void testByAnno()
	{
		UserByAnno obj = context.getBean("userByAnno", UserByAnno.class);
		System.out.println(obj);
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
		System.out.println(obj.createPerson());
	}
	/*
	 * bean類方法的替換.
	 * 借用的也是cglib技術.
	 */
	@Test
	public void testMethodReplace()
	{
		TargetObj obj = context.getBean("targetObj", TargetObj.class);
		// 此時呼叫的是自己重寫的方法,在MethodReplacer的實現類中.
		obj.work(12);
	}
	@After
	public void destroy()
	{
		if (context != null)
		{
			context.close();
		}
	}
}