贫血模式下有限富血的所谓proxy模式

为了代码简单,领域模型使用了纯粹的贫血模型,即只包括field定义和对应的get、set方法。(主要原因是当初是先定义数据库表,再用MybatisGenerator生成Java类,因此自己做修改很麻烦)

但是某些与领域模型密切相关操作,又比较适合以类似于富血模式的方式与领域模型绑定到一起。实现某种形式的绑定后,就天然了拥有了领域模型实例本身这个参数,会减少很多代码的复杂度。

最初的动机是:领域模型之间存在依赖关系,例如学生信息中可能存在班级信息的外键,而与之相关的几乎每个交互功能中都存在类似于“学生姓名和所属班级名称”这样的需求。在每个Controller中都写上一个查询方法,非常繁琐,即便是封装了service类,也至少需要在Controller中以某种形式定义一个承载的变量。而我们需要的仅仅是显示而已。

因此,就为这个领域模型配备了一个类,感觉上职责类似于代替这个类做一些事情,因此起名叫proxy。proxy实例要保存一个领域模型的实例,实现绑定。绑定后,就可以在proxy中直接使用,不需要再显式传递。

基类定义:

public ModelProxy(T model)
{
this.model = model;
}

与某Domain类相关联的proxy子类定义:

public DomainProxy(Domain domainInstance)
{
super(domainInstance);
}

领域模型中定义一个getProxy函数,返回与这个模型绑定的proxy类。

private transient DomainProxy proxy = new DomainProxy (this);

public DomainProxy getProxy()
{
return proxy;
}

而proxy类中的方法,命名模仿Java Bean模式:

/**
* 返回学生所有相关班级记录
*
* @return
*/
public List<Classes> getClasses()
{
。。。

}

这样,就可以实现在jsp中直接使用链式el表达式,或者这些信息。

${domain.proxy.classes[0].name}

${domain.proxy.classes[0].proxy.teacher.name}

……

随着开发进行,proxy的内容不仅仅局限于辅助jsp显示。为了防止代码膨胀中造成的混乱,在实践中又总结出了以下一些经验:

Proxy层主要职责:

  • 封装算法:根据Model类一个或几个字段计算结果或判断真假,如根据生日返回育龄妇女年龄,根据生日判断育龄妇女是否符合退出育龄期的条件等。
  • 1-1或n-1的外键关系,即本Model类中有xxId是指向xx表的,则proxy中应有函数通过外键获取实例,返回值应该只有一个实例。
  • 1-n或n-n的外键关系,即某个类有modelId是指向本Model的,或者通过关联表存在多对多的关联关系,此时Proxy应有函数返回另外一个类的List

注:上述两个外键关系,不一定显式的在数据库中存在。也许表结构中没有直接的id存在的,但是按照业务逻辑,会有关联关系

proxy的设计原则:

  • Proxy负责以上各种函数结果的提供,在必要的时候提供对调用者透明的缓存机制
  • 但proxy不作用于model,不改变model的值,在数据库层有变动,model失效的情况下不负责对model的刷新。
  • Proxy层不提供任何直接或间接对数据有操作的方法。
  • 调用proxy的对象必须是从持久层select出来的有id的对象,手动new的对象不经过insert,不能getProxy。

Web开发教程——命令行入门

初学Java的人一般都会被如何设置path环境变量折腾一顿,到了学JavaWeb的时候,又会被CATALINA_HOME之类的环境变量再折腾一顿。了解了命令行运行程序的原理之后,就可以脱离见招拆招乱试的困境。

基础知识

  •  计算机世界中,我们写的、我们用的,都是“程序”。程序需要一定的方式启动,启动的时候,可以传递参数。
  • 双击桌面上的程序图标、点击开始菜单中的程序图标、双击某个类型的文件(如.doc)后操作系统自动用某个程序(如Word)打开,这些都是图形化Windows提供给我们的,运行程序的方法

命令行概念

操作系统会根据自身特点,提供人机交互工具,Windows的图形化界面就是一种。大部分的操作系统都会提供命令行形式的人机交互工具。

命令提示符(运行-》cmd)就是Windows下的命令行工具。大部分Linux,进去之后就直接是个shell环境了,这就是Linux下的命令行工具。

命令行,顾名思义,输入命令、换行,系统按照你的命令执行。这些所谓的命令,其实都属于上面说的程序。

