- 浏览: 336106 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
yueshang520:
太厉害了
Spring读取XML配置源码解析 -
levin_china:
jianyan163qq 写道好文章!我现在遇到一个调用的问题 ...
Java中Runtime.exec的一些事 -
wenlongsust:
exec可能存在注入漏洞,如何控制安全问题啊?
Java中Runtime.exec的一些事 -
RainWu:
...
设计模式感触之代理模式应用 -
ch_dj:
面向对象涉及原则:1.开闭原则2.少用继承,多用聚合3.针对 ...
设计模式感触之代理模式应用
一、 问题描述
在大部分情况下,容器中的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的另一个方法实现去替换自定义的方法。
三、 实现案例
3.1 放弃IOC
接口类:
package learn.frame.spring.scope.dropioc; public interface Command { public Object execute(); }
实现类:
package learn.frame.spring.scope.dropioc; public class AsyncCommand implements Command { @Override public Object execute() { return this; } }
业务类:
ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,实现了ApplicationContextAware接口的对象会拥有 一个ApplicationContext的引用,这样我们就可以已编程的方式操作ApplicationContext。看下面的例子。
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"); // } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext;//获得该ApplicationContext引用 } }
配置文件:beans-dropioc.xml
单例Bean commandManager的process()方法需要引用一个prototype(非单例)的bean,所以在调用process的时候先通过 createCommand方法从容器中取得一个Command,然后在执行业务计算。
scope="prototype"
<?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="learn.frame.spring.scope.dropioc.AsyncCommand" scope="prototype"></bean> <bean id="commandManager" class="learn.frame.spring.scope.dropioc.CommandManager"> </bean> </beans>
测试类:
package org.shupeng.learn.frame.spring.scope; import java.util.ArrayList; import org.junit.Before; import org.junit.Test; import learn.frame.spring.scope.dropioc.CommandManager; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestCommandManagerDropIOC { private ApplicationContext context; @Before public void setUp() throws Exception { context = new ClassPathXmlApplicationContext("beans-dropioc.xml"); } @Test public void testProcess() { CommandManager manager = (CommandManager) context.getBean("commandManager", CommandManager.class); System.out.println("第一执行process,Command的地址是:" + manager.process()); System.out.println("第二执行process,Command的地址是:" + manager.process()); } }
Test结果:
第一执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@187c55c 第二执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@ae3364
通过控制台输出看到两次的输出借中的Command的地址是不一样的,因为我们为asyncCommand配置了scope="prototype"属性,这种方式就是使得每次从容器中取得的bean实例都不一样。
业务代码和Spring Framework产生了耦合。
3.2 Look方法注入
这种方式Spring已经为我们做了很大一部分工作,要做的就是bean配置和业务类。
新的业务:
package learn.frame.spring.scope.lookup; import learn.frame.spring.scope.dropioc.Command; public abstract class CommandManager { //模拟业务处理的方法 public Object process() { Command command = createCommand(); return command.execute(); } //获取一个命令 protected abstract Command createCommand(); }
配置文件:beans-lookup.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="learn.frame.spring.scope.dropioc.AsyncCommand" scope="prototype"></bean> <bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager"> <lookup-method name="createCommand" bean="asyncCommand"/> </bean> </beans>
变化部分:
- 修改CommandManager类为abstract的,修改createCommand方法也为abstract的。
- 去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
- 修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
测试类:
package learn.frame.spring.scope; import org.junit.Before; import org.junit.Test; import learn.frame.spring.scope.lookup.CommandManager; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestCommandManagerLookup { private ApplicationContext context; @Before public void setUp() throws Exception { context = new ClassPathXmlApplicationContext("beans-lookup.xml"); } @Test public void testProcess() { CommandManager manager = (CommandManager) context.getBean("commandManager", CommandManager.class); System.out.println("第一执行process,Command的地址是:" + manager.process()); System.out.println("第二执行process,Command的地址是:" + manager.process()); } }
测试结果:
第一执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@5bb966 第二执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@1e903d5
控制台打印出的两个Command的地址不一样,说明实现了。
<public|protected> [abstract] <return-type> theMethodName(no-arguments)
- 被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法。
- 为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因。
- Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。
Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,所以尽量采用这种方式。
四、 原理分析(bean的scope属性范围)
scope用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC容器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另外三种scope类型,即request,session和global session类型。不过这三种类型有所限制,只能在web应用中使用,也就是说,只有在支持web应用的ApplicationContext中使用这三个scope才是合理的。
可以使用bean的singleton或scope属性来指定相应对象的scope,其中,scope属性只能在XSD格式的文档生命中使用,类似于如下代码所演示的形式:
DTD: <bean id ="mockObject1" class="..." singleton="false" /> XSD: <bean id ="mockObject1" class="..." scope="prototype" />
注意:这里的singleton和设计模式里面的单例模式不一样,标记为singleton的bean是由容器来保证这种类型的bean在同一个容器内只存在一个共享实例,而单例模式则是保证在同一个Classloader中只存在一个这种类型的实例。
4.1. singleton
singleton类型的bean定义,在一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例。singleton类型的bean定义,从容器启动,到他第一次被请求而实例化开始,只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。
通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope,所以,下面三种配置,形式实际上达成的是同样的效果:
DTD or XSD: <bean id ="mockObject1" class="..." /> DTD: <bean id ="mockObject1" class="..." singleton="true" /> XSD: <bean id ="mockObject1" class="..." scope="singleton" />
4.2 prototype
scope为prototype的bean,容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方。
虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一个新的实例之后,就由这个对象“自生自灭”了。
可以用以下方式定义prototype类型的bean:
DTD: <bean id ="mockObject1" class="..." singleton="false" /> XSD: <bean id ="mockObject1" class="..." scope="prototype" />
4.3 request ,session和global session
这三个类型是spring2.0之后新增的,他们不像singleton和prototype那么通用,因为他们只适用于web程序,通常是和XmlWebApplicationContext共同使用。
request:
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上说,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。
session:
对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们我们可以使用如下形式的制定scope为session:
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间,其他的方面真是没什么区别。
global session:
<bean id ="userPreferences" class="...UserPreferences" scope="globalsession" />
global session只有应用在基于porlet的web应用程序中才有意义,他映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待。
(我只是听说过porlet这个词,好像是和servlet类似的一种java web技术,大家以后遇到的时候可以搜一下!)
五、 新的扩展(注解方式)
自Spring3.x开始,增加了@Async这样一个注解,Spring 文档里是这样说的:
The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. </br> In other words, the caller will return immediately upon invocation and the actual execution of the method will </br> occur in a task that has been submitted to a Spring TaskExecutor.
就是说让方法异步执行。
参考文档:
Spring向单例中注入非单例实例——方法注入【新增加注解方式】
发表评论
-
Hadoop源码编译到eclipse
2013-12-31 11:13 1061将Hadoop源代码导入eclipse,是使用命令: ... -
ZooKeeper源码分析之启动
2012-10-01 16:08 0Full thread dump Java HotSpo ... -
Spring读取XML配置源码解析
2012-09-08 11:25 18948在Spring中,配置文件主要格式是XML,spring ... -
设计模式感触之代理模式-远程代理
2012-01-08 16:33 36611 远程代理的意义 远程代理为一个位于不同的地址空间的 ... -
设计模式感触之代理模式应用
2012-01-03 17:30 4338设计模式感触之代理模式应用 如果说看完设计模式之 ... -
Spring事务处理过程分析
2011-12-17 16:46 0org.springframework.transaction ... -
Tomcat启动错误的几件事
2011-12-14 10:41 8661第一件事:新建项目时在Dynamic web module v ... -
UML2.0中的Property和Attribute
2011-11-09 09:58 940仅仅描述原文,进行一定的翻译,细节再慢慢体会: ... -
Servlet path与映射策略
2011-11-08 09:38 8747三点: 1. 请求URI = context path ... -
[转]Spring包结构解析及相互依赖
2011-11-05 18:49 1774spring.jar是包含有完整发布的单个jar包,sprin ...
相关推荐
主要介绍了浅谈Spring单例Bean与单例模式的区别,具有一定借鉴价值,需要的朋友可以参考下
spring bean 的作用域(scope), SPringle bean的作用域
Spring In Action-3.2@Scope单例、多例Bean,Spring In Action-3.2@Scope单例、多例Bean,Spring In Action-3.2@Scope单例、多例Bean,Spring In Action-3.2@Scope单例、多例Bean
详解Spring中bean的scope以后使用,如何使用spring的作用域:
bean加入spring容器管理的方式,bean加入applicationcontext容器的方式
通过@Bean 和spring的factoryBean注入的bean. 以及对应@service注解注入的类 通过@Autowired 注入对象的时间是现根据类型在根据beanName获取的案例集合
这是在网上下载的例子,平时我们用的ioc都是在web中,本例实现在java application中实现依赖注入
spring动态向容器中添加bean和删除指定bean,不需要重启应用
Spring 的bean的作用域总结,详细的总结了 Spring 的bean的作用域
Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class文件,动态实现spring bean 的动态加载,并通过UrlClassLoader完成jar和class文件的加载。可以实现jar的热替换。spring的bean动态加载则需要对...
Quartz中注入Spring生成的Bean,是调度系统和业务系统联系在一起。
主要介绍了Spring bean为什么默认是单例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
主要介绍了详解Spring中bean的几种注入方式,主要介绍了4种注入,主要有属性注入、构造函数注入、工厂方法注入,非常具有实用价值,需要的朋友可以参考下
在非spring注解类中使用spring容器中的bean_普通类中使用yml配置文件中的配置信息,在大数据采集项目中用到的,已经测试过了
Spring Bean 的作用域之间有什么区别:Bean的作用域: 可以通过scope 属性来指定bean的作用域 ①singleton: 默认值。当IOC容器
spring基于xml配置加载bean 的demo 了解spring的反射机制
Spring-注入依赖,AOP,自动注入Bean。入门。为什么没有0积分?
本篇文章主要介绍了Spring 中如何控制2个bean中的初始化顺序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
spring bean配置 运行环境:eclipse 构建工具:maven 不提供maven构建,maven用来解决jar包的依赖
Spring Bean创建初始化流程