方法一:在初始化时保存ApplicationContext对象
方法二:通过Spring提供的工具类获取ApplicationContext对象
方法三:继承自抽象类ApplicationObjectSupport
方法四:继承自抽象类WebApplicationObjectSupport
方法五:实现接口ApplicationContextAware

常用的5种获取spring 中bean的方式总结:

方法一:在初始化时保存ApplicationContext对象

ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ac.getBean("beanId");

说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。

方法二:通过Spring提供的工具类获取ApplicationContext对象

import org.springframework.web.context.support.WebApplicationContextUtils;
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc);
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
ac1.getBean("beanId");
ac2.getBean("beanId");

说明:
这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。

上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。

方法三:继承自抽象类ApplicationObjectSupport
说明:抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。

方法四:继承自抽象类WebApplicationObjectSupport
说明:类似上面方法,调用getWebApplicationContext()获取WebApplicationContext

方法五:实现接口ApplicationContextAware
说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。
Spring初始化时,会通过该方法将ApplicationContext对象注入。

虽然,spring提供了后三种方法可以实现在普通的类中继承或实现相应的类或接口来获取spring 的ApplicationContext对象,但是在使用是一定要注意实现了这些类或接口的普通java类一定要在Spring 的配置文件application-context.xml文件中进行配置。否则获取的ApplicationContext对象将为null。

如下是我实现了ApplicationContextAware接口的例子

package com.dxz.spring.ioc;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class SpringConfigTool implements ApplicationContextAware {// extends
// ApplicationObjectSupport{ private static ApplicationContext context = null;
private static SpringConfigTool stools = null; public synchronized static SpringConfigTool init() {
if (stools == null) {
stools = new SpringConfigTool();
}
return stools;
} public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context = applicationContext;
} public synchronized static Object getBean(String beanName) {
return context.getBean(beanName);
} }

其次在applicationContext.xml文件进行配置:

<bean id="SpringConfigTool" class="com.dxz.spring.ioc.SpringConfigTool"/>

最后提供一种不依赖于servlet,不需要注入的方式
注意一点,在服务器启动时,Spring容器初始化时,不能通过以下方法获取Spring 容器,如需细节可以观看源码org.springframework.web.context.ContextLoader

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->1 import org.springframework.web.context.ContextLoader; 2 import org.springframework.web.context.WebApplicationContext; 3 4 WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext(); 5 wac.getBean(beanID);

Spring注入非单例bean以及scope的作用范围

一、 问题描述

       在大部分情况下,容器中的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

接口类:

  1. package learn.frame.spring.scope.dropioc;
  2. public interface Command {
  3. public Object execute();
  4. }

实现类:

  1. package learn.frame.spring.scope.dropioc;
  2. public class AsyncCommand implements Command {
  3. @Override
  4. public Object execute() {
  5. return this;
  6. }
  7. }

业务类:

ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,实现了ApplicationContextAware接口的对象会拥有        一个ApplicationContext的引用,这样我们就可以已编程的方式操作ApplicationContext。看下面的例子。

  1. public class CommandManager implements ApplicationContextAware {
  2. //用于保存ApplicationContext的引用,set方式注入
  3. private ApplicationContext applicationContext;
  4. //模拟业务处理的方法
  5. public Object process() {
  6. Command command = createCommand();
  7. return command.execute();
  8. }
  9. //获取一个命令
  10. private Command createCommand() {
  11. return (Command) this.applicationContext.getBean("asyncCommand"); //
  12. }
  13. @Override
  14. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  15. this.applicationContext = applicationContext;//获得该ApplicationContext引用
  16. }
  17. }

配置文件:beans-dropioc.xml

单例Bean commandManager的process()方法需要引用一个prototype(非单例)的bean,所以在调用process的时候先通过            createCommand方法从容器中取得一个Command,然后在执行业务计算。

scope="prototype"

  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="learn.frame.spring.scope.dropioc.AsyncCommand"
  8. scope="prototype"></bean>
  9. <bean id="commandManager" class="learn.frame.spring.scope.dropioc.CommandManager">
  10. </bean>
  11. </beans>

