小编也是由于一些功能需求需要在filter中获取spring bean但是遇到了一些问题,在此与大家分享一下
首先我在我的项目中配置了applicationContext.xml与dispatcher-servlet.xml,applicationContext.xml是针对全局配置,dispatcher-servlet.xml针对spring mvc做配置,但是我只在applicationContext.xml中配置了包的扫描。
<context:component-scan base-package="znck.spring.*"/><!-- 注解支持,添加要扫描的包 -->
要获取spring的bean首先我先说一下spring的BeanFactory,spring中bean是有BeanFactory创建管理的,ApplicationContext则是继承了BeanFactory,使得我们可以通过xml配置的方式来实现bean的管理并且ApplicationContext实现了在spring初始化时实例化的Bean,而BeanFactory则是在第一次访问时实例化Bean。ApplicationContext与BeanFactory均有一个重要的方法getBean()用以获取spring bean。那么我们解决如何获取ApplicationContext就可以解决如何获取spring bean的问题了,为此spring提供了一个机制ApplicationContextAware接口spring bean实现该接口就可以获取到ApplicationContext我实现如下:
package znck.spring.serviceImp.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
// TODO Auto-generated method stub
SpringContextHolder.applicationContext = arg0;
}
/**
* 从静态变量applicationContext中得到Bean, 自动转型为所赋值对象的类型.
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 从静态变量applicationContext中得到Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
return getApplicationContext().getBean(requiredType);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
另外注意一定要将该实现类注册为spring bean组件在xml配置文件中或者使用@Component注解(注解时一定要扫描该包),当ApplicationContext初始化bean时会自动扫描实现了ApplicationContextAware接口的类,并调用setApplicationContext(ApplicationContext arg0)方法将自己本身传递给该实例(另外重复扫描会导致第二次扫描重新传入新的ApplicationContext,可以使用applicationContext.getId();尝试),并且将ApplicationContext对象存储在static对象中,使得其他对象可以直接访问ApplicationContext以获取spring bean。基本获取spring bean的方式就是如此。
愚蠢的小编总是遇到各种别的麻烦,我在文章开头说了我项目中配置applicationContext.xml和dispatcher-servlet.xml并且没有在dispatcher-servlet.xml添加注解扫描,其实applicationContext.xml配置的是spring IOC容器,dispatcher-servlet.xml配置的是springmvc IOC容器,springmvc是spring的子容器,spring中子容器可以访问父容器中的bean但是父容器不能访问子容器中的bean,另外SpringAOP代理也会导致获取bean失败,如果你使用getBean(Class<T> class);方法获取容器中的bean的话,比如你给service层添加了@Transactional注解的话,由于@Transactional注解时采用AOP代理,实际上容器中的相应的bean是代理类所生成的实例,所以你通过被代理类的类型来获取bean会失败报org.springframework.beans.factory.NoSuchBeanDefinitionException错误,只能通过getBean(String beanName);方法来获取bean,并且如果是通过接口代理方式的话,那么强制转化时只能转化为对应的接口类型。这是因为无论是原类还是代理类都继承了相应的接口,相关请自行查看JDK动态代理。我总结了一下关于获取bean问题的一些错误如下:
(1)使用了AOP代理,还使用getBean(Class<T> class);方法获取容器中的bean。
UserService userServiceImp = (UserService) SpringContextHolder.getBean("userServiceImp");
SystemService systemServiceImp = (SystemService) SpringContextHolder.getBean("systemServiceImp");
(2)在applicationContext.xml和dispatcher-servlet.xml中都配置了扫描相同的包
这种做法会导致SpringMVC配置覆盖spring配置,导致部分功能失效,比如你在spring中配置了事物管理机制,并扫描了相关@Transactional注解的包,又在springmvc中也扫描的相关的包,但是却没有在springmvc中配置事物管理,会导致springmvc在IOC容器中生成相同的bean,即把spring响应的bean覆盖掉了,但是springmvc生成的bean是没有织入事物管理机制的,于是会导致事物管理不可以用,反映报错即为事物无法获得。
另外除了以上方式还有别的方式可以获取spring bean特别是在web项目中spring提供了WebApplicationContext类,专门用于在web程序中获取spring bean。WebApplicationContext是继承了ApplicationContext类,并且spring初始化时将其作为属性放到了servletContext中,是web程序可以通过servletContext获取到WebApplicationContext进而获取spring bean,属性key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,使用方式如下
//两种方式作用一致,此为web方式
wac = (WebApplicationContext) fConfig.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//wac = WebApplicationContextUtils.getWebApplicationContext(fConfig.getServletContext());
wac.getBean(UserServiceImp.class);
相关链接如下