Spring的方法注入可分为两种
- 查找方法注入:用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。即我们通常所说的lookup-method注入。
- 替换方法注入:可以实现方法主体或返回结果的替换,即我们通常所说的replaced-method注入。
1.lookup-method注入
解决单例bean引用原型bean的问题。
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="apple" class="com.self.bean.initialization.method.overrides.lookup.Apple"/>
<bean id="banana" class="com.self.bean.initialization.method.overrides.lookup.Banana"/>
<bean id="fruitPlate1" class="com.self.bean.initialization.method.overrides.lookup.FruitPlate">
<lookup-method bean="apple" name="getFruit"></lookup-method>
</bean>
<bean id="fruitPlate2" class="com.self.bean.initialization.method.overrides.lookup.FruitPlate">
<lookup-method bean="banana" name="getFruit"></lookup-method>
</bean>
</beans>
public class Fruit {
public Fruit() {
System.out.println("I got a Fruit");
}
}
public class Apple extends Fruit {
public Apple() {
System.out.println("I got a Apple");
}
public void doSomething() {
System.out.println("Apple doSomething");
}
}
public class Banana extends Fruit {
public Banana() {
System.out.println("I got a Banana");
}
}
public abstract class FruitPlate {
public abstract Fruit getFruit();
}
public class LookUpTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("method.overrides.lookup.xml");
FruitPlate fruitPlate1 = (FruitPlate) classPathXmlApplicationContext.getBean("fruitPlate1");
Apple apple = (Apple) fruitPlate1.getFruit();
apple.doSomething();
}
}
输出:
> Task :spring-debug:LookUpTest.main()
I got a Fruit
I got a Apple
I got a Fruit
I got a Banana
Apple doSomething
源码分析
createBean() 时将mbd属性methodOverrides的overloaded设置为false.
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
调用CGLIB设置代理
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
//由于上文已经把overloaded设置为false. 所以走此逻辑
// Must generate CGLIB subclass.
//必须生成CGLIB字类
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
//反射生成实例
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance;
if (ctor == null) {
instance = BeanUtils.instantiateClass(subclass);
}
else {
try {
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
instance = enhancedSubclassConstructor.newInstance(args);
}
catch (Exception ex) {
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
}
}
// SPR-10785: set callbacks directly on the instance instead of in the
// enhanced class (via the Enhancer) in order to avoid memory leaks.
//设置代理
Factory factory = (Factory) instance;
factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
return instance;
}
LookUpTest
//获取配置的实例
Apple apple = (Apple) fruitPlate1.getFruit();
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
// 获取mbd中的LookupOverride
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(lo != null, "LookupOverride not found");
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
if (StringUtils.hasText(lo.getBeanName())) {
//返回bean
return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
this.owner.getBean(lo.getBeanName()));
}
else {
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));
}
}
}
2. replaced-method注入
<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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="replaceDog" class="com.self.bean.initialization.method.overrides.replace.ReplaceDog"/>
<bean id="originalDog" class="com.self.bean.initialization.method.overrides.replace.OriginalDog">
<replaced-method name="sayHello" replacer="replaceDog">
<arg-type match="java.lang.String"></arg-type>
</replaced-method>
</bean>
</beans>
public class OriginalDog {
public void sayHello() {
System.out.println("Hello,I am a black dog...");
}
public void sayHello(String name) {
System.out.println("Hello,I am a black dog, my name is " + name);
}
}
public class ReplaceDog implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("Hello, I am a white dog...");
Arrays.stream(args).forEach(str -> System.out.println("params:" + str));
return obj;
}
}
public class ReplaceTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("method.overrides.replace.xml");
OriginalDog originalDog = (OriginalDog) classPathXmlApplicationContext.getBean("originalDog");
originalDog.sayHello("dddddddddddd");
}
}
源码分析
注入方式同lookup-method
通过代理获取获取配置的实例
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(ro != null, "ReplaceOverride not found");
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
return mr.reimplement(obj, method, args);
}