首先,我要大字标语表达立场:

你所使用的framework & non-core features,就跟女人穿在身上的衣服一样,越少越好!

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGVsdGF0YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" height="289" width="349">

扯淡完成,说正经的。

让我们继续盯着花姐——啊,不——是 BeanFactory看。

  1. public static SearchService getSearchService() {
  2. if(MOCK) {
  3. return new SearchServiceMock();
  4. } else {
  5. LuceneDAO luceneDAO = getLuceneDAO();
  6. MysqlDAO mysqlDAO = getMysqlDAO();
  7.  
  8. return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);
  9. }
  10. }

有木有点儿标题所说的“春天在这里”的意思了?

纳尼?没看出来?

好吧,或许你习惯了spring的xml装配方式,所以认为把两者关联起来看实在须要超常的想象力,那么,

我把BeanFactory改头换面。简单的实现一个基于文本字符串的装配,咋们再来看看效果:

  1. package cn.com.sitefromscrath;
  2.  
  3. import java.lang.reflect.Constructor;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7.  
  8. public class BeanFactory {
  9.  
  10. private static Map<String, String> appBean = new HashMap<String, String>();
  11. private static Map<String, String[]> appRef = new HashMap<String, String[]>();
  12.  
  13. static {
  14. appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
  15. appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
  16. appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
  17.  
  18. appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
  19. }
  20.  
  21. public static Object getBean(String id) {
  22.  
  23. try {
  24.  
  25. String className = appBean.get(id);
  26. Class clazz = Class.forName(className);
  27. Constructor constructor;
  28.  
  29. String[] ref = appRef.get(id);
  30.  
  31. if(ref == null || ref.length == 0) {
  32. constructor = clazz.getConstructor();
  33. return (Object)constructor.newInstance();
  34. }
  35.  
  36. Class[] parameterTypes = new Class[ref.length];
  37. Object[] initargs = new Object[ref.length];
  38.  
  39. for(int i = 0; i < ref.length; i++) {
  40. String r = ref[i];
  41.  
  42. String rclassName = appBean.get(r);
  43. parameterTypes[i] = Class.forName(rclassName).getInterfaces()[0]; //这里我偷懒了:)
  44. initargs[i] = getBean(r);
  45. }
  46.  
  47. constructor = clazz.getConstructor(parameterTypes);
  48.  
  49. return (Object)constructor.newInstance(initargs);
  50.  
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. return null;
  54. }
  55. }
  56.  
  57. public static void main(String ... arg) {
  58.  
  59. LuceneDAO luceneDAO = (LuceneDAO) getBean("luceneDAO");
  60. int[] vals = luceneDAO.findDocIDs("test");
  61. for(int v : vals) {
  62. System.out.println(v);
  63. }
  64.  
  65. String keywords = "test";
  66. SearchService searchService = (SearchService)getBean("searchService");
  67. List results = searchService.search(keywords);
  68. for(int i = 0; i < results.size(); i++) {
  69. Result result = (Result) results.get(i);
  70. System.out.print("[" + result.title + "]");
  71. System.out.println(result.content);
  72. }
  73. }
  74.  
  75. }

执行结果输出:

  1. 1
  2. 2
  3. 3
  4. 4
  5. [result 1]something..................
  6. [result 2]something..................
  7. [result 3]something..................
  8. [result 4]something..................

结果正确!

再看看我们对类的装配:

  1. private static Map<String, String> appBean = new HashMap<String, String>();
  2. private static Map<String, String[]> appRef = new HashMap<String, String[]>();
  3.  
  4. static {
  5. appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
  6. appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
  7. appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
  8.  
  9. appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
  10. }

请比較和spring.xml的差别:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans >
  3.  
  4. <bean id="luceneDAO" class="cn.com.sitefromscrath.dao.LuceneDAOMock" />
  5.  
  6. <bean id="mysqlDAO" class="cn.com.sitefromscrath.dao.MysqlDAOMock" />
  7.  
  8. <bean id="searchService" class="cn.com.sitefromscrath.service.SearchServiceInRealBiz">
  9. <constructor-arg index="1" ref="luceneDAO" />
  10. <constructor-arg index="2" ref="mysqlDAO" />
  11. </bean>
  12.  
  13. </beans>

