- 浏览: 156485 次
文章分类
最新评论
-
飘零雪:
[b][/b][i][/i][u][/u]引用
自定义Mave archetype的创建 -
fujohnwang:
<div class="quote_title ...
基于iBatis的开源分布式数据访问层 -
gzenzen:
<pre name="code" c ...
基于iBatis的开源分布式数据访问层 -
fujohnwang:
bornwan 写道我就很想知道分布式数据源,水平切分之后排序 ...
基于iBatis的开源分布式数据访问层 -
fujohnwang:
gzenzen 写道什么时候支持mybatis3、spring ...
基于iBatis的开源分布式数据访问层
Table of Contents
前阵子“袜子 ”电话里随便聊了点儿有关在Spring里面如何扩展某些行为的话题, 其实, 这些话题本身没有什么技术含量, 完全是根据使用场景来权衡罢了, “袜子 ”心里肯定也已经有数了,不过,感觉就这两个话题来说说也挺好的, 因为跟阿九这阵子的路子有些吻合, 讲简单的东西,但一定要把简单的东西讲清楚, 讲架构当然更能吸引眼球,但我一直认为“The problem is not the design, it's the implementation. ”, 所以, 我还是愿意说些很简单,很基本的东西.
在现有Spring框架的默认支持下,我们可以注入单独声明的Enum类型的依赖关系, 例如:
public enum FixtureEnum { FIXTURE_ONE, FIXTURE_TWO; } public class Sample{ private FixtureEnum fOne; ... } <bean id="target" class="...Sample"> <property name="fOne" value="FIXTURE_ONE"/> </bean>
我们也可以注入以String或者复杂对象类型作为key的Map:
<bean id="target" class="..."> <property name="mapping"> <map> <entry key-ref="complexObject" value="anything"/> <entry key="stringvalue" value-ref="..."/> </map> </property> </bean> <bean id="complexObject" class="..."> </bean>
可是,把这两个结合起来, 我们要注入以Enum作为Key的Map的话,可能默认的支持就帮不了什么大忙了, 如果我们声明一个Map依赖对象, 但它的Key是Enum类型的话:
public class InjectionTarget { @EnumKeyType(FixtureEnum.class) private Map<FixtureEnum,String> mapping; ... // setters or getters and other things }
如果单单简单的定义依赖注入关系如下:
<bean id="target" class="...InjectionTarget"> <property name="mapping"> <map> <entry key="FIXTURE_TWO" value="FIXTURE TWO"/> </map> </property> </bean>
恐怕最终得到的不是一个Map<FixtureEnum,String>类型的Map,而是一个Map<String,String>类型的Map, 小沈阳语:为什么那?
Java5的Generic是Erase-Based, 这就意味着,运行期间无法获得Map的Key相关的Generic类型信息, 那么, Spring在做注入的时候,也就没法知道应该将String形式表达的依赖对象转换成什么类型, 只好保持原样啦, 所以,以通常形式表达的map注入,最终得到的就成了一个Map<String,..>类型的Map,而不是Map<Enum,..>类型的Map.那谁可能说了,那怎么其它复杂对象作为Key怎么没问题那? 原因很简单嘛, 你直接指定了对象的引用嘛,不服,你把对象类型直接写上试试?
那怎么解决这个问题那? 显然我们无法在运行期间通过反射之类的途径来获得Map的Key类型了,那么,我们就明确指定呗,如何明确指定那?可以考虑几种方式...
我们可以自定义一个FactoryBean来“生产 ”以Enum类型作为Key的Map,通过该自定义FactoryBean的某个Property类指定Key的Enum类型是什么, 就可以在“生产 ”过程中生成或者转换出相应的Map实例, Spring默认提供了一个MapFactoryBean,我们可以在这个父类的基础上做进一步的工作,说白了,就是直接根据明确指定的Enum类型将已经注入的Key值做一下转换, 之后,Map的Key就从String变成了指定的Enum类型, 一个实例代码可以实现如下:
public class EnumKeyMapFactoryBean extends MapFactoryBean { private Class<? extends Enum<?>> enumType; private EnumKeyConversionSupport conversionSupport = new EnumKeyConversionSupport(); @Override protected Object createInstance() { return conversionSupport.convert(super.createInstance(), enumType); } public void setEnumType(Class<? extends Enum<?>> enumType) { this.enumType = enumType; } public Class<? extends Enum<?>> getEnumType() { return enumType; } }
super.createInstance()返回的是最初的Map实例,我们通过EnumKeyConversionSupport这个类和明确指定的Enum类型进行一下转换, 就可以获得最终想要的Map实例了. EnumKeyMapFactoryBean的适用看起来如下:
<bean id="ekMap" class="cn.spring21.sandbox.springext.EnumKeyMapFactoryBean"> <property name="enumType" value="cn.spring21.sandbox.springext.FixtureEnum"/> <property name="sourceMap"> <map> <entry key="FIXTURE_ONE" value="anything"/> <entry key="FIXTURE_TWO" value="anything"/> </map> </property> </bean>
ekMap现在的Key就是我们最终想要的FixtureEnum类型.
Tip |
如果感觉上面的配置方式很繁琐,可以考虑自定义XML Schema类简化配置,类似于spring的util命名空间提供的简化配置形式. |
自定义FactoryBean的形式当然可以达成目的,不过, 使用上来看,可能不是太方便,毕竟,每次遇到这样的情况都需要声明那么一个FactoryBean的bean定义, 而且,配置的形式也不是那么简洁,本着“精益求精 ”的精神,我们是不是可以想一下,还可以有更好的方法那?
要明确指定Map的Key的类型是Enum类型,不一定非要通过XML配置的形式,我们还可以使用Annotation,通过为相应的Map标注某一表明了Key的Enum类型的Annotation, 我们同样可以获得Key的Enum类型信息,例如,我们可以声明某一Annotation如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface EnumKeyType { Class<?> value(); }
然后在遇到适用Enum作为Key的Map的情况下,就可以通过这一Annotation对这样的Map进行标注:
public class InjectionTarget { @EnumKeyType(FixtureEnum.class) private Map<FixtureEnum,String> mapping; ... }
这样,虽然我们无法在运行期间获得Map的Key的Generic类型信息,但可以通过Annotation来获得,不过, 光标注一下,Spring可不会聪明到马上知道你标注这么个Annotation要干嘛,我们得写点儿东西让Spring知道遇到这个 Annotation该干点儿什么事情, 所以,可以定义一个BeanPostProcessor来做这个事情,例如:
public class EnumKeyMapBeanPostProcessor implements BeanPostProcessor { protected static final transient Logger logger = LoggerFactory.getLogger(EnumKeyMapBeanPostProcessor.class); private EnumKeyConversionSupport conversionSupport = new EnumKeyConversionSupport(); @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Field[] fields = bean.getClass().getDeclaredFields(); for(Field field:fields) { if(field.isAnnotationPresent(EnumKeyType.class)) { try { convertKeyType(field,bean); } catch (Exception e) { logger.warn("failed to do map key convert.\n{}",e); } } } return bean; } protected void convertKeyType(Field field,Object bean) throws Exception { EnumKeyType eType= field.getAnnotation(EnumKeyType.class); Class<?> clazz = eType.value(); field.setAccessible(true); Object map = field.get(bean); if(Map.class.isAssignableFrom(map.getClass()) && clazz != null) { Map<Object, Object> result = conversionSupport.convert(map, clazz); field.set(bean, result); } } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } }
只要将这个EnumKeyMapBeanPostProcessor注册到Spring的ApplicationContext, 那么,之后要注入以Enum作为Key的Map的时候,只要简单的使用EnumKeyType标注一下这些Map就可以了,一劳多得, 如果应用中有多处需要这样的Map注入,使用这种方式显然要比适用自定义的FactoryBean要省事不少.
默认情况下,我们可以通过Spring的XML配置文件中的<list>或者<set>等元素为某一个对象注入一组依赖对象,只要我们能够确定容器中的哪些bean定义应该纳入这组依赖对象就行,例如:
public class InjectionTarget { private List<T> collection; ... }
<bean id="it" class="...InjectionTarget"> <property name="collection"> <list> <ref bean="t1"/> <ref bean="t2"/> ... </list> </property> </bean> <bean id="t1" class="..."/> <bean id="t2" class="..."/> ...
可是,大部分情况下,list里面都是同一类型的依赖对象(你要混合元素类型,那是你的事情),每次添加一个这样类型的依赖对象,就需要配置文件里添加一 个bean定义,然后<list>处改一下,很是烦躁,是吧? 我们可以通过某些方式来简化这种场景下的配置或者消除它,例如...
我们可以自定义一个FactoryBean,让它替我们自动去容器里查找指定类型的一组依赖对象,然后,我们只要把这个FactoryBean挂接到依赖这组依赖对象的bean定义上就行了. 要让自定义的FactoryBean能够查找容器中指定类型的对象,我们可以让它实现ApplicationContextAware接口(这个接口能做啥事儿我就不多说了):
public class CollectionInjectionFactoryBean implements FactoryBean,ApplicationContextAware { private ApplicationContext applicationContext; private Class<?> componentType; @Override public Object getObject() throws Exception { @SuppressWarnings("unchecked") Map<String,Object> map = this.applicationContext.getBeansOfType(getComponentType()); if(map == null || map.isEmpty()) { return Collections.EMPTY_LIST; } return map.values(); } @SuppressWarnings("unchecked") @Override public Class getObjectType() { return Collection.class; } @Override public boolean isSingleton() { return false; } @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.applicationContext = arg0; } public void setComponentType(Class<?> componentType) { this.componentType = componentType; } public Class<?> getComponentType() { return componentType; } }
有了它之后,如果你想为某个对象注入一族A类型的依赖对象,那么就定义一个CollectionInjectionFactoryBean,并指定它的componentType为A; 如果想注入一族B类型的依赖对象,就指定它的componentType为B,依此类推.例如:
<bean id="target" ..> <property name=".." ref="collectionInjectionFB"/> </bean> <bean id="collectionInjectionFB" class="cn.spring21.sandbox.springext.CollectionInjectionFactoryBean"> <property name="componentType" value="...AType"/> </bean>
如果应用里这种场景不多,那使用这种自定义FactoryBean的方式还可以将就一下,但多的话,那也依然减少不了多少配置,这个时候,可以考虑下面这种方式.
如果可能,开发人员肯定不愿在java代码与配置文件之间切换,最好是只关注Java代码文件,这也就是为啥Annotation很受开发人员欢迎的原因之一. 所以,如果某个对象的属性需要注入一组依赖对象,那么,最好的方式就是直接在Java代码中直接标注这种依赖关系,鉴于此,我们定义一用于此目的的Annotation如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface InjectCollectionOf { Class<?> value(); }
有了该Annotation之后,我们就可以在对象中需要注入一组依赖对象的Property处标注该Annotation:
public class InjectionTarget { @InjectCollectionOf(SomeType.class) private Collection<SomeType> collection; ... }
为了让容器按照我们的旨意行事,我们最后需要提供一个自定义的BeanPostProcessor实现,如下所示:
public class CollectionInjectionBeanPostProcessor implements BeanPostProcessor,ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(CollectionInjectionBeanPostProcessor.class); private ApplicationContext applicationContext; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Field[] fields = bean.getClass().getDeclaredFields(); for(Field field:fields) { if(field.isAnnotationPresent(InjectCollectionOf.class)) { Class<?> componentType = field.getAnnotation(InjectCollectionOf.class).value(); if(componentType == null) { continue; } @SuppressWarnings("unchecked") Map<String,Object> componentCandidates = this.applicationContext.getBeansOfType(componentType); if(componentCandidates != null && !componentCandidates.isEmpty()){ field.setAccessible(true); try { field.set(bean,componentCandidates.values()); } catch (IllegalArgumentException e) { logger.warn("argument is not a collection.\n{}",ExceptionUtils.getFullStackTrace(e)); } catch (IllegalAccessException e) { logger.warn(ExceptionUtils.getFullStackTrace(e)); } } } } return bean; } @Override public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException { return arg0; } @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.applicationContext = arg0; } }
实现原理上跟自定义的FactoryBean差不多,无非就是多了Annotation检测相关逻辑, 最后,只要将这个自定义的BeanPostProcessor注册到容器, 所有标注了@InjectCollectionOf的Property就可以被正确的注入了:
<bean id="target" class="..InjectionTarget"> ... </bean> <bean class="...CollectionInjectionBeanPostProcessor"/>
如果需要针对Collection的明确子类型的类似注入需求, 依葫芦画瓢就可以了.
发表评论
-
基于iBatis的开源分布式数据访问层
2011-03-28 11:46 5536http://code.alibabatech.com/wik ... -
分布式数据访问与同步场景浅析
2010-09-06 19:50 2238分布式数据访 ... -
Netty Framework Tips And Gotchas
2010-08-11 18:01 2664王福强(Darren.W ... -
有关Maven编译DeprecatedAPI失败的问题
2010-08-02 10:59 4468在项目代码里用了sun.misc.Signal ... -
Java Daemon Control
2010-07-27 17:50 2904Java Daemon Control ... -
Event Driven Style API Design Instead of Old Procedure Style Ones
2010-07-12 19:53 1472王福强(Darren.Wang) <f ... -
HA狭义与广义论
2010-07-09 09:25 1462Author: Darren Wang(fujohnwang) ... -
Why We Need A Global ID Generator?!
2010-05-18 13:01 1623Table of Contents 1. Pai ... -
Gotchas With JUnit's Execution Model
2010-03-26 09:22 1052Maybe you have known it before, ... -
Transaction Management Patterns In Brief
2010-02-09 10:27 1762There are several patte ... -
框架API设计相关的碎言
2009-11-17 09:32 1610框架的API设计,应该是 ... -
自定义Mave archetype的创建
2009-10-29 20:12 12351Table of Contents ... -
看来有人已经有要抢先推出这个节目的意思了
2009-10-27 19:29 1017这篇blog对java, clojure和scala中的并发处 ... -
Roma Documentation Outline
2009-10-27 17:35 150Roma Docume ... -
Hot Stuff - Lombok
2009-10-22 19:46 1027give it a try, it's really cool ... -
ROMA框架潜在改进点思考(Thinking in ROMA improvements)
2009-10-21 19:53 1931. 关于ROMA现有表单 ... -
Valang Validator under the hood
2009-10-19 13:29 1658Table of Contents 1. Va ... -
ThreadSafety, Non-ThreadSafety 与 Stateless, Stateful有必然的对应关系吗?
2009-10-09 09:11 1859“It depends. ” 我们 ... -
A Big Piture On Concurrency
2009-09-12 09:49 12383- Concurrency Share (Concur ... -
尴尬的COC
2009-08-25 11:04 994Convention Over Configurati ...
相关推荐
在本例“Spring Ioc(依赖注入)入门例子--属性注入”中,我们将关注如何通过属性注入来管理依赖。 属性注入是指Spring容器将一个对象的属性值设置为另一个对象的引用。这通常用于将服务对象注入到需要使用它们的...
5. `@Autowired`:这是实现依赖注入的关键注解,Spring容器会自动找到类型匹配的Bean来注入。如果存在多个候选Bean,可以使用`@Qualifier`指定特定的Bean。 6. `@Value`:这个注解用于注入基本类型的值或从属性文件...
本文将深入探讨Spring的核心概念——依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect Oriented Programming,简称AOP),并基于提供的"TestSpring"文件进行讲解。 首先,让我们从Spring的依赖注入...
1. Spring依赖注入 使用XML和注解两种方式管理Bean,提供灵活的配置选项。 支持单例和多例模式,满足不同场景的需求。 2. MyBatis Plus 提供强大的ORM功能,简化数据库操作。 封装了通用的增删改查操作,提高...
Spring的核心特性包括依赖注入(Dependency Injection, DI)与面向切面编程(Aspect-Oriented Programming, AOP),这些特性使得Spring能够有效地管理和组织软件组件。 #### 二、Spring的核心概念 - **控制反转...
Spring框架是Java企业级应用开发中极为重要的一环,它提供了一个全面的编程和配置模型,用于现代Java基础结构,例如:依赖注入(DI)、面向切面编程(AOP)、事务管理等。Spring3作为Spring框架的一个重要版本,在...
Spring框架是Java语言中最为流行的应用程序开发框架之一,它的核心功能之一是IoC(控制反转)容器,用于实现依赖注入和控制反转的设计模式。本篇文档将对Spring框架的核心源码进行解析,以帮助开发者更深入地理解...
Spring的核心特性包括依赖注入(DI)和面向切面编程(AOP),这两个特性在我们的转账功能中扮演了重要角色。 依赖注入允许我们解耦组件,通过容器管理对象的生命周期和依赖关系。在本例中,我们可以创建一个`...
在Java Web开发中,这两个框架经常一起使用,Spring 提供了依赖注入(DI)和面向切面编程(AOP)等功能,而 Struts 2 则是一个强大的MVC(Model-View-Controller)框架,负责处理用户请求和业务逻辑。 Struts 2 和 ...
Struts2和Spring是两个非常重要的Java开源框架,它们分别在MVC(Model-View-Controller)架构和依赖注入(Dependency Injection,DI)方面发挥着关键作用。将这两个框架整合在一起,可以构建出高效、可维护的企业级...
3. 依赖注入:在实例化Bean的过程中,容器会根据定义的依赖关系,将其他Bean注入到当前Bean中,实现依赖关系的自动装配。 4. 初始化处理:如果Bean定义了初始化方法,容器会在所有依赖注入完成后调用该方法,完成...
Struts1.3是一款基于MVC(Model-View-Controller)设计模式的框架,主要用于控制应用程序的流程,而Spring2.5则是一个全面的轻量级应用框架,提供了依赖注入(DI)和面向切面编程(AOP)等功能,以及对其他框架的...
自动装配(autowire)是Spring提供的一种简化依赖注入的方式,分为byType和byname两种模式。byType会根据类型匹配Bean进行注入,byname则根据Bean的名称进行匹配。默认命名空间下,还有`beans`用于指定环境、`alias`...
Spring框架是Java开发中不可或缺的一部分,它以其强大的依赖注入(DI)和面向切面编程(AOP)功能而闻名。本文将深入探讨Spring的核心概念、关键特性以及如何在实际项目中应用它们。 首先,让我们从Spring的核心...
整合的主要目的是利用 Spring 提供的依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)特性,来管理和控制 Struts2 中的 Action 类以及相关业务逻辑。 首先,我们来看一下...
在这个主题下,我们将深入理解Spring如何通过注解来实现依赖注入,简化Java应用的开发。 **Spring IoC** IoC是Spring框架的核心特性,它反转了传统的对象创建和管理流程。在传统编程中,对象通常会自行创建依赖的...
在Spring框架中,`@Resource`和`@Component`是两个重要的注解,它们用于不同的目的,但都与依赖注入(Dependency Injection,简称DI)息息相关。理解这两个注解的使用和区别是掌握Spring框架核心概念的关键。 首先...
Spring框架则以其灵活的依赖注入、AOP(面向切面编程)以及强大的事务管理等特性,成为Java应用开发的首选。本实例将详细介绍如何利用CXF 2.7.3和Spring 3.0.7进行服务整合。 首先,我们需要了解CXF的核心功能。CXF...