标签:
对于上面的问题Spring提供了三种解决方案:
这里只说前两种方案的实现,第三种方案因为不常用,略过不提,有兴趣的可以了解一下。
一:实现环境
二:通过实现ApplicationContextAware接口以编程的方式实现
ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,实现了ApplicationContextAware接口的对象会拥有一个ApplicationContext的引用,这样我们就可以已编程的方式操作ApplicationContext。看下面的例子。
package com.flysnow.injection; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.flysnow.injection.command.Command; /** * 命令管理器 * @author 飞雪无情 * */ public class CommandManager implements ApplicationContextAware { //用于保存ApplicationContext的引用,set方式注入 private ApplicationContext applicationContext; //模拟业务处理的方法 public Object process(){ Command command=createCommand(); return command.execute(); } //获取一个命令 private Command createCommand() { return (Command) this.applicationContext.getBean("asyncCommand"); // } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext;//获得该ApplicationContext引用 } }
下面定义Command接口和其实现类AsyncCommand。
package com.flysnow.injection.command; /** * 一个异步处理命令的实现 * @author 飞雪无情 * */ public class AsyncCommand implements Command { /* (non-Javadoc) * @see com.flysnow.lookup.command.Command#execute() */ public Object execute() { //返回自身实例,是为了测试的时候好看出每次返回的不是同一个实例 return this; } }
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 通过scope="prototype"界定该bean是多例的 --> <bean id="asyncCommand" class="com.flysnow.injection.command.AsyncCommand" scope="prototype"></bean> <bean id="commandManager" class="com.flysnow.injection.CommandManager"> </bean> </beans>
以上主要是单例Bean commandManager的process()方法需要引用一个prototype(非单例)的bean,所以在调用process的时候先通过createCommand方法从容器中取得一个Command,然后在执行业务计算,代码中有注释,很简单。
测试类如下:
package com.flysnow.injection; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.flysnow.injection.CommandManager; public class TestCommandManager { private ApplicationContext context; @Before public void setUp() throws Exception { context=new ClassPathXmlApplicationContext("beans.xml"); } @Test public void testProcess() { CommandManager manager=context.getBean("commandManager", CommandManager.class); System.out.println("第一执行process,Command的地址是:"+manager.process()); System.out.println("第二执行process,Command的地址是:"+manager.process()); } }
可以通过控制台输出看到两次的输出借中的Command的地址是不一样的,因为我们为asyncCommand配置了scope="prototype"属性,这种方式就是使得每次从容器中取得的bean实例都不一样。通过这样方式我们实现了单例bean(commandManager)中的方法(process方法)引用非单例的bean(asyncCommand)。虽然我们实现了,但是这不是一种好的方法,因为我们的业务代码和Spring Framework产生了耦合。下面介绍Spring提供的另外一种干净的实现方式,就是Lookup方法注入。
三:通过Lookup方法注入来实现
使用这种方式很简单,因为Spring已经为我们做了很大一部分工作,我们要做的就是bean配置和业务类。
修改后的CommandManager和bean配置文件如下:
public abstract class CommandManager { //模拟业务处理的方法 public Object process(){ Command command=createCommand(); return command.execute(); } //获取一个命令 protected abstract Command createCommand(); }
运行测试,控制台打印出的两个Command的地址不一样,说明我们实现了。
<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。
这里的createCommand方法就成为被注入方法,他的定义形式必须为:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。还有,Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。
四:小结
Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,所以尽量采用这种方式。
标签:
原文地址:http://www.cnblogs.com/drizzlewithwind/p/5740145.html