好吧,没什么根本上的差别,我们相同可以解析xml,得到我们自己实现的BeanFactory所须要的一切要素。

好了。经过我们将代码从零開始,重复重构,到一个比較“经典”的模式。我们找到了“春天”。这也是spring的core features。

或许是对spring看待的角度不同,我发现我对spring的依赖注入和控制反转的用途和不少人并不一致。下一章。我打算结合一些开发和測试技巧,论述一下我的看法。

只是。如今,是吐槽spring的时间:

我在我的博文《thinking in asp》appendix A - 吐槽JAVA 中以前说到:

还有新版本号的spring,怎么说呢,它把java的annotation机制玩儿到了“奇技淫巧”的程度。

因为那个系列主要是讲视频编转码技术。因此。对spring仅仅是一带而过,如今总算找到机会了:)

Long long ago, in the old good times, spring还是依赖xml做装配的小姑娘,青春漂亮,带着点儿书卷学生气。

可是如今看,这丫头已经涂脂抹粉,跻身上流社会。出入商务场合。尽管不讨厌,可是不让人认为亲近了 -_-b

看传统的 spring.xml,就如同看电路板设计图,一个个元器件清清楚楚,什么型号,怎么走线。怎样装配。尽管没有图形化。可是一目了然,基本上能够做到不用查看源代码。就能把整个系统的逻辑关系梳理的八九不离十。

而自从有了annotation,比方autowire。xml不重要了,你无法再从一个基于spring的大型项目中迅速找到一份天然的“提纲”。

——spring開始不顾一切的媚俗。——或许俺是个受虐狂,只是“ruby way”和“傲娇”的python显然更对我的胃口——设计原则是不能够松口的:)

我以前问spring的一些使用者,怎样找到通过annotation装配尤其是自己主动装配的类,或者是某个隐藏在spring-mvc框架下annotation声明的URL,

给我的答案例如以下:

首先:

然后:

好吧。我得到的结论是:与其如此,不如不用:)

当然,假设你说。通过一些词法/语法解析器,也能够得到基于annotation的“提纲”。比方用 lex+yacc 亦或 antlr 打造一个工具。

本座的答复是:老子被编译原理搞的几宿没睡了,小心一指头把你戳出去三公里远去~~~!

接着。说说spring框架下怎样强測试的问题:

时刻记住。每个模块。甚至最“小”的方法,在实现它之前,都必需要先设计怎样測试它。

由于我们如今讨论的是一个web项目,我想说一个非常多开发人员会使用的方法:

ContextListener --> WebApplication --> BeanFactory

因为spring的“人性化”。这个步骤甚至不须要你写代码。

如今的问题是,假设我们的项目採取这种方式。你怎样做dao 或者 service层的单元測试?

比方前面提到的 SearchService,他须要通过spring装配LuceneDAO 和 MysqlDAO,可是问题出现了:

你假设想让SearchService的方法跑起来,你必须启动TOMCAT等web容器!

这样的紧耦合的程度简直是令人发指 :) 让一切单元測试成为不可能~~~~

而我,在前面的章节重复强调 “不须要启动tomcat。不须要查看网页的实际效果,也能保证系统模块的正确” 就是这个意思:)

当然。spring提供了ApplicationContext,在web容器中相同能用——这也是我使用的方式——可是,我想说的是。spring的WebApplication模式错误的诱导了开发人员。引发了大量的 bad smell。

在玩儿了一把怎样从最简单的需求出发。重复重构到模式/框架之后,下一章我会再次绕回去,spring已经说得够多的啦:)

to be continued.....