图形化界面易用性好,但由于操作方式基于二维平面上的鼠标点击,想把某个操作录制成脚本,自动执行,很困难。而命令行,由于是纯文本输入输出,利于制作脚本,随便打开个纯文本编辑器,每条命令输入进去,保存,就可以了。因此,命令行是自动化执行任务的主要途径

命令行基本语法

输入命令或程序,补充一些参数,回车执行。

命令和程序的区别,暂时没有找到太严谨的定义。我的理解是,操作系统提供命令行环境本身的一些工具,如cd,属于命令。对于这些命令,并没有一个单独的程序叫cd的与之对应。而程序,应该是符合操作系统规则的独立可执行文件。

例:dir /p
  • 第一个单词,是命令或者程序名称,如上面的dir
  • 第一个单词以后的部分,是参数,如上面的/p。某些参数用于配置程序的运行方式,如dir /p用于配置分页显示。某些参数是作为路径,指定了一个文件给该程序使用。具体的参数含义,取决于该程序怎样使用,在操作系统和命令行工具层面,并没有特殊的含义。
  • >用于组合一个命令(以及参数)和一个文件,会将前一个命令在控制台输出的内容 输入到文件中
  • |用于组合两个命令(以及参数),会将前一个命令在控制台输出的内容输入给第二个命令

Path问题

输入什么能被正常执行?

命令的处理方式是特殊的,这里只说程序。

将输入的内容作为文件路径,找文件,找得到的话,如果该文件是可执行的,则执行,这样,程序就运行起来了。

所以,我们需要解决的是,输入什么样的内容,可以让我们想要的那个可执行文件被找到。

另外,在输入表示文件路径的参数的时候,也要保证该文件可以被找到,从而程序才能使用这个文件。一般来讲,程序使用文件的方式也遵循操作系统找文件的一般习惯,因此,与找可执行文件的方式相同。

那么,什么样的路径能够被找到?

路径分为绝对路径和相对路径两种。绝对路径以/开头,表示从硬盘的根目录开始找起(Windows下,如果不在一个分区内,则通过盘符区分,如c:/abc……)。相对路径不以“/”开头,其“相对”指的是当前目录,即从当前目录开始,根据相对路径的内容找起。

任何时刻,命令行都有“当前目录”的概念。windows命令行工具中,会在行首提示当前目录,如:

D:\var>

可以通过cd命令切换当前目录。linux环境下的显示和操作方式也类似。

如果输入的是绝对路径,那么与当前目录无关,直接按照路径内容找就可以了。

如果输入的是相对路径,则以当前目录为基础,加上相对路径,进行查找。如当前的目录为d:/abc,输入的内容为bcd/ef.exe,则最终找到的内容为d:/abc/bcd/ef.exe。

相对路径中有两个特殊符号,一个点(.)表示当前目录,两个点(..)表示上级目录。如当前目录为d:/abc,输入的内容为../ef.exe,则找到的内容为d:/ef.exe。

环境变量

环境变量是指,在操作系统环境下命令行可以读取的,统一预定义的变量内容。在命令行下可以通过set命令查看和配置。

Window下有个特殊的环境变量,叫path,配置了多个目录,用“;”分隔。如果在命令行运行程序时输入的是不包含路径的程序名称,那么查找过程也会包含path中的所有路径。在查找顺序上,Windows貌似是先当前目录后path,而linux相反。

案例分析:永远找不到的javac

我刚学习java的时候,经历过一下午的各种找不到。貌似现在的jdk已经能在安装后让你直接运行了,如果是这样的话,那就换成永远都找不到的ant,永远都找不到的mvn,都可以。

当你输入javac并回车时,操作系统要怎样找这个程序?在当前路径中找,找不到的话就去path中找。如果都没有,那当然会提示找不到。

网上一定给出了很多教你如何设置path的方法,根据上面说的内容,应该可以知道为什么这样设置就能成功执行了。那再加个题目,能不能找到除了设置path外的第二种方法?用绝对路径就可以了。

那再加个一个题目,装完了oracle之后,eclipse就启动不起来了,是什么原因?

答案:eclipse要javac才能启动-》找javac的时候,依赖了path变量中的内容-》安装oracle时,oracle附带装了一个低版本的jdk,并且放到了path中的最靠前位置-》eclipse不能用这个低版本的jdk启动,于是报错了。

其它

本文中的不严谨

