Spring方法注入

引用
在大部分情况下,容器中的bean都是singleton类型的。如果一个singleton bean要引用另外一个singleton bean,或者一个非singleton bean要引用另外一个非singleton bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个非singleton(prototype)类型的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例


对于上面的问题Spring提供了三种解决方案: 
  • 放弃控制反转。通过实现ApplicationContextAware接口让bean A能够感知bean 容器,并且在需要的时候通过使用getBean("B")方式向容器请求一个新的bean B实例。
  • Lookup方法注入。Lookup方法注入利用了容器的覆盖受容器管理的bean方法的能力,从而返回指定名字的bean实例。
  • 自定义方法的替代方案。该注入能使用bean的另一个方法实现去替换自定义的方法。


这里只说前两种方案的实现,第三种方案因为不常用,略过不提,有兴趣的可以了解一下。 

一:实现环境  
  • Eclipse3.4
  • JDK1.5
  • Spring3.0.3
  • Junit 4测试框架
  • 依赖jar有log4j-1.2.16.jar,commons-logging-api-1.1.1.jar,cglib-nodep-2.2.jar。

二:通过实现ApplicationContextAware接口以编程的方式实现  
ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,实现了ApplicationContextAware接口的对象会拥有一个ApplicationContext的引用,这样我们就可以已编程的方式操作ApplicationContext。看下面的例子。 
Java代码   收藏代码
  1. package com.flysnow.injection;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4. import org.springframework.context.ApplicationContext;  
  5. import org.springframework.context.ApplicationContextAware;  
  6.   
  7. import com.flysnow.injection.command.Command;  
  8.   
  9. /** 
  10.  * 命令管理器 
  11.  * @author 飞雪无情 
  12.  * 
  13.  */  
  14. public class CommandManager implements ApplicationContextAware {  
  15.     //用于保存ApplicationContext的引用,set方式注入  
  16.     private ApplicationContext applicationContext;  
  17.     //模拟业务处理的方法  
  18.     public Object process(){  
  19.         Command command=createCommand();  
  20.         return command.execute();  
  21.     }  
  22.     //获取一个命令  
  23.     private Command createCommand() {  
  24.         return (Command) this.applicationContext.getBean("asyncCommand"); //  
  25.     }  
  26.   
  27.     public void setApplicationContext(ApplicationContext applicationContext)  
  28.             throws BeansException {  
  29.         this.applicationContext=applicationContext;//获得该ApplicationContext引用  
  30.     }  
  31.   
  32. }  

下面定义Command接口和其实现类AsyncCommand。 
Java代码   收藏代码
  1. package com.flysnow.injection.command;  
  2.   
  3. /** 
  4.  * 一个命令接口 
  5.  * @author 飞雪无情 
  6.  * 
  7.  */  
  8. public interface Command {  
  9.     /** 
  10.      * 执行命令 
  11.      * @return 
  12.      */  
  13.     public Object execute();  
  14. }  

Java代码   收藏代码
  1. package com.flysnow.injection.command;  
  2.   
  3. /** 
  4.  * 一个异步处理命令的实现 
  5.  * @author 飞雪无情 
  6.  * 
  7.  */  
  8. public class AsyncCommand implements Command {  
  9.   
  10.     /* (non-Javadoc) 
  11.      * @see com.flysnow.lookup.command.Command#execute() 
  12.      */  
  13.     public Object execute() {  
  14.         //返回自身实例,是为了测试的时候好看出每次返回的不是同一个实例  
  15.         return this;  
  16.     }  
  17.   
  18. }  

Bean配置文件如下: 
Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.         <!-- 通过scope="prototype"界定该bean是多例的 -->  
  7.         <bean id="asyncCommand" class="com.flysnow.injection.command.AsyncCommand" scope="prototype"></bean>  
  8.         <bean id="commandManager" class="com.flysnow.injection.CommandManager">  
  9.         </bean>  
  10. </beans>  

