如何从当前spring容器中获得bean?

在用spring做一个web项目,有一个需求是在没有servletContext的情况下,根据beanId获得当前容器中的具体bean对象。请问如何实现?注意,这个beanId可能是外部系统传过来的,所以无法用@autowire提前注入。
发现

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationCon;

但这个方法只能获得web容器,我想做的是获得当前的spring root container即无论当前是通过web还是非web加载的bean都能让我获得到。

如果不是web项目可以直接

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();

然后我每次都是中这个context去取bean,但如果是web项目,该怎么做呢?

回答: 如何从当前spring容器中获得bean?

  1. 写一个类,实现BeanFactoryAware接口,把该接口配置到spring中,然后把getbean方法写成静态的,就可以动态获取了。下面是示例:
    public class Springfactory implements BeanFactoryAware {
    
    	private static BeanFactory beanFactory;
    
    	// private static ApplicationContext context;
    
    	public void setBeanFactory(BeanFactory factory) throws BeansException {
    		this.beanFactory = factory;
    	}
    
    	/**
    	 * 根据beanName名字取得bean
    	 * 
    	 * @param beanName
    	 * @return
    	 */
    	public static <T> T getBean(String beanName) {
    		if (null != beanFactory) {
    			return (T) beanFactory.getBean(beanName);
    		}
    		return null;
    	}
    
    }
    

    使用的时候,通过Springfactory.getBean("beanName"),就可以获取到bean了。注意:这个是静态方法,直接通过类名去调用。

