在Spring当中将用户的配置元数据转化为BeanDefinition之后,后面就会通过这个BeanDefinition创建Bean实例。将基于xml的配置元数据解析为BeanDefinition的过程在方法为BeanDefinitionParserDelegate#parseBeanDefinitionElement
,在这里方法中包括如下源码
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
这里涉及到方法注入的问题,其实对应的着xml中的两个标签,lookup-method
和replaced-method
,后面一个老师讲得也没啥问题,此处略过,主要说一下lookup-method
。当时老师举了一个例子,大致如下所示,然后说方法注入这种方式现在已经用不到了,所以没啥用。
<bean id="myCommand" class="fiona.apple.AsyncCommand">
<!-- inject dependencies here as required -->
</bean>
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
但是事实真的是这样的吗?通过查询官方文档,发现老师说的结果(没啥用
)可能是对的,但绝不是上面这个例子那么简单。
这个方法注入确实是用于解析依赖注入的,但是官方的例子与老师的例子是有差别的
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
这里被注入的bean是原型类型的(scope="prototype"
)。而且官方提供了注解类型配置方法
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
其实Lookup方法注入不是简简单单的一个依赖注入的问题,而是用于解决不同SCOPE类型Bean的注入问题或者说不同生命周期bean的依赖注入问题.
官方文档详细描述了不同生命周期的bean依赖注入的问题,关于了方法注入的具体使用方式和原理可以参考本人的另一篇博客:Spring注入之方法注入
这种方式由来已久,从下面这个类在1.1就有可见一斑
之所以这种方式慢慢变得少用了,一方面是因为有其他的方式解决不同生命周期的bean注入方式的出现。使用scoped-proxy
来解决问题。官方文档参考Scoped Beans as Dependencies.。相关的博客参考Spring注入之单例bean注入原型bean。
另一方面,更重要的一点是基于Java配置的方式出现后,完全可以按照如下的方式将一个原型Bean注入到单例Bean当中了。
package org.springwork.demo.config;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.*;
import org.springwork.demo.lookup.Command;
import org.springwork.demo.lookup.CommandManager;
import org.springwork.demo.service.DemoService;
//@ComponentScan(basePackages = {"org.springwork.demo"})
@Configuration
@ImportResource(value = {"classpath:applicationContext.xml"})
public class RootConfig {
// @Bean
// public DemoService demoService() {
// DemoService demoService = new DemoService();
// demoService.setTag("java");
// return demoService;
// }
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Bean
public Command command() {
return new Command();
}
@Bean
public CommandManager commandManager() {
return new CommandManager() {
@Override
protected Command createCommand() {
return command();
}
};
}
}
在这里定义了两个bean,前者为多例,后者为单例,此处在单例当中通过方法注入了多例。
其中另个类的定义如下
package org.springwork.demo.lookup;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//@Scope("prototype")
//@Component
public class Command {
private Object state;
public void setState(Object state) {
this.state = state;
}
public Object getState() {
return state;
}
public Object execute() {
System.out.println(this.hashCode() + " - execute");
return this.hashCode();
}
}
在这里调用对象的execute会打印hashcode,如果不是同一个对象,就会打印不同的hashcode
package org.springwork.demo.lookup;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
//@Component
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
// @Lookup
protected abstract Command createCommand();
}
最后的结果如下所示
获取的单例调用方法两次,调用注入的对象的方法,打印hashcode,而hashcode是不同的,说明确实在单例中注入了多例。而且上面这种方法也是最优雅的。
其实基于Java的方式,我也是参考Spring官方文档的。
但是这种基于Java的方式已经不算是Lookup方法注入了(没有lookup注解)。但是从另一个角度来说,在基于Java的元数据配置中的引用另一个方法来注入对应的bean何尝不是另一种方法注入呢?所以说看起来Lookup方法注入
是没有用的,但其实它要解决的问题依然存在,同时它的思想以另外一种方式存在着。从基于xml的元数据配置到基于注解的,再到基于Java的,它只是改变了容貌,但是内在还是那么一套吧。
在IOC容器中DI的重要性不言而喻,而不同生命周期的bean的注入问题伴随着不同元数据配置方式的不同解决的方案在不断的优化着。可见look-up方法注入的是何其的重要呀!你认为呢?
不知道老师是知道而不讲,还是没理解到这一层呢?如果是前者,那么这种课是不是有点敷衍呢?