普通Java类获取spring 容器的bean的5种方法

普通Java类获取spring 容器的bean的5种方法

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

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

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

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

说明:这种方式适用于采用Spring框架的独立应用程序,须要程序经过配置文件手工初始化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 对象注入。session

 

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

 

方法五:实现接口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的做用范围

1、 问题描述

 

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

 


 

2、 解决方案

       对于上面的问题Spring提供了三种解决方案:

  • 放弃控制反转。

           经过实现ApplicationContextAware接口让bean A可以感知bean 容器,而且在须要的时候经过使用getBean("B")方式向容器请求一个新的bean B实例。

  • Lookup方法注入。

            Lookup方法注入利用了容器的覆盖受容器管理的bean方法的能力,从而返回指定名字的bean实例。

  • 自定义方法的替代方案。

            该注入能使用bean的另外一个方法实现去替换自定义的方法。

 

3、 实现案例

 

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的地址不同,说明实现了。



    <lookup-method>标签中的name属性就是commandManager Bean的获取Command实例(AsyncCommand)的方法,也就createCommand方法,bean属性是要返回哪一种类型的Command的,这里是AsyncCommand。



    <public|protected> [abstract] <return-type> theMethodName(no-arguments)  
                  被注入方法不必定是抽象的,若是被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。不然,动态生成的子类会覆盖类里的具体方法。
    为了让这个动态子类得以正常工做,须要把CGLIB的jar文件放在classpath里,这就是咱们引用cglib包的缘由。
    Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的。
    Lookup方法注入干净整洁,易于扩展,更符合Ioc规则,因此尽可能采用这种方式。


    4、 原理分析(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技术,你们之后遇到的时候能够搜一下!)



    5、 新的扩展(注解方式)

  自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.   
             就是说让方法异步执行。
相关文章
相关标签/搜索