don't repeat yourself -约定优于配置(spring领域约定)
小弟刚学习spring,发现一个事情就是配置xml相当麻烦,而且有很多地方都是重复性的工作,于是本着DRY,约定优于配置,越简单越好和闲来没事干的精神对spring做了一点小的的学习,由于spring刚学,很多做得不对的,还望指出。
约定,现在我们假设约定如下:

只要在dao包下面,且其超类实现了Dao接口的类 我们就认为他是dao类
只要在service包下面,且其超类实现了service接口的类,我们就认为他是service类
只要在operation包下面,且其超类实现了operation接口的类,我们就认为他是operation类。
所有的entity类都放在entity包下面。
有了上面的约定在根据服务调用关系operation-service-dao,我们就进行动态的注入,而不是每次都要写同样的配置代码了。
实现思想:自定义一个BeanDefinitionParser类似于component-scan,并根据以上规则扫描和注入组件。
代码:
重写org.springframework.context.config.ContextNamespaceHandler增加一个解析器。
public class ContextNamespaceHandler extends NamespaceHandlerSupport{public void init(){ ......... registerBeanDefinitionParser("wangscu-component-scan", new SimpleComponentScanBeanDefinitionParser());}} SimpleComponentScanBeanDefinitionParser所做的工作就是将其base-package属性的包里面的所有资源交给SimpleBeanDefinitionScanner做解析,然后将SimpleBeanDefinitionScanner解析出来的BeanDefinition注入到该Context里面。
SimpleComponentScanBeanDefinitionParser.java 片段
@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext){String[] packages = StringUtils.tokenizeToStringArray(element.getAttribute(基包名称),ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);SimpleBeanDefinitionScanner scanner = configureScanner(element, parserContext);Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(packages);registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;}前面所提到SimpleBeanDefinitionScanner的功能就是扫描所有包下面的资源了,然后将获得资源要交给SimpleTypeFilter掉后的资源来定制BeanDefinition。
SimpleBeanDefinitionScanner.java 片段
private void doServiceInjection(ScannedGenericBeanDefinition scannedGenericBeanDefinition, String basePackage){AnnotationMetadata meta = scannedGenericBeanDefinition.getMetadata();String className = meta.getClassName();try{Class<? extends Service> service = (Class<? extends Service>) getClass().getClassLoader().loadClass(className);Field[] fields = service.getDeclaredFields();for (Field field : fields){Class<? extends Object> dao = field.getType();if (dao.getName().startsWith(basePackage + ".server.dao.")){String name = SimpleBeanNameGenerator.getDaoName(dao);Object val = parsePropertyValue(scannedGenericBeanDefinition, field.getName(), name);PropertyValue pv = new PropertyValue(field.getName(), val);scannedGenericBeanDefinition.getPropertyValues().addPropertyValue(pv);break;}}}catch (Exception e){}}剩下的就还有一个SimpleTypeFilter,SimpleBeanNameGenerator了,SimpleTypeFilter就是将上面提到的3种对象include出来交给scanner。
代码如下
@Overrideprotected Boolean matchSuperClass(String superClassName){if (superClassName.startsWith("java.")) { return null; }try{Class<? extends Object> clazz = getClass().getClassLoader().loadClass(superClassName);Class<?>[] array = clazz.getInterfaces();for (Class<?> clazz1 : array){if (clazz1 == SimpleComponentScanBeanDefinitionParser.DAO){return Boolean.TRUE;}else if (clazz1 == SimpleComponentScanBeanDefinitionParser.SERVICE){return Boolean.TRUE;}else if (clazz1 == SimpleComponentScanBeanDefinitionParser.OPERATION) { return Boolean.TRUE; }}}catch (Exception e){}return null;}SimpleBeanNameGenerator.java就是一个组件名字生成器,方便与查找和注册BeanDefinition。
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry){if (definition instanceof GenericBeanDefinition){try{String beanName = definition.getBeanClassName();Class<? extends Object> beanClass;beanClass = getClass().getClassLoader().loadClass(beanName);Class<?>[] array = beanClass.getSuperclass().getInterfaces();for (Class<?> clazz1 : array){if (clazz1 == SimpleComponentScanBeanDefinitionParser.DAO){return getDaoName(beanClass.getInterfaces()[0]);}else if (clazz1 == SimpleComponentScanBeanDefinitionParser.SERVICE){return getServiceName(beanClass.getInterfaces()[0]);}else if (clazz1 == SimpleComponentScanBeanDefinitionParser.OPERATION) { return getOperationName(beanClass.getInterfaces()[0]); }}}catch (Exception e){}}return super.generateBeanName(definition, registry);}public static String getDaoName(Class<? extends Object> clazz){String beanName = clazz.getSimpleName();return beanName.substring(0, 1).toLowerCase() + beanName.substring(1);}public static String getOperationName(Class<? extends Object> clazz){String beanName = clazz.getSimpleName();return beanName.substring(0, 1).toLowerCase() + beanName.substring(1);}public static String getServiceName(Class<? extends Object> clazz){String beanName = clazz.getSimpleName();return beanName.substring(0, 1).toLowerCase() + beanName.substring(1);}测试配置
moudle.xml片段
<?xml version="1.0" encoding="UTF-8"?> ....<context:wangscu-component-scan base-package="com.wangscu.core" />....</beans>
测试代码
package com.wangscu.spring;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.wangscu.core.server.operation.用户.通过标识符获得用户;public class EntryPoint{public static void main(String[] args) throws Exception{String[] moudles = new String[]{ "moudle.xml" };ApplicationContext context = new ClassPathXmlApplicationContext(moudles);通过标识符获得用户 operation = context.getBean("通过标识符获得用户", 通过标识符获得用户.class);operation.setId(1l);operation.execute();System.out.println(operation.getUser().getName());}}ps:
是不是还有更简单的方法实现上述功能,做到系统和spring真正的低耦合,少配置?名字生成器尚显简单,希望前辈们给小弟一点建议。如果要和struts之类的整合,那么struts那边该如何做?小弟对struts不熟。
所做修改文件和测试代码见附件。
露怯了..
呵呵,
“他明白了,你呢”-------罗家英《大话西游》
这种不能用了么?这种不能用了么?能用。 38 楼 wang_scu 2009-09-02 xajhzc 写道wang_scu 写道Saito 写道 Spring首先他是一个容器.其次他才是一个solution..
还有一点. 抽掉Spring你要干什么? 两种选择. 自己去造一个个factory . 一个个singleton .显然是不现实的.. 另一种选择. 换成别的解决方案. 类似Guice . .现在Guice跟Spring在annotation方面已经达成一致. 并推出了标准. 你换的话是没有成本的. .对于一个项目来说. 第一种方案就是扯淡. 第二种方案确实Guice上有一些所谓的性能优势. 但是现在也已经可以无缝接入了. 就看你要不要为了一个ioc .去牺牲掉别的Spring特性了.
说来惭愧,我们公司就是用的你所谓的扯淡的方法,倒也发现简单。
@autowired能在guice上用,这到没听说过,不是spring包下面的东西吗?到时候是不是还要改代码呢?
我想您还没有理解我题目的意思。
还是觉得直接用注解配置比较省事
如果有框架想取代SPRING,自然要做套同样的解释器。我这人比较乐观~
楼主觉得自己写工厂、单例啥的简单?
汗……还不如用SPRING呢
没有可比性。solution没有好坏 重要是你如何用。
就像上面piziyan说的 二层也没啥不好的。 39 楼 jnoee 2009-09-03 继续做下去可能就整出一个Grails来了,不如拿来主义。 40 楼 summeryhrb 2009-09-03 自定义一个类加载器,并配合注释,即可实现真正的约定,如果有某些特殊情况,通过注释即可解决