spring容器中能拥有两个同种类型的bean吗。我有两个dao类同时实现一个接口,这两个接口注入时报了异常。

  1. Q
    No unique bean of type [com.ncl.pes.dao.iface.business.EpmPositionDao] is defined。如果只注入其中一个没错,但如何能把两个同时注入呢???
  2. A
    我的是完全相同的两个类,就改了个类名。然后同时注入。
    @Repository
    public class EpmPositionSqlMapDao extends BaseSqlMapDao implements EpmPositionDao{另一个是
    @Component("test1")
    public class TestDao extends BaseSqlM……


    我测试过了,两个类实现同一个接口,在一个实现类加上@Component("test1"),一个加上@Component("test2")
    在引用的时候加上@Resource(name="test1")或者@Resource(name="test2"),就会指定用哪个实现类,不加resource注解才会出现上面那个错误

ejb容器中实体bean的调用问题

  1. Q
    实例:一个简单的Student实体bean,一个会话bean通过EntityManager持久化实体。以下为测试代码:
    Student stu=new Student();
    ManyToManyDAO sd=(ManyToManyDAO)ctx.lookup("***");
    System.out.println(stu.getId());
    sd.saveStu(stu);
    System.out.println(stu.getId());
    结果:两次输出结果均为null
    后来,调用stu.findStu查询Student,可以获取id
    求教:为什么感觉main中stu和远端stu是两个对象,发现main中stu状态是不会变的,不会有id传入,hibernate就是这样吗?分布式应用和本地应用中jpa有不同机理吗?
    如果能给以详细介绍,不胜感激。
  2. A
    stu = sd.saveStu(stu);

spring声明式事务,service层必须在ioc容器中声明?

  1. Q
    各位,小弟在学习spring+hibernate 声明式事务时,发现一个问题, 做了两种测试

    1. 建立一个普通的 java project
    x.y.services 包下有 applicationContext.xml, DefaultFooService.java, 在applicationContext.xml中对DefaultFooService进行声明式事务,并注入 DefaultFooService
    运行后成功实现事务管理

    2. 建立一个 web dynamic project
    在applicationContext.xml中对DefaultFooService进行声明式事务,但不注入 DefaultFooService,而是在 DefaultFooService.java中,


    这样的话,事务没有成功

    所以,请问 问题出在哪?
    我个人猜测是不是因为我没有使用 IoC容器管理 DefaultFooService,所以声明式事务无法实现,如果是这样,应该怎样进行事务管理?

    谢谢!
    问题补充:补充:
    public class DefaultFooService implements FooService {
       private static FooDaoHibernate fooDaoHibernate;
       private static RelationshipDaoHibernate relationshipDaoHibernate;
       private static ApplicationContext ctx = null;
       private static SessionFactory sessionFactory = null;

       ...
    }

    1. 建立一个普通的 java project, 在applicationContext.xml中,注入DefaultFooService,
    <bean id="fooService" class="x.y.service.DefaultFooService">
        <property name="fooDaoHibernate" ref="fooDao" />
        <property name="relationshipDaoHibernate" ref="relationshipDao" />
    </bean>
    <bean id="fooDao" class="x.y.service.FooDaoHibernate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <bean id="relationshipDao" class="x.y.service.RelationshipDaoHibernate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    2. 建立一个 web dynamic project,在applicationContext.xml中,没有上面的代码,而是如正文所述。
  2. A
    哥们,你这个我看是没有在你的applicationcontext中配置dao,而是在initmethod中直接instant的dao,所以你的这个dao不是由spring容器进行维护的,所以spring对它什么也做不了,自然就无法进行声明式事物控制了。你如果使用annotation注入的dao的话也是一样可以生效的,另外一个小建议就是如果使用@Transactional,最好放在service层。

spring是如何加载配置文件中的bean信息的?

  1. Q
    XML部分配置:

    <bean id="test" class="com.test.SpringTest">
      <list>
      <value>1</value>
      <value>2</value>  
      <value>3</value>
      </list>
    </bean>

    问题:

    1.配置文件加载中默认情况下它会执行StringTest中的哪个方法?是怎么执行的?

    2.比如在SpringTest类中有个getString(String key)方法,这个方法中的参数key是怎么来的?
    问题补充:OpenMind 写道getString(String key)? setString(String key)吧?
    可以自己指定生命周期方法:

    <bean id="test" class="com.test.SpringTest"
    init-method="start" destroy-method="stop">

    init-method会在所有属性设置完毕以后调用,destroy-method会在servlet容器关闭的时候调用。

    spring自己定义的bean加载的时候没有设置init-method属性,那么它是通过什么配置去类中找方法执行的呢?
  2. A
    Spring框架内的一些Bean是实现了某些生命周期接口的,Spring容器会在合适的时候调用这些方法,比如afterPropertiesSet方法,好像是这个名字

spring中的bean何时被实例化的

  1. Q
    今天在读spring技术内幕的时候,里面有一句话,在容器初始化完成以后,IoC容器的使用就准备好了,在客户第一次向IoC容器请求Bean时,IoC容器对相关的Bean依赖关系进行注入。当我deBug项目的时候,发现TOMCAT启动的阶段,就开始调用相关配置Bean的构造方法实例化,我想知道bean到底是在那个阶段实例化?
  2. A
    1、单例是在Spring IoC容器初始化时初始化,容器初始化完毕 Bean就被初始化了
         即默认是立即初始化 可以设置lazy="true" 来延迟初始化(即第一次访问时)

    2、原型 是获取时初始化

jvm实例,tomcat容器,spring容器,在内存中的关系

  1. Q
    1.一个java项目对应一个jvm 吗?
    2.tomcat里面加载多个java项目 ,是不是用了一个jvm?
    3.java项目中的spring容器,部署到tomcat容器,启动tomcat以后的jvm实例 ,在内存中是怎么一个包含关系?

    如果说 一个程序启动一个jvm实例,一个项目能保证在一个jvm里面吗
    另外 如果一个tomcat加载n各项目 他们之间彼此调用(比如通过http访问),是不是跨jvm?
    谢谢
    问题补充:csslisi 写道1、集群环境可能是多个jvm
    2、一个java进程就是一个jvm,main方法启动的,同一个tomcat的多个web应用都在一个jvm里
    3、jvm包含tomcat运行环境,tomcat加载了应用上下文,应用上下文加载spring运行环境
    4、一个tomcat的各个项目之间是独立的上下文环境,如果通过http访问,也相当于跨jvm,不是引用调用
    csslisi 写道1、集群环境可能是多个jvm
    2、一个java进程就是一个jvm,main方法启动的,同一个tomcat的多个web应用都在一个jvm里
    3、jvm包含tomcat运行环境,tomcat加载了应用上下文,应用上下文加载spring运行环境
    4、一个tomcat的各个项目之间是独立的上下文环境,如果通过http访问,也相当于跨jvm,不是引用调用

    谢谢你的回复
    我想问下既然多个应用在一个tomcat下的话,tomcat又在一个jvm里面
    是不是说 启动tomcat里面的每个项目只是用了线程?
    这里面的项目之间的通信是进程间通信 还是属于线程间通信呢?
    如果跨jvm肯定是进程间通信了吧 但你讲道 这些项目在同一个jvm里面 能在解释下吗
    问题补充:csslisi 写道应用跟线程没什么关系,线程是个计算执行的概念,而应用上下文占用的是内存。你如果打印线程名称,就能看到应用线程是动态分配的,线程1可以在应用1出现,也可以在应用2出现。其实Tomcat是JVM的main进程启动的一个Socket服务,同时会加载应用的上下文环境、初始化执行线程池

    项目之间的调用看你用什么方式,如果通过http访问,就相当于跨jvm;项目虽然都在一个虚拟机里,但属于不同的类加载器环境,除非定义SystemClassloader级别的静态变量,没想到有其他办法能引用调用,呵呵。
    tomcat线程之间不可以相互调用,除非是自定义的多线程。
    谢谢你的回复
    最后再多问一句啊
    两个项目部署在一个tomcat里面和两个项目分别部署在两个tomcat里面 两种情况
    这两个项目之间的数据传输(比如通过httpClient访问),都是跨jvm,是不是两种情况的效率完全一样呢?
  2. A
    应用跟线程没什么关系,线程是个计算执行的概念,而应用上下文占用的是内存。你如果打印线程名称,就能看到应用线程是动态分配的,线程1可以在应用1出现,也可以在应用2出现。其实Tomcat是JVM的main进程启动的一个Socket服务,同时会加载应用的上下文环境、初始化执行线程池

    项目之间的调用看你用什么方式,如果通过http访问,就相当于跨jvm;项目虽然都在一个虚拟机里,但属于不同的类加载器环境,除非定义SystemClassloader级别的静态变量,没想到有其他办法能引用调用,呵呵。
    tomcat线程之间不可以相互调用,除非是自定义的多线程。

spring 集成quazt 后如何实现等spring容器加载完成之后自动执行一次任务?

  1. Q
    我的项目中使用了spring集成quazt来实现任务调度任务的功能,现在有这样一个需求,我需要通过后台任务每隔半小时统计一次业务数据,然后放到缓存中,前台页面通过实时刷新页面来从缓存中获取统计的数据,可能,当我们系统刚上线的时候调度任务还没有执行,要等到指定的时间点才执行,我现要想让系统等到spring容器加载完成后就自动执行一次,请问有什么办法吗?

    <bean id="refreshOldAccountTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"
    		autowire="no">
    		<property name="jobDetail">
    			<ref bean="refreshOldAccountStaticTask" />
    		</property>
    		<!-- cron表达式 -->
    		<property name="cronExpression">
    		
    			<!--  设置的定时器,每30分钟执行一次 -->
    			<value>* 10/30 * * * ? * </value>  
    		</property>
    	</bean>
    
    
    <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  --> 
        <bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" 
         	lazy-init="false" autowire="no"> 
              <property name="triggers"> 
                  <list>
                     
                      
                      <ref bean="refreshOldAccountTrigger"/>
                    
                  </list>
                 
              </property> 
               <property name="autoStartup" value="true"/>
        </bean>
    
  2. A
    用SimpleTriggerBean即可

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> 
            <property name="jobDetail" ref="myJob" />  
            <property name="startDelay" value="开始时的延迟 如0" /> 
            <property name="repeatInterval" value="重复执行间隔" /> 
        </bean> 

Struts2和Spring整合中的Action是由谁创建的,有什么区别

  1. Q
    以下配置均未在Struts配置文件中加入<constant name="struts.objectFactory" value="spring" />

    情况说明如下:登录页面提交到Action中,Action在execute方法中调用ms对象中的valid方法进行验证,返回SUCCESS.

    在Action的构造方法中输出一行文字


    第一种情况:在Struts中配置Action,配置name,class配置成Spring中Action类的Bean ID,不指定调用方法。spring中配置Action Bean,class配置实际类名,并显示指定ms Bean的引用。这时Action由Sping来创建。构造方法输出两行

    第二种情况:在Struts中配置Action,配置name,class配置成实际类名。spring中只配置ms Bean的引用,进行自动装配。这时Action由Spring进行创建。构造方法输出一行

    第三种情况,在Struts中配置Action,配置name,class配置成实际类名,指定方法名不为默认的execute,为任意其他,例如test,这个方法调用ms对象中的valid方法进行验证。spring中配置Action Bean,class配置实际类名,并显示指定ms Bean的引用。这时也是可以成功的。构造方法输出一行

    第三种情况中,如果Struts中不指定方法,或者指定execute方法,均报错。虽然报错,但是控制台输出一行Action构造方法中的文字。

    想问一下第三种情况的Action由谁来创建,Struts创建和Spring来创建有什么区别
  2. A
    引用第三种情况,在Struts中配置Action,配置name,class配置成实际类名,指定方法名不为默认的execute,为任意其他,例如test,这个方法调用ms对象中的valid方法进行验证。spring中配置Action Bean,class配置实际类名,并显示指定ms Bean的引用。这时也是可以成功的。构造方法输出一行
    这种情况在集成spring的情况下其实是:
    SpringObjectFactory代码
        
    
        <!--  Make the Spring object factory the automatic default -->
        <constant name="struts.objectFactory" value="spring" />

    会自动注册如上一个bean(struts2中的bean),org.apache.struts2.spring.StrutsSpringObjectFactory就是一个struts2的ObjectFactory实现,并委托给spring(可以认为是 策略/适配器模式实现)

    而struts2默认的IoC容器实现是:
        <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
        <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />

请教关于spring mvc中使用json-lib-ext-spring返回json的问题

  1. Q
    我做了一个这样的框架:
    spring+hibernate+spring mvc

    想让spring mvc返回json数据,使用了json-lib-ext-spring1.0.2。

    刚弄完时是http://localhost/listPerson.do可以在页面上显示出json数据的,但是后来改了一些东西。然后再调用http://localhost/listPerson.do却不显示json数据了,而是提示下载listPerson.do。请教大家是怎么回事啊?

    代码如下:

    web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
        <display-name>Spring Annotation MVC Sample</display-name>
        <!--  Spring 服务层的配置文件 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
    
        <!--  Spring 容器启动监听器 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
        <servlet>
            <servlet-name>json</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>2</load-on-startup>
        </servlet>
        <servlet-mapping>
        	<servlet-name>json</servlet-name>
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
    </web-app>
    


    applicationContext.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"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
     	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     	http://www.springframework.org/schema/context
     	http://www.springframework.org/schema/context/spring-context-2.5.xsd
     	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
     	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
        <!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->
    	<!--
    		bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/
    	-->
    	<!--
    		<context:annotationconfig/> 将隐式地向 Spring 容器注册
    		AutowiredAnnotationBeanPostProcessor、
    		CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor
    		以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。
    		使用这个选项必须加入最上面的3、6、7项
    	-->
    	<context:annotation-config/>
    	<!--
    		<bean id="boss" class="net.sf.test.Boss"/>
    		<bean id="office" class="net.sf.test.Office"> <property name="no" value="001"/> </bean>
    		<bean id="car" class="net.sf.test.Car" scope="singleton">
    			<property name="brand" value=" 红旗 CA72"/>
    			<property name="price" value="2000"/>
    		</bean>
    	-->
    
    	<aop:config>
    		<aop:advisor
    			pointcut="execution(* net.sf.service.*.*(..))"
    			advice-ref="txAdvice" />
    	</aop:config>
    	<tx:advice id="txAdvice">
    		<tx:attributes>
    			<tx:method name="insert*" />
    			<tx:method name="update*" />
    			<tx:method name="*" propagation="REQUIRED"/>
    		</tx:attributes>
    	</tx:advice>
    	<!--bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /-->
    	<bean id="transactionManager"
    		class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory"
    			ref="entityManagerFactory" />
    	</bean>
    	<bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource"
    		destroy-method="close">
    		<property name="driver"><value>oracle.jdbc.driver.OracleDriver</value></property>
    		<property name="driverUrl"><value>jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.1)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = orcl)))</value></property>
    		<property name="user" value="test"></property>
    		<property name="password" value="test"></property>
    		<property name="alias" value="myblogdb"></property>
    		<!--property name="houseKeepingSleepTime"><value>9000</value></property-->
    		<property name="prototypeCount" value="5"></property>
    		<property name="maximumConnectionCount" value="100"></property>
    		<property name="minimumConnectionCount" value="10"></property>
    		<property name="trace" value="true"></property>
    		<property name="verbose" value="true"></property>
    	</bean>
    
    	<bean id="entityManagerFactory"
    		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="jpaVendorAdapter">
    			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    				<property name="database" value="ORACLE" />
    				<property name="generateDdl" value="true" />
    				<property name="showSql" value="true" />
    			</bean>
    		</property>
    	</bean>
    
    	<context:component-scan base-package="net.sf">
    		<!--context:include-filter type="aspectj" expression="edu.jlu.fuliang.util..*"/-->
    		<context:include-filter type="regex" expression="net\.sf\.service\..*"/>
    		<context:include-filter type="regex" expression="net\.sf\.test\..*"/>
    		<context:exclude-filter type="regex" expression="net\.sf\.action\..*"/>
    	</context:component-scan>
    
    </beans>
    


    json-servlet.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"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    
        <!-- ①:对web包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能-->
        <context:component-scan base-package="net.sf.action"/>
    
     	<!-- ②:启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
    
        <!--  ③:对模型视图名称的解析,即在模型视图名称添加前后缀 -->
    	<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"/>
    </beans>
    
    


    views.properties
    jsonView.(class)=net.sf.json.spring.web.servlet.view.JsonView
    jsonView.contentType=application/json;charset=UTF-8
    


    PersonAction.java
    package net.sf.action;
    
    import net.sf.service.PersonService;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class PersonAction {
    	@Autowired
        private PersonService _personService;
    
        @RequestMapping("/listPerson.do") // <—— ①
        public String listPerson(Model m) {
        	//_personService.getList();
        	m.addAttribute("personList", _personService.getList());
            System.out.println("call listAllBoard method.ss");
            return "jsonView";
        }
    }
    


    PersonServiceImpl.java
    package net.sf.service.impl;
    
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
    
    import net.sf.service.PersonService;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    @Scope("prototype")
    @Service("personService")
    public class PersonServiceImpl implements PersonService {
    	EntityManager em;
    
    	@PersistenceContext
    	public void setEm(EntityManager em) {
    		this.em = em;
    	}
    
    	public List getList(){
    		String sHql = "FROM Person";
    		Query query = em.createQuery(sHql);
    		List resultList = query.getResultList();
    		return resultList;
    	}
    	public String test(){
    		return "hello world, 雪狐!";
    	}
    }
    
  2. A
    我也遇到过这样的问题。。把json-lib-ext-spring的异常处理bean的bean配置项去掉就可以了。。