各种定义,措辞,基本上都是我随口说的,并没有经过严格考证。比如所有的内容,都是在我的当前操作系统,当前程序设计环境,当前的业务需求下想到,换成类linux环境,/和\的区别就能让这里的例子基本失效。

不要背诵本文中的任何结论,我想表达的是在我得出这些结论时的思考过程。

 

 

知乎爬虫(一)

知乎上有很多内容不错的回答,但在网上看总归对眼睛、颈椎都不利,所以想到写个脚本把内容爬下来,放到kindle中看。筛选了一下,抓取知乎的收藏夹,从抓取难度、内容组织程度等各个方面都比较合适。第一版导出txt,由于很多精彩内容图都很多,所以后续版本增加了导出pdf功能。由于只是给定url爬取内容,不涉及到读取超链接递归查询,感觉严格来说并不能叫“爬虫”,一时也不知道这应该叫什么名字,先这么称呼着吧。

语言选了最熟悉的Java,用到的技术主要包括:

  • httpclient:抓取网页内容的工具。今年更新版本到4.3,据说api给得很不错,对多线程也有了比较到位的支持。工作项目中用了2.x版本,个人代码直接上最新版。官网地址:http://hc.apache.org/index.html
  • 网页解析工具:jsoup,支持css选择器语法选择html元素。http://jsoup.org/
  • pdf生成工具:iText。常见的java pdf库,坑也不少。最诡异的一个坑是输出中文的时候必须要亚洲语言包,但这个语言包在官网上的下载地址死活都找不到,而网上能找到的大多版本比较旧,想与最新的itext版本结合的话,要手动修改包名。后来发现,核心包的下载地址指向了sourceforge网站,该网站上有extrajar的下载地址,里面有亚洲语言包,地址:http://iweb.dl.sourceforge.net/project/itext/extrajars/extrajars-2.3.zip
  • html生成pdf的辅助工具:xmlWorker,依赖于iText的工具,刚发现的时候惊为天人。官网地址:http://sourceforge.net/projects/xmlworker/。解决中文坑的参考地址:http://www.micmiu.com/opensource/expdoc/itext-xml-worker-cn/

核心代码见:http://nonesuccess.me/?p=293

抓取网页内容

httpclient支持get、post方法获取html内容。具体api使用方法,在官网的quick start上有很详尽的例子,这两者之间的区别也应该是http协议的内容。在api的形式上,简单说,get方式就是拼个字符串作url,post方式则提供了生成参数的api。

我们做企业级开发的时候很少注意get和post的区分,其实在http语义上是有get表查询、post表更新这种区分的。像知乎这种产品,感觉会有大批原教旨主义前端,所以抓取页面内容,用get就足够了。

首先新建一个HttpClient实例,再使用url构造一个HttpGet对象,最后调用httpClient实例的api执行get请求,生成response对象。

[java]

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response1 = null;
try
{
response1 = httpclient.execute(httpGet);
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}

[/java]

httpclient能够透明的处理cookie信息。 使用同一个httpclient对象执行的所有请求,共享cookie。可以采用这种机制处理需要保存session的场景,例如知乎的某些收藏夹,需要登录后才能查看。

执行http请求后,将生成一个CloseableHttpResponse对象,该对象可以获取http请求响应的相关信息。这里只需要获取返回内容,先使用getContent方法获取inputstream,再读取该inputStream,转换成String。

[java]

try
{
content = inputStreamToString(entity1.getContent(), "UTF-8");
} catch (IllegalStateException | IOException e)
{
e.printStackTrace();
}

try
{
response1.close();
} catch (IOException e)
{
e.printStackTrace();
}

[/java]

这里封装了一个inputstream转string的函数如下:

[java]

/**
* 将InputStream转换为String的工具函数 当发生IOException异常时,会将前面正确的字符串返回
*
* @param stream
* @return
*/
private static String inputStreamToString(InputStream is, String encoding)
{
int i = -1;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
while ((i = is.read()) != -1)
{
baos.write(i);
}
} catch (IOException e)
{
log.error("读取html内容的inputStream发生异常", e);
// TODO 继续向上抛出,由上层处理
}
String content = null;
try
{
content = baos.toString(encoding);
} catch (UnsupportedEncodingException e)
{
log.error("不支持的字符集" + encoding);
throw new RuntimeException(e);
}

return content;

}

[/java]

这里使用了ByteArrayOutputStream。网上流传着很多文章,使用的是每次读取一行,或者定义定长大小的byte数组循环读取的方案。前者对换行符的处理很繁琐,后者在处理到数组边界的时候会诡异的丢字符然后有一两个字节就乱码了。

读取到内容之后就可以交给解析模块解析想要的功能了。

想爬知乎的收藏夹,自然先涉及到分析url模式。这部分其实看几个网页就清楚了,比较简单。经过分析,模式为:

http://www.zhihu.com/collection/[收藏夹编号]?page=[x]

所以抓取的时候,先到网上找感兴趣的收藏夹,在地址栏中把编号粘贴出来,先读取第一页,解析总页数,再顺序抓取每一页就可以了。

 

JSP页面中,根据ID获取对象的语法糖封装

需求:

经常需要在jsp页面中,根据id获取某Model的实例,用于显示其某个属性。如:人事管理系统中,大部分的页面都需要根据id获取人员信息,显示姓名、性别等不同的属性。如果都在后台取得之后,用servlet api或者mvc框架的api放到request中,过于繁琐,会造成大量的冗余代码。

解决方案:

在application中放置假map,key为id(或者其他需要频繁查询的key字段),value为根据key查询出的对象。这样,就可以在jsp页面中用el表达式访问map的语法,获取到该对象,并进一步获取其属性。

具体实现方法为:项目启动时,在application域中放置一个实现Map接口的匿名类(可采用Listener方式),只需要重写其get函数。如:

