Spring手动获取指定Bean

起因

由于项目需要把一个项目的某个功能移植到另一个项目中,但是两个项目结构不同,新写的接口不是被Spring容器管理的,没办法在接口中使用@Autowired注解自动注入Serivice(Ps.我这里说的接口是对外接口,不是Java中的interface,实际上是一个类),只能尝试手动注入。在网上查了查最终尝试了三种方式,对于我的问题前两种手动注入bean的方式都无法获取到实例,有效的只有方法三,先列出方法,问题分析在后面。


方法

方法一:使用BeanFactory

BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");   
factory .getBean("beanName");  

方法二:使用ApplicationContext

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");   
ac.getBean("beanName");  

方法三:实现ApplicationContextAware接口

写一个工具类去实现ApplicationContextAware接口,保存ApplicationContext为静态实例供全局调用。

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;


public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
     */

    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        BeanUtil.applicationContext = applicationContext;
    }

    /**
     * 取得存储在静态变量中的ApplicationContext.
     */
    public static ApplicationContext getContext() {
        checkApplicationContext();
        return applicationContext;
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static  T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static  T getBean(Class clazz) {
        checkApplicationContext();
        System.out.println(clazz);
        return (T) applicationContext.getBeansOfType(clazz);
    }

    /**
     * 清除applicationContext静态变量.
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }

    private static void checkApplicationContext() {
        Assert.notNull(applicationContext,
                "applicationContext未注入,请在applicationContext.xml中定义BeanUtil");
    }
}

还需要在Spring的配置文件中application.xml中手动注册一下这个工具类


然后在需要的地方就可以直接调用

TestService testService = BeanUtil.getBean("testService");

问题分析

一开始在使用@Autowired注解无法注入之后,先后尝试了前两种方案,打断点发现service同样是null,没有成功获取到bean,多方尝试之后最终***仅有第三种方法成功***了。至于问题的成因有过几种猜测:

猜测一: 看到网上有人说由于自己把自动扫包的配置全部放在的SpringMVC的配置文件里,而Spring上下文访问不到SpringMVC的上下文(反之可以),所以get不到bean。
我看了看项目的配置文件,不大可能是这种情况。

猜测二: 在多线程环境下,由于web容器不能感知线程的启动,所以Spring无法向线程中注入。Ps.如果由Spring管理线程池,似乎是可以自动注入的,这部分暂时没有细看,以后再做补充吧。
但是因为不清楚新的项目我做的这部分是否涉及到多线程,无法确定是否该原因。

猜测三: 这部分类没有被Spring管理,所以在类中也无法由Spring去注入,或者采取前两种方案时,由于不在同一个线程中所以获取到的context不是最开始的context,而是new了一个新的。
后两种猜想在***本质上是有相似之处***的,因为不管是线程还是类,同样都不是由Spring容器去管理,而是在需要的地方去new。经过与人探讨,认为大概率是这种情况。


其他

在上面的解决办法中还有一些需要注意的小细节

  1. applicationContext获取bean的时候是可以通过byName或者byType的方式去获取到bean,在我们的工具类中可以看到分别调用了applicationContext的getBean和getBeansOfType方法,这里有一点需要注意,通过***byName***方式获取到的bean是***直接返回的bean的实例***,而***byType***方式则会***返回一个Map集合***,其中的key是bean的name,value则是bean的实例。我之前没注意到这一点,直接把byType方式的返回结果赋给了bean,结果出现了***类型不能转换***的错误。
  2. BeanFactory和ApplicationContext加载bean的时候有一点不太一样的地方,BeanFactory采用的是***延迟加载***的方式,第一次用到bean的时候才会加载,而ApplicationContext则是在容器启动的时候***一次性创建了所有的bean***。它们还有很多其他不同之处,可以参考Spring中ApplicationContext和beanfactory区别。
  3. 在手动加载bean的时候,需要注意关闭容器,否则容易造成数据库连接不能释放。推荐使用AbstractApplicationContext,使用完之后调用close方法释放资源
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
context.close();//释放资源

你可能感兴趣的:(各种问题)