两个非常实用的获取 Spring Bean 的 Struts Action 基类方法

我们在实际的 Spring+Struts 的项目中一般都会定义一个 Struts Action 基类。基类中当然是提供一些公共的方法了,用上了 Spring 的话,里面放几个 getBean() 方法就会方便许多。Web 项目多数时候我想还是在 Action 中有经常性的获取 Bean 实例,如果有用在别处的话,也可以借鉴。

在没有泛型支持下,我们从 Spring Context 中获取 Bean 要强型转换类型,如:

SomeClass some = (SomeClass)context.getBean("someClass");

上面对应的方法原型是 Object getBean(String beanName);

从 jdk 1.5 开始有泛型了,可以加一个泛型,省却了转型步骤,看起来要舒服一些,如:

SomeClass some = getBean("someClass", SomeClass.class);

上面对应的方法原型是 <T> T getBean(String beanName, Class<T> requiredType);

再进一步,像 Struts2、Rails、Grails 那样我们讲求约定优于配置,假如我们在 Spring 配置文件中声明 Bean 时约定用首字母小写的短类名(如 SomeClass 类定义 BeanName 为 someClass) 的话,我们同样只需一个参数就可得到我们想要的 Bean 实例。这时候获取 Bean 实例的方式就如下:

SomeClass some = getBean(SomeClass.class);

上面对应的方未能原型是 <T> T getBean(Class<T> requiredType);

是不让代码更好写了,不用像很多项目中把所有的 Spring 的 BeanName 全定义成常量值,可以保证不会写错了。而且这也很通用,完全有理由在书写 Spring 配置时这样规范 Bean Name 定义,好像也没有太多的理由让一个 Class 在 Spring 中声明两次。而且 Spring 的注解方式也是这么取名的,见类:org.springframework.context.annotation.AnnotationBeanNameGenerator, 给类加个注解 @Component、@Controller、@Repository 或 @Service 时默认就这么取名的。

所以综合起来,我们在 Struts(1 或 2) 的基类 Action 中可以写这么两个实用的 getBean() 方法,完整代码如下:

注释中 /* <M> */ 和 /* implements ModelDriven<M> */ 是 Action 的 ModelDriven 泛型写法。如果来得精悍一点你尽可以只保留第二个方法,需稍加改动,且当然要保证它的前提就是了,即规范的 BeanName。

前面获得 BeanName 的代码你完全可以自己来写,这里两行:

        String shortClassName = ClassUtils.getShortName(requiredType);
        String beanName = Introspector.decapitalize(shortClassName);

是借用自:org.springframework.context.annotation.AnnotationBeanNameGenerator 的 String buildDefaultBeanName(BeanDefinition definition) 方法里的代码。

改造完原来的基类 Action 之后,忽然又觉得,Spring 本身该有提供 <T> T getBean(Class<T> requiredType) 这个方法的吧。翻了下 API, 果如其然。那就是:org.springframework.beans.factory.BeanFactory 接口中就定义了该方法:

 <T> T getBean(Class<T> requiredType) throws BeansException;

那就应该在各种 BeanFactory 的某个实现类中有相应的 getBean(requiredType) 方法实现。我们继续找到一个实现来看看:

org.springframework.beans.factory.support.DefaultListableBeanFactory 类中有:

它的实现原理大概也看出来了,先从 Spring 配置中遍历出所有类型相符的 BeanName,然后定位到确定的 BeanName,再调用一次

<T> T getBean(String beanName, Class<T> requiredType);

方法,显然效率上没有完全依照约定直接通过 BeanName 获取 Bean 实例的效率高

,还是继续走自己的路吧。

但是:有一个问题要提请注意一下了,前面我在 Spring 中定义的 Bean 类未使用接口,对于面向接口的编程就更要保持良好的习惯了。比如 Spring 里这样的配置:

<bean id="userService" autowire="byName"/>

正常获取该 Bean 是:

UserService userService = getBean("userService", UserService.class);

如果按照上面的办法还是:

UserService userService = getBean(UserService.class);

没问题,因为依据传入的 UserService.class,依然可以用同样的规则得到 BeanName 是 userService。如果写成:

UserService userService = getBean(UserServiceImpl.class);

就爱莫能助了,不存在 userServiceImpl 这样的 BeanName,你要是使用 Spring BeanFactory 的

<T> T getBean(Class<T> requiredType)

比如 beanFactory.getBean(UserServiceImpl.class) 没问题,可以得到你想要的 Bean,因为它是判断类型的,子类型当然成立的。但是为何要写成 getBean(UserServiceImpl.class); 呢,就像回到从前也不会写成:

UserService userService = (UserServiceImpl)getBean("userService");

一样,这等于是把接口与实现绑定了。所以再次论证前面定义的那两个 getBean() 方法更强的通用性。

本文链接 https://yanbin.blog/two-useful-getbean/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments