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

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

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

扯淡完成,说正经的。

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

	public static SearchService getSearchService() {
if(MOCK) {
return new SearchServiceMock();
} else {
LuceneDAO luceneDAO = getLuceneDAO();
MysqlDAO mysqlDAO = getMysqlDAO(); return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);
}
}

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

纳尼?没看出来?

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

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

package cn.com.sitefromscrath;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class BeanFactory { private static Map<String, String> appBean = new HashMap<String, String>();
private static Map<String, String[]> appRef = new HashMap<String, String[]>(); static {
appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz"); appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
} public static Object getBean(String id) { try { String className = appBean.get(id);
Class clazz = Class.forName(className);
Constructor constructor; String[] ref = appRef.get(id); if(ref == null || ref.length == 0) {
constructor = clazz.getConstructor();
return (Object)constructor.newInstance();
} Class[] parameterTypes = new Class[ref.length];
Object[] initargs = new Object[ref.length]; for(int i = 0; i < ref.length; i++) {
String r = ref[i]; String rclassName = appBean.get(r);
parameterTypes[i] = Class.forName(rclassName).getInterfaces()[0]; //这里我偷懒了:)
initargs[i] = getBean(r);
} constructor = clazz.getConstructor(parameterTypes); return (Object)constructor.newInstance(initargs); } catch (Exception e) {
e.printStackTrace();
return null;
}
} public static void main(String ... arg) { LuceneDAO luceneDAO = (LuceneDAO) getBean("luceneDAO");
int[] vals = luceneDAO.findDocIDs("test");
for(int v : vals) {
System.out.println(v);
} String keywords = "test";
SearchService searchService = (SearchService)getBean("searchService");
List results = searchService.search(keywords);
for(int i = 0; i < results.size(); i++) {
Result result = (Result) results.get(i);
System.out.print("[" + result.title + "]");
System.out.println(result.content);
}
} }

执行结果输出:

1
2
3
4
[result 1]something..................
[result 2]something..................
[result 3]something..................
[result 4]something..................

结果正确!

再看看我们对类的装配:

	private static Map<String, String> appBean = new HashMap<String, String>();
private static Map<String, String[]> appRef = new HashMap<String, String[]>(); static {
appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz"); appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
}

请比較和spring.xml的差别:

<?xml version="1.0" encoding="UTF-8"?>
<beans > <bean id="luceneDAO" class="cn.com.sitefromscrath.dao.LuceneDAOMock" /> <bean id="mysqlDAO" class="cn.com.sitefromscrath.dao.MysqlDAOMock" /> <bean id="searchService" class="cn.com.sitefromscrath.service.SearchServiceInRealBiz">
<constructor-arg index="1" ref="luceneDAO" />
<constructor-arg index="2" ref="mysqlDAO" />
</bean> </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. css定位、position与float同时使用的情况

    一.css定位 CSS 有三种基本的定位机制:普通流.浮动和绝对定位. 1.普通流:未专门指定的元素都在普通流中定位,position:static/relative;和float:none;也在普通 ...

  2. 数据通讯与网络 第五版第24章 传输层协议-TCP协议部分要点

    上一博客记录了UDP协议的关键要点,这部分记录TCP协议的关键要点. 24.3 传输控制协议(TRANSMISSION CONTROL PROTOCOL) TCP(Transmission Contr ...

  3. BZOJ 3831 单调队列DP

    思路: 这好像是我刚学单调性的时候做的题 (我是不会告诉你 我被这题教做人了的...) i-stk[head]>k 删队头 f[stk[tail]]>f[i]||(f[stk[tail]] ...

  4. Android6.0以上版本获取本机蓝牙地址

    Android6.0以上版本使用BluetoothAdapter.getDefaultAdapter().getAddress()是获取不到正确的蓝牙地址的,返回的值都是02:00:00:00:00: ...

  5. 【Linux】Ubuntu输入法不能开机自启的解决方法

    操作系统:Ubuntu Kylin 16.10 自从操作系统安装了搜狗输入法以后,每次重启电脑都需要手动启动Fcitx,才能启动搜狗输入法.下面给大家介绍输入法开机自启的解决方法: 操作系统的用户家目 ...

  6. vue遇到的大坑,h5在ios10版本下不能打开页面

    无论是谁,在做事情的过程中总是会遇到学坑,才能成为最后的大神 这个坑不说了,找了半天.希望能帮助到你们 进入build文件夹: 找到webpack.prod.conf.js文件: 在UglifyPlu ...

  7. eas之新建窗口

    public void actionObjectProp_actionPerformed(ActionEvent e)          throws Exception {     UIContex ...

  8. eas之kdtable格式化

    设置表.列.行或单元的格式化字符串 // 设置表table.getStyleAttributes().setNumberFormat(formatString); // 设置列column.getSt ...

  9. 文件元数据、文件夹操作(day08)

    一.获取文件的元数据(meta data) 通过read write可以对文件的内容进行读写. 但是今天我们要操作的是文件的元数据(文件的属性信息) day08$ls -l hello -rw-rw- ...

  10. malloc实现机制、缓冲机制、文件操作、mmap虚拟地址(day06)

    一.malloc的实现机制(缓冲机制) 库函数跟系统调用之间的关系 什么是缓冲? 内存分配的原理. 封装 函数A的实现代码中调用了函数B.函数B的功能是函数A主要的功能,这样就说函数A封装了函数B. ...