以上主要是单例Bean commandManager的process()方法需要引用一个prototype(非单例)的bean,所以在调用process的时候先通过createCommand方法从容器中取得一个Command,然后在执行业务计算,代码中有注释,很简单。 
测试类如下: 
Java代码   收藏代码
  1. package com.flysnow.injection;  
  2.   
  3. import org.junit.Before;  
  4. import org.junit.Test;  
  5. import org.springframework.context.ApplicationContext;  
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  7.   
  8. import com.flysnow.injection.CommandManager;  
  9.   
  10. public class TestCommandManager {  
  11.     private ApplicationContext context;  
  12.     @Before  
  13.     public void setUp() throws Exception {  
  14.         context=new ClassPathXmlApplicationContext("beans.xml");  
  15.     }  
  16.   
  17.     @Test  
  18.     public void testProcess() {  
  19.         CommandManager manager=context.getBean("commandManager", CommandManager.class);  
  20.         System.out.println("第一执行process,Command的地址是:"+manager.process());  
  21.         System.out.println("第二执行process,Command的地址是:"+manager.process());  
  22.     }  
  23.   
  24. }  


可以通过控制台输出看到两次的输出借中的Command的地址是不一样的,因为我们为asyncCommand配置了scope="prototype"属性,这种方式就是使得每次从容器中取得的bean实例都不一样。通过这样方式我们实现了单例bean(commandManager)中的方法(process方法)引用非单例的bean(asyncCommand)。虽然我们实现了,但是这不是一种好的方法,因为我们的业务代码和Spring Framework产生了耦合。下面介绍Spring提供的另外一种干净的实现方式,就是Lookup方法注入。 

三:通过Lookup方法注入来实现  
使用这种方式很简单,因为Spring已经为我们做了很大一部分工作,我们要做的就是bean配置和业务类。 
  • 首先修改CommandManager类为abstract的,修改createCommand方法也为abstract的。
  • 去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
  • 修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
  • 其他保持不变

修改后的CommandManager和bean配置文件如下: 
Java代码   收藏代码
  1. public abstract class CommandManager {  
  2.     //模拟业务处理的方法  
  3.     public Object process(){  
  4.         Command command=createCommand();  
  5.         return command.execute();  
  6.     }  
  7.     //获取一个命令  
  8.     protected abstract Command createCommand();  
  9. }  

Xml代码   收藏代码
  1. <bean id="commandManager" class="com.flysnow.injection.CommandManager">  
  2.             <lookup-method name="createCommand" bean="asyncCommand"/>  
  3.         </bean>  

运行测试,控制台打印出的两个Command的地址不一样,说明我们实现了。 
<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。 
这里的createCommand方法就成为被注入方法,他的定义形式必须为: 
Java代码   收藏代码
  1. <public|protected> [abstract] <return-type> theMethodName(no-arguments);  

被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。还有,Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。 

四:小结  
Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,所以尽量采用这种方式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring 中,构造方法注入是一种依赖注入的方式,它通过构造方法来实现对依赖对象的注入。以下是如何在 Spring 中进行构造方法注入的步骤: 1. 创建需要被注入的依赖对象的类,并在该类的构造方法上添加 `@Autowired` 注解。 ```java public class Dependency { // 使用@Autowired 注解进行构造方法注入 @Autowired public Dependency() { // 构造方法逻辑 } } ``` 2. 在需要使用该依赖对象的类中,声明该依赖对象的成员变量,并在该成员变量上添加 `@Autowired` 注解。 ```java public class MyClass { // 使用@Autowired 注解进行构造方法注入 @Autowired private Dependency dependency; } ``` 3. 在 Spring 的配置文件(例如 applicationContext.xml)中,配置依赖对象和需要使用该对象的类。 ```xml <!-- 配置依赖对象 --> <bean id="dependency" class="com.example.Dependency" /> <!-- 配置需要使用该对象的类 --> <bean id="myClass" class="com.example.MyClass" /> ``` 这样,当 Spring 容器启动时,会自动识别 `@Autowired` 注解,并在初始化对象时自动将依赖对象注入到需要使用它的地方。注意,需要保证配置文件中已经正确引入了 Spring 的命名空间和依赖注入的约束。 除了使用 `@Autowired` 注解进行构造方法注入,还可以使用 `@Inject` 注解或者通过 XML 配置文件的方式实现构造方法注入。具体选择哪种方式取决于个人偏好和项目需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值