测试类:

  1. package org.shupeng.learn.frame.spring.scope;
  2. import java.util.ArrayList;
  3. import org.junit.Before;
  4. import org.junit.Test;
  5. import learn.frame.spring.scope.dropioc.CommandManager;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;
  8. public class TestCommandManagerDropIOC {
  9. private ApplicationContext context;
  10. @Before
  11. public void setUp() throws Exception {
  12. context = new ClassPathXmlApplicationContext("beans-dropioc.xml");
  13. }
  14. @Test
  15. public void testProcess() {
  16. CommandManager manager = (CommandManager) context.getBean("commandManager",
  17. CommandManager.class);
  18. System.out.println("第一执行process,Command的地址是:" + manager.process());
  19. System.out.println("第二执行process,Command的地址是:" + manager.process());
  20. }
  21. }

Test结果:

  1. 第一执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@187c55c
  2. 第二执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@ae3364

通过控制台输出看到两次的输出借中的Command的地址是不一样的,因为我们为asyncCommand配置了scope="prototype"属性,这种方式就是使得每次从容器中取得的bean实例都不一样。

业务代码和Spring Framework产生了耦合。

3.2 Look方法注入

这种方式Spring已经为我们做了很大一部分工作,要做的就是bean配置和业务类。

新的业务:

  1. package learn.frame.spring.scope.lookup;
  2. import learn.frame.spring.scope.dropioc.Command;
  3. public abstract class CommandManager {
  4. //模拟业务处理的方法
  5. public Object process() {
  6. Command command = createCommand();
  7. return command.execute();
  8. }
  9. //获取一个命令
  10. protected abstract Command createCommand();
  11. }

配置文件:beans-lookup.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="learn.frame.spring.scope.dropioc.AsyncCommand"
  8. scope="prototype"></bean>
  9. <bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager">
  10. <lookup-method name="createCommand" bean="asyncCommand"/>
  11. </bean>
  12. </beans>

变化部分:

  • 修改CommandManager类为abstract的,修改createCommand方法也为abstract的。
  • 去掉ApplicationContextAware的实现及相关set方法和applicationContext变量定义
  • 修改bean配置文件,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。

测试类:

  1. package learn.frame.spring.scope;
  2. import org.junit.Before;
  3. import org.junit.Test;
  4. import learn.frame.spring.scope.lookup.CommandManager;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;
  7. public class TestCommandManagerLookup {
  8. private ApplicationContext context;
  9. @Before
  10. public void setUp() throws Exception {
  11. context = new ClassPathXmlApplicationContext("beans-lookup.xml");
  12. }
  13. @Test
  14. public void testProcess() {
  15. CommandManager manager = (CommandManager) context.getBean("commandManager",
  16. CommandManager.class);
  17. System.out.println("第一执行process,Command的地址是:" + manager.process());
  18. System.out.println("第二执行process,Command的地址是:" + manager.process());
  19. }
  20. }

测试结果:

  1. 第一执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@5bb966
  2. 第二执行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@1e903d5

控制台打印出的两个Command的地址不一样,说明实现了。

<lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪种类型的Command的,这里是AsyncCommand。
 
  1. <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格式的文档生命中使用,类似于如下代码所演示的形式:

  1. DTD:
  2. <bean id ="mockObject1" class="..." singleton="false" />
  3. XSD:
  4. <bean id ="mockObject1" class="..."   scope="prototype" />

注意:这里的singleton和设计模式里面的单例模式不一样,标记为singleton的bean是由容器来保证这种类型的bean在同一个容器内只存在一个共享实例,而单例模式则是保证在同一个Classloader中只存在一个这种类型的实例。

4.1. singleton

singleton类型的bean定义,在一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例。singleton类型的bean定义,从容器启动,到他第一次被请求而实例化开始,只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。

通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope,所以,下面三种配置,形式实际上达成的是同样的效果:

  1. DTD or XSD:
  2. <bean id ="mockObject1" class="..." />
  3. DTD:
  4. <bean id ="mockObject1" class="..." singleton="true" />
  5. XSD:
  6. <bean id ="mockObject1" class="..."   scope="singleton" />

4.2 prototype

scope为prototype的bean,容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方。

虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一个新的实例之后,就由这个对象“自生自灭”了。

可以用以下方式定义prototype类型的bean:

  1. DTD:
  2. <bean id ="mockObject1" class="..." singleton="false" />
  3. XSD:
  4. <bean id ="mockObject1" class="..."   scope="prototype" />

4.3 request ,session和global session

这三个类型是spring2.0之后新增的,他们不像singleton和prototype那么通用,因为他们只适用于web程序,通常是和XmlWebApplicationContext共同使用。

 request:

  1. <bean id ="requestPrecessor" class="...RequestPrecessor"   scope="request" />

Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上说,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。

        session

