- 浏览: 57980 次
文章分类
最新评论
Spring 方法注入 非单例bean的调用
在大部分情况下,容器中的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代码
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。
Java代码
package com.flysnow.injection.command;
/**
* 一个命令接口
* @author 飞雪无情
*
*/
public interface Command {
/**
* 执行命令
* @return
*/
public Object execute();
}
package com.flysnow.injection.command; /** * 一个命令接口 * @author 飞雪无情 * */ public interface Command { /** * 执行命令 * @return */ public Object execute(); }
Java代码
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代码
<?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,然后在执行业务计算,代码中有注释,很简单。
测试类如下:
Java代码
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类为abstract的,修改createCommand方法也为abstract的。
去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
其他保持不变
修改后的CommandManager和bean配置文件如下:
Java代码
public abstract class CommandManager {
//模拟业务处理的方法
public Object process(){
Command command=createCommand();
return command.execute();
}
//获取一个命令
protected abstract Command createCommand();
}
public abstract class CommandManager { //模拟业务处理的方法 public Object process(){ Command command=createCommand(); return command.execute(); } //获取一个命令 protected abstract Command createCommand(); }
Xml代码
<bean id="commandManager" class="com.flysnow.injection.CommandManager">
<lookup-method name="createCommand" bean="asyncCommand"/>
</bean>
<bean id="commandManager" class="com.flysnow.injection.CommandManager"> <lookup-method name="createCommand" bean="asyncCommand"/> </bean>
运行测试,控制台打印出的两个Command的地址不一样,说明我们实现了。
<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。
这里的createCommand方法就成为被注入方法,他的定义形式必须为:
Java代码
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。还有,Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。
四:小结
Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,所以尽量采用这种方式。
在大部分情况下,容器中的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代码
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。
Java代码
package com.flysnow.injection.command;
/**
* 一个命令接口
* @author 飞雪无情
*
*/
public interface Command {
/**
* 执行命令
* @return
*/
public Object execute();
}
package com.flysnow.injection.command; /** * 一个命令接口 * @author 飞雪无情 * */ public interface Command { /** * 执行命令 * @return */ public Object execute(); }
Java代码
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代码
<?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,然后在执行业务计算,代码中有注释,很简单。
测试类如下:
Java代码
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类为abstract的,修改createCommand方法也为abstract的。
去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
其他保持不变
修改后的CommandManager和bean配置文件如下:
Java代码
public abstract class CommandManager {
//模拟业务处理的方法
public Object process(){
Command command=createCommand();
return command.execute();
}
//获取一个命令
protected abstract Command createCommand();
}
public abstract class CommandManager { //模拟业务处理的方法 public Object process(){ Command command=createCommand(); return command.execute(); } //获取一个命令 protected abstract Command createCommand(); }
Xml代码
<bean id="commandManager" class="com.flysnow.injection.CommandManager">
<lookup-method name="createCommand" bean="asyncCommand"/>
</bean>
<bean id="commandManager" class="com.flysnow.injection.CommandManager"> <lookup-method name="createCommand" bean="asyncCommand"/> </bean>
运行测试,控制台打印出的两个Command的地址不一样,说明我们实现了。
<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。
这里的createCommand方法就成为被注入方法,他的定义形式必须为:
Java代码
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。还有,Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。
四:小结
Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,所以尽量采用这种方式。
发表评论
-
下载文件名乱码
2014-10-10 10:57 426/** * @return the downloa ... -
Java中的阻塞和非阻塞IO包各自的优劣思考
2013-11-06 10:09 549转载地址:http://javag.itey ... -
java NIO简介
2013-11-06 10:04 5711. 基本 概念 IO 是主存和外部 ... -
PoI导出数据
2013-09-12 18:16 520用 HSSFWorkbook导出xls 只能导出6w多条数据 ... -
java.io.FileNotFoundException:文件名、目录名或卷标语法不正确
2013-03-30 20:41 2599java.io.FileNotFoundExcepti ...
相关推荐
Spring Boot普通类调用bean【从零开始学Spring Boot】”旨在指导初学者如何在非Spring管理的类中访问和使用Spring容器中的bean。下面将详细讲解这个主题。 首先,了解Spring Boot的基础概念是必要的。Spring Boot...
在`doCreateBean()`方法中,Spring会创建Bean的实例,`createBeanInstance(beanName, mbd, args)`执行Bean实例的创建,而`populateBean(beanName, mbd, instanceWrapper)`则负责填充Bean的属性,将依赖注入到Bean中...
- **销毁回调**:当Spring容器关闭时,或者单例Bean不再需要时,会调用`@PreDestroy`注解的方法或者在XML中指定的`destroy-method`属性的方法。 - ** Prototype Beans不保证销毁**:由于Prototype Bean可能有多个...
- 对于单例Bean,Spring容器在关闭时会调用`destroy-method`属性指定的方法或带有`@PreDestroy`注解的方法进行清理工作。对于非单例Bean,通常不进行销毁操作。 7. **生命周期回调** - Spring提供了一些回调接口...
综上所述,Spring Bean重复执行两次的问题通常是由于配置错误、依赖注入循环、初始化回调的不当使用、静态工厂方法的误用、AOP代理的配置问题或是Bean作用域设置不准确导致的。通过仔细检查和修正这些问题,可以避免...
- **@PostConstruct** 和 **@PreDestroy**:标记初始化和销毁方法,Spring会在Bean创建后和销毁前调用这些方法。 在`spring_autozp_demo`压缩包中,可能包含了一个Spring应用的示例代码,演示了如何使用上述注解和...
当我们需要在非Spring管理的普通类中调用Spring管理的bean对象时,需要掌握特定的方法。这篇文档将详细介绍如何实现这一操作,并探讨相关的源码和技术工具。 首先,理解Spring的核心概念——依赖注入(Dependency ...
3. **Bean接口回调**:如果Bean实现了特定的接口,如`BeanNameAware`、`BeanFactoryAware`或`ApplicationContextAware`,Spring会在适当的时候调用对应的回调方法。这些接口允许Bean获取其ID、BeanFactory引用或...
单例bean在整个应用中只有一个实例,而原型bean每次请求都会创建新的实例。其他作用域主要适用于Web应用。 **生命周期管理** Spring容器对bean的生命周期进行管理,包括初始化、活跃期和销毁。你可以为bean定义...
3. **初始化**:在属性注入完成后,Spring会执行Bean实现的初始化回调方法,这些方法可以通过实现`InitializingBean`接口的`afterPropertiesSet()`方法或者使用`init-method`属性来指定。此外,还可以通过配置`@...
- **单例Bean的实例化**:对于非懒加载(lazy-init)的单例Bean,Spring会使用反射机制创建实例并将其存储在内存中,以备后续请求使用。 - **依赖注入**:在实例化Bean的过程中,Spring会检查BeanDefinition中的依赖...
- **工厂方法**:可以定义一个静态或非静态工厂方法,Spring调用这个方法来实例化bean。 3. **依赖注入(DI)** Spring的依赖注入允许bean之间通过接口而非具体实现进行耦合,提高了代码的可测试性和可维护性。...
在这个例子中,`setRedisService`方法被`@Autowired`注解,Spring会在初始化时调用这个方法,将`RedisService`实例注入到`redisService`字段。然后,静态方法`t()`就可以安全地访问这个实例了。 **解决方法2:降低...
对于非POJO对象或者有特殊初始化需求的对象,Spring提供工厂方法来创建并注入依赖。 6. **@Qualifier注解**: 当有多个相同类型的bean,而我们需要指定具体哪一个时,`@Qualifier`注解可以用来明确指定。 7. **@...
单例Bean在整个应用中只有一个实例,而原型Bean每次请求都会创建新的实例。 7. **AOP**:面向切面编程是Spring的另一大特色,它允许我们在不修改源代码的情况下,添加额外的行为。例如,我们可以通过切面实现日志...
初始化方法在Bean创建后且所有依赖注入完成之前调用,而销毁方法在Bean被移除前调用。这为我们提供了在Bean生命周期中的特定时刻执行逻辑的机会。 依赖注入是Spring的核心特性,Bean定义可以明确地声明其依赖的其他...
当在单例Bean的方法上使用时,Spring会在每次调用该方法时返回一个新的Bean实例。 ```java public abstract class AbstractService { @Lookup public abstract MyService createMyService(); } ``` 7. **`@...
- 谨慎处理初始化:ApplicationContext在初始化时会实例化所有单例Bean,可能导致性能影响,尤其是在大型应用中。 - 避免过度使用`getBean()`:频繁使用`getBean()`可能导致代码过于紧密地耦合于Spring容器,降低...
Spring 框架是 Java 后端开发中的核心组件,它提供了一种强大的依赖注入(Dependency Injection,简称 DI)机制,使得开发者可以更加专注于业务逻辑,而非对象的创建与管理。"粗略实现spring创建bean"这个主题主要...
对于Singleton Bean,Spring在容器关闭时会调用`@PreDestroy`注解的方法和实现DisposableBean接口的`destroy()`方法。对于Prototype作用域的Bean,Spring不会自动管理销毁,需要由用户负责。 在实际应用中,理解...