public void contextInitialized(ServletContextEvent contextEvent)
        {
                contextEvent.getServletContext().setAttribute(“modelPoolById”, new HashMap<Long, Model>()
                {
                        /**
                         *
                         */
                        private static final long serialVersionUID = 1L;

                        @Override
                        public Org get(Object key)
                        {
                               //通过Dao层访问该方法并返回
                        }

                });

……

 

在jsp页面中,可以直接使用el表达式访问:${modelPoolById[id].xxx}

如果项目需要,可以在保证访问接口不变的情况下,增加缓存等其他复杂逻辑。

比写JSP标签的方式更轻量,只封装了查询逻辑,并没有限制要显示那些字段,而是提供了一个完整的对象。并且,嵌套给其它标签使用方面也更方便。

项目组J2ee程序员的标志,你中招没 转载+评论

原文在此

校园级别的程序员的标志:

 

代码中最多的是嵌套if(null == xxx),还要告诉你,null必须写在前面,我靠。

防止把==写成=,c语言时代常犯的错误。由于null不能做左值,在写=的时候出现编译错误。一般来讲,在java中,由于boolean和其他类型不会作隐式转换,因此这么写没有意义。

写着写着突然想起来这么个代码:

Boolean b = true;
               
                if(b=null)
                {
                       
                }

 

顺利编译通过,也许把null写在==的左侧还是有意义的。

 

后台满是system.out.println(“——–程序应该会运行到此处的。。。userId:”)。

调试程序过程中,如果想在控制台中打印什么东西,最好用log工具,如已经在多少年前就俗到家了的log4j。好处是:打印日志的语句和控制是否打印日志、控制怎样打印日志的语句是解耦的,可以在程序中随便写打印log的语句,然后在正式上线后,通过某个选项,令其全部失效。

当年流行的组合是log4j+apache commons logging,在程序中引用commons logging的api,build path中放一个log4j的jar包然后再写个log4j的配置文件并且初始化一下,实际生效的就是log4j了。这样做的好处是,想换日志实现类的时候,不用修改程序代码。

今年的流行款是logback+slf4j,对应上面那两个东西。logback的实现据说效率高了多少倍,不过除了非常关键的模块外,这点开销基本可以忽略不计。
html页面总是对不起一两个div的线,老用firefox去框框显示那个div。

搞前端的都是神,我到现在也对不齐那堆div。另提一句,bootstrap是良心作,好人一生平安。
IO异常处理那个就是叠罗汉啊。

异常处理是个技术活,我当年的思路是,根本不知道有非受控异常这件事,出现受控异常未处理的编译错误时,点开eclipse的自动处理选项,然后按照心情选一个。

一点经验:

数据库异常、io异常就直接上抛,如果框架非得搞成checked的话(譬如ibatis2.x),发现了就给封一层RuntimeException,一直抛到顶层让这个事务挂了就好了。然后客户会明确的知道这个程序sb了,怒不可遏的给维护打电话,然后维护远程ssh,重启数据库了事。如果项目经理稍稍良心一点的话,针对这种错误可以做的包括:1、打异常log,2、给客户一个明确的提示“系统挂掉了,请联系系统维护人员电话是13xxxxxxxxx”而不是显示一堆异常堆栈然后再让客户翻电话本。想写段代码自动处理这些异常的想法不是不能实现,但对于业务定制类的系统来说,代价太大。

nullpointerexception、各种illegelxxxexception,其它各种开发人员懒得去学的异常,这些基本上都是程序的bug,开发测试阶段出现了就调整,打各种补丁,直至所有的测试流程都走通。实际运行阶段再出的话,类比上一条。

在业务基本走通,正常流程都没问题了,而项目组又恰好有足够的时间投入在异常架构上,可以做下面的事:分析业务,找出需要处理的业务异常,如根据身份证号查出了两个完全不同的人等,定义处理这些异常的逻辑,定义项目级别的异常基类api,之后就可以从底层一层层的加各种if判断,抛出异常,再向上找到该处理异常的地方,写catch代码。最重要的就是在抛异常的时候,把所有有意义的信息都记录下来,比如上面说的身份证号的异常,起码要记录身份证号是什么、找出的两个人的主键,另外还可以记录操作时间、操作人等信息。只要在异常抛出的时候做到这一点,随便你决定在哪一层catch,哪怕只做个log,都会让维护人员、后续开发人员感谢你的良心。

一般级别的程序员标志:
会struts+spring+hibernate|mybatis,面试神器。

曾经的观点:

怎样用这些东西:找个文档读一点,都能学会。

为什么要用这些东西:体现真正水平的问题。

现在的观点:

怎样用这些东西:做到随便给给技术,读读文档就能用上的,都是水平已经不错了的人。

为什么要用这些东西:网上答案一搜一大把,需要分辨是直接拿的结论,还是真被某些雷炸过然后很良心的告诉你,struts2真操蛋。

struts1、2、spring mvc这一层,用的并不多。由于没做过什么访问量特别高的项目,因此对struts2的效率并没有太多吐槽。现在的选择标准:1、是否提供了足够的语法糖让一般水平的人也能够快速开发 2、是否没有提供不恰当的语法糖,从而大幅度的增加维护难度。annotation、零配置这些东西,个人觉得最好用于元属性设置,而不要用于流程控制。曾经有个同事发现前台传进来的参数莫名其妙的会丢一个,后来发现在引用的某个jar包中有一个annotation方式定义的filter。尽管实际上的问题是架构组没有写完善的文档,没有做培训,但是经过那件事之后我也同意,这种东西放在一个统一的配置文件里面看着会更清楚一点,控制粒度也更细一点。

当年实训初学spring就觉得这东西很蛋疼,迄今为止一直没遇到复杂度高到有大批量属性需要注入的地方。aop的应用场景倒是遇到几个,不过出于对庞大的spring以及配置文件膨胀的恐惧,我宁愿改变整个的框架,定一个抽象层然后用各种各样的strategy模式。倒是觉得有完善annotation支持的轻量级aop应该是个不错的选择。

hibernate用的也不多,我好像对主流的s2sh有本能的抗拒……一直对sql的结构化有深深的执着,因为你真的不想没事总给别人调那一堆动辄五六行变量名很奇怪大小写都有的sql语句。所以在看到hibernate做某些复杂查询必须用hql的时候就放弃了这门技术了,sql本身就不想看,再发现这sql实际上是在java里面用String拼起来的……

ibatis/Mybatis一系用的比较多的原因是,职业生涯用到的首套一站式框架的作者就用的这玩意儿,属于马屁股性质的事情。这东西的好处就是把sql和java代码分离开了,程序看起来比较干净。但是让每个模块的程序员都去决定要写哪些sql,怎样写,同样是个很可怕的事情。所以当年我定义了一套针对每张表的标准sql列表,几项最基本的CRUD操作。select操作只支持id查询和各个属性的and相等查询。这个基本上已经可以满足80%的需求了,剩下20%,定好了配置文件分割策略后,找个sql好点的人慢慢写就是了。另外,这东西的一大槽点是只支持假分页,我们的处理方式一般是忽悠客户买个内存大点的机器就得了,不具备这种忽悠机制的同行们请慎重选择,ibatis想解决这个问题需要hack jar包,mybatis也需要自己写个插件支持,大概有个半天到一天的工作量。

再后来觉得配置文件有些繁琐,所以又在apache commons dbutils基础上封装了一套,不用写配置文件,定义好列名和属性名的映射关系后,自动生成CRUD操作并作db访问操作。在封装复杂的select操作时发现了nutz这个东西,觉得思路基本一致,就懒得再继续写了。

nutz提供的dao工具实现了用比较结构化的方法拼sql语句,试了试,用着还不错。事务管理机制据说有槽点,没细看。

决定我没有实际使用的槽点:1、不支持手动配置db和java的名称映射,而我给项目组定的映射规则又比较奇葩,给每个属性都写个annotation有点蛋疼。2、貌似是用反射方式直接读写java属性,而不是用java bean规范的get、set方法,而我的很多工具类都是做的虚拟属性,只有两个无比复杂的get/set方法,没有实际的属性。当发现annotation只能加在字段上时,欲哭无泪只好放弃了。

现在项目中用着的还是mybatis,用官方提供的mybatis generator生成配置文件,单表操作支持得很完善,多表操作自己写sql解决。

能分清楚group和having的区别。

关于db另一个要说的问题:复杂的东西尽量别放sql里面。orm层提供基本操作就够了,剩下的逻辑交给java层表达。做好封装,实在有效率问题,再专门进行优化。好吧,我承认我不知道这哥俩到底有什么区别。


数据库里的字段必须只能2个长度,不能32个长度,性能问题。表名要以T开头,蛋快碎了。

长度问题没看懂要说什么。前两天遇到一个问题,在oracle中如果用char类型的,而实际内容又不正好等于char长度的时候,select出来的数据是带空格的……各种trim都感觉不太好使之后,果断varchar2了。反正这点效率差异,早就被各种奇葩的业务逻辑给抵消了,还是那句话,有问题的时候再优化。

表名问题,我觉得加前缀还是为了跟视图进行区分。当表只有几张时,什么区分也不用做,当表有一百七十张时,什么区分都不管用。所以,战略上看需求决定就行了……

一点经验:通通加T跟没加是一个效果。现在项目中的做法:数据库中大部分的表都是对应了一个业务实体,这些类直接写名字就行,在这些表上建的视图统一加一个V_前缀,也就能起到区分效果了。一般来讲,一百七十张表中至少有一百五十五张以上都是这种表。其他的表,则大多对应了某些特殊场景或算法,实际开发中一般由专门的核心人员负责,可以按照权限控制、事务控制等实际用途,加个什么前缀,谁负责哪个模块就关注哪个前缀就好了。


会用jquery.ajax获取数据,不知道ajax的同步锁。

我也不知道。对于一切锁,我的意见都是,上个最大粒度的,保证数据一致性。出现的所有问题具体分析,逐个减小锁的粒度。

一会显示的表格有数据,一会又没数据啊,太生气了。

遇到此类抱怨时:1、告诉他,工业界没有玄幻故事,2、再提同样的问题的话,辞了他。让这种人觉得自己还有混在程序员界的错觉是对他的不负责。

详细记录当时的运行环境,一般都会找到原因的。

骚年级别的程序员标志:

懂div的float,clear的含义。

1、上一套bootstrap,解决80%的问题。2、剩下20%的问题,找个跟你关系不错的前端,没事多请人家吃吃饭,然后就解决了。


数据库超过n记录表横向纵向分表。

问题在于,n等于几。高估n无所谓,反正要出现的问题肯定会出现。低估n的话,造成的进度损失会让你有自杀的冲动,造成的数据损失会让项目经理有杀了你的冲动。

知道oracle的用with,这个sql写起来看起就舒服了。

这个也不会,粗略google了一下,确实有点用途。

看到aop能说一大堆废话,又是代理又是反射,就是没写过。

你的错误在于,不知道用途就研究实现原理。

DateUtil一定写过好多次,简直太复杂了,非常多的格式定义,那个static格式变量,必须要深刻理解才能知道。

1、有句评价说的好:把一个工具包的几乎所有方法都写到标记过时,这是得有多仇视这个社会。

2、自从有了JodaDate,妈妈再也不担心我的日期处理了。当一个同事又一次熬通宵写出一个DateUtil然后我拿出了JodaDate之后……

砥柱级别的程序员标志:

会架构程序,能用extjs或者easyui写个框架frame,还能写个递归menu。

所有的知识点都很基础,但是能把这一切都完整写出来,完成debug之后让项目组用上,一段时间之后还能维护或者添加点新功能的,都是中流砥柱。说白了,这是个情商要求大于智商要求的活。业务系统定制开发,实际上都是这种类型的活。你并不需要有特别高深的技术,也不会突然面对多么巨大的困难,只会在一个个看似不起眼的bug中,把所有激情都消磨殆尽。

会用ps处理图片,还能写上几个字,XXXX系统beta版本。

会ps的都是神,不解释。

基本上util包的作者,用log或者拦截器记录日志。

并且,愿意跟一切动过你util包的人玩命。

能用fiter或者Interceptor处理权限,但是搞不懂如何处理button的权限。

在业务级别去掉button权限的需求就好了。

真正的解决方案没有实际执行过,只是一个想法:系统建模的时候,权限模型直接建到操作级,比方说每个Action处理函数对应一个操作,针对这个操作定义每个角色的权限。button在概念上同样映射到权限就可以了。至于怎么针对函数做权限控制,随便你用xml或者annotation定义一下就行。

明白了异常处理转换成RuntimeExcetion太好了,不会丢失而且好处理。

异常处理真是个技术活。当我说,我也不知道我的方案好在哪,只是觉得你们的方式不优雅时,我清楚的听到了对方心里的嗤笑声。往往我的结论都会在大概三个模块开发周期之内被确认。

page分页里代码和css样式和类class都在jsptag里,认为没法分啊,这个是典型。

前端用于提交参数,目测所说的代码是计算page、rowperpage这些属性的。随便找套js grid控件,看看他们的参数提交方式,前端不依赖任何jsp,分到那个份上我觉得就足够了。

小牛级别的程序员:

知道url资源树和menu的区别。

不明觉厉。这种概念性的东西其实挺多人都挺不重视的,曾经很反对这种不重视,但是重视了很多年还真没重视到什么收获。现在的观点就是,用到了再说。

能手写css,懂important能拿来做啥,这个好玩得很。

又是前端,上bootstrap吧

能够理解数据库必须用主外键,否则那帮家伙一定会乱写程序。

只要是实体类,必须有主键,并且一定要有物理主键,不能只依赖于逻辑主键。id这东西,找不到其他用途的话就简单的当个快速查询定位的工具就行。随着业务复杂度的增加,你会发现它的表现力越来越强。在大部分依赖于持久化的业务类系统中,可以简单的定义,有id就代表这东西存在,没id就代表不存在,顺着这条思路往下想,很多业务都会简化。

只用id做外键,不要用神马身份证号、订单编号之类的东西。然后你的程序随便怎么写都能写得下去。

会设计数据库模型,几百张表的小意思。

针对真实世界,只作抽象,不作修改,保持整个系统概念上的一致性。然后你会发现,设计的模型会恰好符合数据库设计的各种准则。这时候这个数据库结构就能用了。

如果你设计出一张自认为很有用的关系表,却起不出合适的名字来;或者数据库中有一个不是纯粹为了效率问题而设置的冗余字段,相信我,你终将遇到一个你的模型无法表现的业务需求。

注释用//只有一行,不用/**多行,因为程序即注释。

jdk标准注释都不用,那javadoc咋办?

好吧,程序即注释这东西,几个水平相当、思路相近的人,通过不定期的结对编程、互相重构代码,还是可以做到的。如果是大规模的开发,还是建议通过架构层面合理的分层解决。

知道struts模型驱动代替属性注入,方便太多事了。

又一个语法糖。有了实际需求再用,到底用不用不要争论起来没完,遵循这两点就行了。这个真心不是核心问题。

用过this做参数传递,哈好多人都没用见过。

哈真神奇!这话真有人对我说过。

技术上this就是个指向自身的引用。某些具体的场景确实用起来很有意思,高层面的意义还没太想清楚,只有一个模模糊糊的印象,大概就是做了一件把自身委托给其他对象的事,封装了某个参数传递的过程,也就是封装了自身和被委托类的关系。

se级别的程序员:

研究过struts,hibernate的源代码,ui里有颜色互补概念,看起来是要舒服点啊。

学源代码要跟写代码结合起来执行,学到了新的模式之后,多想想有什么应用场景,但是真的实际使用要慎重。譬如说看到struts2的层层wrapper模式后就用了一次,被喷了好长时间

觉得struts,hibernate,spring,要扔掉一个框架,一定是spring,这个废啊。

让我选的话,我扔hibernate。

写过mvc,知道前端拦截器,中心分发器,后置处理,bean映射。

要知道就算没有这些概念,代码层面也一定会实现mvc的全部功能。然后找到没有这些概念的代价,哪些东西就耦合了,哪些变更就应对不了了等等。最后你的水平就提高了。

会用模型驱动user.save(),代替dao。

少传一个参数,概念上优雅了一些。模型驱动太考验建模能力,一定要在一个范围内把所有问题想清楚再使用。建议把DDD那本书看个两三遍再说。

不过这东西看上去真的很吸引人。

能用metadata生成一堆乱七八杂的代码,这下爽多了。

metadata的解释是“描述数据的数据”,比方说数据库的表结构定义可能就算是一种metadata。在写代码过程中能正确的抽象出元数据之后,眼光会提高一个层次,至于是不是要搞生成代码的工具,因项目而异。

曾经用过一段时间的freemarker,写一些轻量级的代码生成工具还是挺好用的。

研究过Annotation,用Annotation写过注解,知道Annotation如何继承,太复杂搞不懂。

拿Annotation实现过一套Model工具,没有深入了解过ejb,可能有点entity bean的思路在里面把。

前面说过一部分annotation了。这东西的好处就是把元数据跟java代码放到一起了,于是好找也好改了,坏处也是放在一起所以耦合了。如果代码量大到一定程度之后,最好把所有主力都集合到一块儿商量一下,到底是xml好还是annotation好。

在代码量没大到一定程度,或者annotation配置的数据仅仅是annotation所在的类自己用的话,可以在开发效率上考虑一下这个问题。jdk提供了语言层面的annotation操作工具,使用简单,有一部分的编译期检查,写起来比xml要舒服。另外,个人认为annotation的语法不太适合定义层次太深的结构,在类前面写上四层annotation再用ide做个formatter,说实话挺愁人的。

Boss级别的程序员:

仰慕一下。。。。

 

以上经验建立在如下基础上:

我做的项目是大部分是技术要求特别简单,业务要求中等复杂,需求变更特别频繁,开发人员平均素质不足,工期不是很紧的类型,所以关注点集中在如何通过分层隔离业务复杂度,以及如何通过语法糖来降低开发复杂度。bug方面,比较关注的是影响数据一致性的bug,只要不影响数据一致性,哪怕系统直接挂了,都不是影响项目的生死因素。

在做技术方案的时候,比较倾向于:

1、通过各种设计模式封装复杂度,提供尽量简单,甚至无脑开发的接口

2、忽略一切效率问题,在业务打通之后再想优化的事

3、能在编译器做的事就不往运行期放,哪怕会影响开发的灵活性

多页面提交,统一持久化解决方案

需求:

人员信息包括一张主表和若干张子表,点击某链接进入某人员信息详情页面后,可以针对主表和子表信息进行修改。所有的修改统一保存后,打包提交到上级审核(相当于做底层的持久化操作),审核通过后,进行实际修改操作,审核不通过时,保留原信息。

计划采用Command和Strategy模式,将修改操作抽象为若干Command,每个Command都有changeStrategy函数做实际的变更。在Command之上建立Transaction概念,用以表示一批的修改内容,要么同时生效,要么同时不生效。

问题:

每个保存操作会提交一个单独的http请求到后台,此时需要作Command的持久化。多个修改操作需要多次重复此过程,最终每个Command都与同一个Transaction相关联。因此,需要在多次http请求之间共享Transaction信息。

解决方案:

使用JSP的session机制进行信息共享。考虑到客户端的不可靠,不能依赖浏览器的onclose事件来判断用户操作是否完成。

在session中建立Map,key为人员id,value为Transaction对象。

进入人员主表信息页面之前,先根据该人员信息的id在上述map中查询,如果有Transaction对象则删除,之后建立新的Transaction对象。

在每个修改请求的处理函数中,根据人员id在session中查找到Transaction对象,新建command并与之关联,一并存入session中。

点击统一的保存后,根据人员id查找到Transaction对象,修改状态并作持久化,进入下一流程。

优点:

依赖于用户的明确操作,即“点击保存按钮”,来完成持久化动作,避免浏览器异常退出后,流程不能正常完成。