对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们我们可以使用如下形式的制定scope为session:

  1. <bean id ="userPreferences" class="...UserPreferences"   scope="session" />

Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间,其他的方面真是没什么区别。

global session:

  1. <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 文档里是这样说的:

  1. The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. </br>
  2. In other words, the caller will return immediately upon invocation and the actual execution of the method will </br>
  3. occur in a task that has been submitted to a Spring TaskExecutor.

就是说让方法异步执行。

普通Java类获取spring 容器的bean的5种方法的更多相关文章

  1. Java代码获取spring 容器的bean几种方式

    一.目的 写了一个项目,多个module,然后想在A模块中实现固定的config注入,当B模块引用A时候,能够直接填写相对应的配置信息就行了.但是遇到一个问题,B引用A时候,A的配置信息总是填充不了, ...

  2. 普通java类获取spring容器bean的方法

    很多时候,我们在普通的java类中需要获取spring的bean来做操作,比如,在线程中,我们需要操作数据库,直接通过spring的bean中构建的service就可以完成.无需自己写链接..有时候有 ...

  3. 获取Spring容器中Bean实例的工具类(Java泛型方法实现)

    在使用Spring做IoC容器的时候,有的类不方便直接注入bean,需要手动获得一个类型的bean. 因此,实现一个获得bean实例的工具类,就很有必要. 以前,写了一个根据bean的名称和类型获取b ...

  4. SpringBoot 之 普通类获取Spring容器中的bean

    [十]SpringBoot 之 普通类获取Spring容器中的bean   我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器 ...

  5. 普通Java类获取Spring的Bean的方法

    普通Java类获取Spring的Bean的方法 在SSH集成的前提下.某些情况我们需要在Action以外的类中来获得Spring所管理的Service对象. 之前我在网上找了好几好久都没有找到合适的方 ...

  6. [十]SpringBoot 之 普通类获取Spring容器中的bean

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,想直接使用 ...

  7. Spring Boot中普通类获取Spring容器中的Bean

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,自己动手n ...

  8. 普通java类加入spring容器的四种方式

    今天在自己开发的工具类中使用了spring注入的方式调用了其他类,但是发生的报错,在整理了后今天小结一下. 首先简单介绍下spring容器,spring容器是整个spring框架的核心,通常我们说的s ...

  9. Spring Ioc源码分析系列--容器实例化Bean的四种方法

    Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...

随机推荐

  1. node.js 安装express 提示 command is not found

    肯定有遇到这样的问题. 在执行express -e app时,出现 command is not found 此时需要执行 : $ npm install -g express-generator 你 ...

  2. global, $GLOBALS[]

    // global在函数中产生一个指向函数外部变量的别名变量,而不是真正的函数外部变量,一旦改变了别名的变量指向地址,就会发生一些意外的情况 $a = 10; function test() { gl ...

  3. 关闭一个winform窗体刷新另外一个

    例如Form1是你的主窗体,然后Form2是你的要关闭那个窗体,在Form1中SHOW FORM2的窗体那里加上一句f2.FormClosed += new FormClosedEventHandle ...

  4. 升级iOS10后SearchController焦点无法获取的问题

    原来在没升级之前,是这样获取的,好使 - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.sea ...

  5. 一个页面,多个flash(刚学jq插件)

    只贴js那部分哦 调用 // flash轮播图 var sumF=$('.btnTabs span').length/4; //有四个flash var flashT01=new flash($('. ...

  6. [PHP]MemCached高级缓存

    Memcache Win32 的安装下载:Memcache Win32 [www.php100.com]   [www.jehiah.cz/projects/memcached-win32/] 1.解 ...

  7. Java学习--Equals与“==”

    在Java规范中,它对equals()方法的使用必须要遵循如下几个规则: equals 方法在非空对象引用上实现相等关系: 1.自反性:对于任何非空引用值 x,x.equals(x) 都应返回 tru ...

  8. 【git】切换分支获取代码

    Welcome to Git (version 1.9.5-preview20150319) Run 'git help git' to display the help index.Run 'git ...

  9. ASP.NET MVC Razor视图(2)

    昨天介绍了一些Razor的基本语法,几天接着向下说: 补成一个,上次介绍了怎么输出原样的文本,用<text></text>标签,下面再介绍一种语法: @{@:我爱北京}  这个 ...

  10. Java琐记

    svn项目倒入,所选的文件夹一定是src上面以及的:然后eclipse会自动创建一个项目,项目名称就是src上级文件夹的名称:然后会按照路径下的文档结构如导入到eclipse的结构中: 被标记为// ...