spring ioc---DI進階之方法的注入和替換
阿新 • • 發佈:2018-12-12
方法注入解決的需求:
若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();
}
}
}