开发,从需求出发 &#183; 之四 春天在这里的更多相关文章

  1. 开发,从需求出发 &#183; 之三 春天在哪里

    <西游降魔>里面的<儿歌三百首>里面有首儿歌叫做<春天在哪里> 歌词是这种: 春天在哪里 春天在哪里 春天就在小朋友的眼睛里 通过俺的渣英语翻译之后是这种: whe ...

  2. 开发,从需求出发 &#183; 之二 造飞机的工厂

    CD镇楼~~! 如今.让我们切换到后端开发者的角度看问题.我们须要做的是实现一下这个类,让它返回真实的业务数据. package cn.com.sitefromscrath.service; impo ...

  3. 吴裕雄--天生自然PythonDjangoWeb企业开发:需求

    开发或者做一个项目,是要有一个需求过来的,而不是无缘无故的,启动一个项目,或者推动整个项目进行下一步迭代.这个需求可能是根据用户反馈增加的,可能是老板提出来的,也有可能是产品经理提出来的,但是无论是什 ...

  4. [eShopOnContainers 学习系列] - 00 - 开发环境需求

    开发环境需求 https://github.com/dotnet-architecture/eShopOnContainers/wiki/00.-Dev-machine-requirements 我的 ...

  5. 开发人员需求能kill杀死其它阻塞自己的会话,测试发现需要alter system权限有风险

    模拟开发人员需求,可以杀死其它阻塞自己的会话1.能有查询阻塞会话确认的权限SQL> grant select on v_$session to testa;SQL> grant selec ...

  6. DNF邀请码开发再开发方案需求

    一.原因分析:   1.现实原因:主播粉丝量级有限,一定规模粉丝注册消耗完后无法进 行之后合作 2.主播资源有限,能合作主播数量少   3.直播粉丝真实接近核心用户,但是不能将其有效转化为平台流水   ...

  7. 测试开发【提测平台】分享3-正式开发产品需求&项目初始化

    上两个分享主要是介绍和演示基本前后端所要使用的框架,接下来我们将正式进入到[提测平台的开发] 提要先给出依赖和内容点: 提测平台定义和产品原型需求说明 使用github创建代码仓库进行项目管理 Fla ...

  8. HBase应用开发回顾与总结系列之四:HBase配置管理类接口设计

      利用Eclipse进行HBase应用开发时,至少需要确定三个配置信息,如下表所示: #hbase config #HMaster服务部署主机及端口号 hbase.master=hdp-wuyong ...

  9. iOS开发——项目需求-快速回到当前界面的顶部

    利用UIWindow实现快速到达顶部 如下图,在状态栏添加一个没有颜色的UIWindow(里面添加一个按钮),实现点击这个按钮时能快速的回到当前界面的顶部 核心代码 一.利用UIWindow实现到达顶 ...

随机推荐

  1. Ruby类扩张(extension)

    创建: 2017/09/07 更新: 2017/09/16 修改标题字母大小写 ruby ---> Ruby    扩张类  class 类名     扩张的内容  end           ...

  2. JS代码放在哪里比较好!

    在页面上加上<script></script>只有2个地方:head中,body体中 如果外部的JS文件,在head中加,写页面特效js放在body后面. <html&g ...

  3. JSP页面使用EL表达式内容显示不全问题记录

    1.当EL表达式里面的值存在引号之类的字符时, ${caseparam.cp_value}的值为 {"cpage":"1","resType" ...

  4. node js koa js严格模式

      当前为配置 非原创 引用于“得金” ### nodejs项目配置终端命令 1. 检查本地 nodejs 版本`$node -v` 如果版本低就升级 2. 安装 n 升级命令 `$npm insta ...

  5. vue-cli脚手架安装过程(精简版)

    1:打开node的控制台,并找到对应的文件夹 2:检查node的版本 node -v 3:检查npm的版本 npm -v 4:检查cnpm的版本 cnpm -v 5:安装全局及脚手架 cnpm ins ...

  6. Android Retrofit 2.0文件上传

    Android Retrofit 实现(图文上传)文字(参数)和多张图片一起上传 使用Retrofit进行文件上传,肯定离不开Part & PartMap. public interface ...

  7. 深圳面试一周记录——.NET(B/S)开发

    个人简单信息:2011年毕业,最高学历大专,最近一份工作在广州:有做架构设计经验,有一年的带团队(10人左右)经验:互联网和行业软件公司都待过. 为免不必要的争论,本文说地址的就不说公司行业,说公司行 ...

  8. Visual Basic for Application

    Private Sub Worksheet_SelectionChange(ByVal Target As Range) 'The note of Visual Basic for Applicati ...

  9. react基础篇二

    组件 & Props & 生命周期 组件可以将UI切分成一些独立的.可复用的部件,这样你就只需专注于构建每一个单独的部件. 组件从概念上看就像是函数,它可以接收任意的输入值(称之为“p ...

  10. grep命令总结

    grep (缩写来自Globally search a Regular Expression and Print)是一种强大的文本搜索工具,它能使用特定模式匹配(包括正则表达式)搜索文本,并默认输出匹 ...