#
总有那么一些代码,在测试环境下,是不能轻易被调用的。
比如:
1)发送系统任务邮件到客户邮箱,可能一不小心,就把测试邮件发送给了真实客户的邮箱里;
2)调用跨公司的系统接口,而对方系统没有测试环境,每调用一次接口,就会在对方系统产生垃圾数据;
3)调用的代码可能需要大量的cpu运算,占用大量的内存空间,消耗大量的资源;
等等。。。
为了解决这样的需求,
1)在代码中,到处充斥着这样的代码:
1 if(在测试环境下) {
2 打印日志;
3 } else {
4 调用真实的业务逻辑;
5 }
于是乎,需要到处维护这样的代码,一旦增加此类需求,就需要编写同样的代码
2)部分懒惰的程序员,连这样的if...else...也不愿意写,仅仅在注释中说明下在测试环境中调用方法的危害性。
于是,在测试阶段,一旦和测试部门沟通不足,导致代码还是经常被调用到,如果是在作压力,性能测试,那么危害性可想而已。
曾发生过,压力测试某个功能,结果把大量的测试邮件,发送给了客户,影响很差。
那么,如何解决这样的需求场景呢?
没错,采用proxy模式,可以搞定。考虑到现在很多企业都使用Spring作为IOC容器,本文就简单介绍,如何采用spring aop来解决问题。
以发送邮件的需求作为虚拟场景。
现在有个Service,专门负责邮件的发送。
1. MyService.java
1 public class MyService {
2 public void sendMailSafely() {
3 System.out.println("send mail successfully.");
4 }
5 }
如果这个sendMailSafely被客户端调用,那么毫无疑问,邮件不管任何环境下,都会被成功发送。
需要有个方法拦截器,对这个方法做拦截。
2. MyInterceptor.java
1 public class MyInterceptor implements MethodInterceptor {
2
3 private boolean isProduction = false;
4
5 @Override
6 public Object invoke(MethodInvocation invocation) throws Throwable {
7 if (!isProduction) {
8 System.out.println("is production environment.do nothing
");
9 return null;
10 }
11 return invocation.proceed();
12 }
13
14 public void setProduction(boolean isProduction) {
15 this.isProduction = isProduction;
16 }
17
18 }
这个拦截器,根据配置文件的参数isProduction判断是否在正式环境,如果是在测试环境,对方法做拦截,仅仅打印log,不真实调用业务逻辑。
如何让sendMailSafely()方法被此拦截器做拦截,所以通过spring配置文件,配置一个advisor,通知对以Safely结尾的方法做拦截
3. application.xml
1 <bean id="safetyAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" scope="singleton">
2 <property name="advice">
3 <ref local="myInterceptor" />
4 </property>
5 <property name="patterns">
6 <list>
7 <value>.*Safely</value>
8 </list>
9 </property>
10 </bean>
附上application.xml的全部内容
1 <beans default-autowire="byName">
2
3 <!-- service实例 -->
4 <bean id="myService" class="cn.zeroall.javalab.aop.MyService" scope="singleton" />
5
6 <!-- 方法拦截器 -->
7 <bean id="myInterceptor" class="cn.zeroall.javalab.aop.MyInterceptor" scope="singleton">
8 <property name="production" value="false" />
9 </bean>
10
11 <!-- 通知者 -->
12 <bean id="safetyAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" scope="singleton">
13 <property name="advice">
14 <ref local="myInterceptor" />
15 </property>
16 <property name="patterns">
17 <list>
18 <value>.*Safely</value>
19 </list>
20 </property>
21 </bean>
22
23 <!-- myService代理类 -->
24 <bean id="safetyService" class="org.springframework.aop.framework.ProxyFactoryBean" scope="singleton">
25 <property name="interceptorNames">
26 <list>
27 <value>safetyAdvisor</value>
28 </list>
29 </property>
30 <property name="targetName" value="myService" />
31 </bean>
32
33 </beans>
写一个Client类来做演示。
4. Client.java
1 public class Client {
2
3 private ApplicationContext ctx = new ClassPathXmlApplicationContext(
4 "cn/zeroall/javalab/aop/application.xml");;
5
6 public static void main(String[] args) {
7 Client c = new Client();
8 c.sendMail();
9 c.sendMailSafety();
10 }
11
12 public void sendMail() {
13 MyService myService = (MyService) ctx.getBean("myService");
14 myService.sendMailSafely();
15 }
16
17 public void sendMailSafety() {
18 MyService myService = (MyService) ctx.getBean("safetyService");
19 myService.sendMailSafely();
20 }
21
22 }
大家可以看看最终输出的结果内容。
一直来,我都不会滥用AOP,尤其反感使用AOP来写业务逻辑内容,但是对于这类非业务逻辑需求,采用spring aop技术那是刚刚好啊。
最后,附上全部代码文件(使用maven构建)。
演示代码
逐渐地,发觉数据订正成为了我工作的一部分;
逐渐地,发觉一天有四个小时的时间在数据订正上的日子越来越多;
逐渐地,发觉一天仅仅只有两个小时投入到编码的日子也频繁起来;
逐渐地,发觉我面向的客户也不仅仅是PD,测试部门,客服、销售、销售支持也成为了我的服务对象。
我不是做技术支持的,但是客服、销售、销售支持的咨询以及提交数据订正的申请打扰却影响到了
我正常的工作,只能利用晚上加班的时候,去完成一天的编码工作。
为什么会有那么多数据订正发生?
合理的数据订正,一般发生于下面的两种可能:
1)系统程序存在bug,那么毫无疑问,只能作bug fix工作,然后集中进行一次数据订正操作;
2)业务部门,由于不小心,操作失误等原因,产生错误数据。这种情形发生的比较少,一般由这种原因导致的错误数据,
我这边收到销售支持提交的数据订正申请,都会马上协助完成订正工作。
但是,现在越来越多的项目,开发时间严重被压缩,在项目过程中,
1)业务逻辑本身就考虑不周全,没有考虑和牵连系统的关系,导致需求逻辑就存在问题;
2)为了减少开发人日,把本来该交给系统实现的需求,却考虑人工来完成,增加了人为误操作的发生概率;
3)为了减少开发成本,对接口行为不做逻辑验证处理,而接口错误参数,往往增加了系统错误数据的产生;
4)开发时间紧急,开发人员在不熟悉原有系统的基础上,就进行新功能的开发;过于过程式的开发;
系统、代码设计的时间过少;不敢做重够,等等,导致代码可读性很差,维护性不强,易出bug。
。。。。。。
由这些原因而造成的bug,形成的错误数据,我厌恶为其做数据订正:
不在源头做控制,一旦出了问题,才考虑到手工数据订正来暂时性的解决问题,这绝对不是一个好的项目团队的做法。
我们一直在宣称要做百年的企业,但是我们目前的系统,又能维持几年呢?
最具魅力圣火传递城市人气榜
在msn网站(
http://msn.ynet.com/eventmsnc.jsp?eid=38162866&cd=china)上有最具魔力圣火传递城市人气榜投票。身为绍兴人,看到自己的家乡排名考后,心有不甘,又看到温州等城市有人采用自动投票工具不断给自己刷票,于是乎,花费晚上一个小时的时间,用java(选用apache httpclient组件开发,比较方便。)也写了一个自动投票小软件,给绍兴投票。
本想在自己的机器上跑程序,但是考虑到不可能7*24小时运行,于是在网上找到一个jsp免费空间(
http://eatj.com),把投票工具部署成web app形式,放到eatj网上,借用人家的服务器,替绍兴投票 :)
通过
http://stone2083.s43.eatj.com/web/vote?action=query这个url,可以查看目前所有城市投票总数。
http://eatj.com,一直来提供了免费空间的服务,作为java初学者,可以拿这个空间练练手,或者作为小作品展示的地方。
20 MB space;
提供mysql服务;
提供tomcat5/tomcat6选择;
jdk5/jdk6选择
对于免费来说,已经是很好的服务了。
(但是对于免费空间,每隔6个小时,它会中断服务,需要手工启动下,这个有点恶心。)
今天参加了
第二届阿里巴巴网络侠客行大会。
由于最近加班比较多,早上贪睡不起,错过了侠客行上午场的会议,据说林斌—谷歌中国工程院副院长在讲话中介绍了google的一些技术,没听颇为可惜。
下午场的会议,我选择是“
分会场三 开放服务框架/Open Service framework”
第一场会议是林昊—淘宝网平台架构师带来的OSGI分享。
OSGI确实是一个很好的概念,很好的实现了模块动态化管理。试想一下,以后软件的功能可以像硬件一样,动态化插拔,那是多爽的一件事情。
Eclipse3版本,就是base on osgi的一个成功案例,用过eclipse开发的同学,一定对eclipse的plugin管理机制很心动,可以动态增加,删除插件。Eeclipse本身只提供了一个平台,功能都可以通过插件的方式增加。并且可以按照开发者的需求,增加插件。
据说jdk7版本,就会在语言级别上支持osgi,这则消息也是振奋人心。----目前jdk只有class(类),package(包)的概念,却没有module(模块)的概念,所谓模块化开发,仅仅是人工分割package的方式来实现。
概念是好,但是针对目前以有的功能,如何不伤筋动骨的完成base on osgi或者run on osgi,仍然是一个很大的一个问题。这也是很多企业对osgi仅仅停留在观望态度上的一个原因。
第二场会议是黄柳青—普元首席科学家、CTO分享的SCA--感觉他老人家英语说得比中文还好。
之前,我对SCA没有任何的了解,甚至连概念都没有听过。正好趁此分享机会,对SCA做个概念性的了解。
SCA(Service Component Architecture)
这玩意,究其本质,其实是对代码层面做了可视化组件的封装。他的概念是,把每个逻辑都看成是一个Component(组件/构件),然后根据不同的业务需要,去配置不同的Component,以及component之间的业务流。
其实,这个概念是很好的,尤其结合他天生的搭档OSGI,可以使得所有的开发者眼前一亮。
试想一下,以后有个系统(Base on OSGI),业务流程中,其中有个业务需求发生了变化,那么只需要开发者开发一个新的component,并且把原先的component动态uninstall,并且把新的component动态install,系统可以在运行期,就完成需求的变动。多爽。
概念是好,但是是否能流行,还需要时间的考验。
第三场会议是 袁红岗—金蝶中间件首席架构师 介绍
OperaMask,只是自己做这块内容没有兴趣,就换了会场,去了分
会场一 开放平台/Open Platform。
第三场会议赵进—/阿里软件首席架构师 介绍
Alisoft SAAS Platform
这小子年纪轻轻就当上了阿里软件的首席架构师,只有26岁,对他充满了敬意。努力向他学习。
这场分享,感觉只是很肤浅的介绍了阿里软件saas的平台。过程中更多的是讲了saas的概念和阿里软件saas的一些模式,没有涉及到技术细节层面的内容,比较失望。
SAAS的概念近年来逐渐流行起来。如何构建SAAS平台系统,是我最关注的点。比如:
如何发布开放API接口,
如何管理开放API接口,
如何对开放API进行测试,
如何确保开放API接口的安全性,
API接口采用什么技术调用,SAAS Platform是否统一规范对API的调用,采用什么方式传输数据,等等。
这些细节,都没有在这次分享中涉及到,太失望了。
整体来说,这次网络侠客行,还是让自己增长了不少见识,学到了不少技术。
希望阿里巴巴在接下去的几届中,能越办越好,更希望在技术交流会上,能出现更多国内技术的分享,期待国内软件业的发展 :)
eclipse安装插件的方式,常见的一般有3种:
1)把插件一股脑儿都扔到$ECLIPSE_HOME/plugins下面;
这是最方便的一种安装方式了,但是如果插件一多,就很难管理。如果想停用某几个插件,嘿嘿,没辙。。。
2)利用eclipse的manage configuration功能;
eclipse-->help-->software updates-->manage configuration
在这里,添加extension location(外部扩展点),把插件的地址一个一个添加进来。
这也是我一度使用的方法。可以比较方便的管理插件。
但是唯一不爽的,就是一旦eclipse重装,你就需要把这些扩展点一个一个添加进来,比较麻烦。
3)采用link方式安装插件
这是我目前最喜欢的一种安装eclipse plugins的方式。
在$ECLIPSE_HOME下,建立一个links目录。
links目录下,创建link文件(文件名和后缀可以随意指定),比如findbugs.link,内容如下:
path=/usr/software/eclipse_ext/plugins/findbugs
path后面跟的就是插件的地址。
需要注意的是,插件比如采用标准的目录结构
eclipse
|------plugins
|------features
采用link的方式,不但方便插件的管理,而且当eclipse重装的时候,只要把links目录copy到新的$ECLIPSE_HOME下即可。
ubuntu下,thunderbird是我首选的邮件管理工具(类似于windows下的outlook)。
用起来蛮爽,唯一不足就是没有日历功能,不能接受来自outlook的事件邀请。
幸好,thunderbird有个日历插件,
Lightning。刚好满足我的需求。建议使用。
放一张截图上来:
firefox是我首选的浏览器,但是启动速度实在不敢恭维。
从网上找了一些文章,修改了几个参数。
前提:地址栏输入 about:config 进入参数配置页面
首选项名称:config.trim_on_minimize,
类型:布尔
键值:true
作用:最小化时释放内存
备注:据说只在windows下有效(怪不得,我在linux下确实感觉没什么效果)
首选项名称:browser.sessionhistory.max_total_viewers
类型:整数
键值:0 (当然,你也可以设置成你需要缓存的页面数)
作用:前进/后退 功能,用于缓存页面数量
首选项名称:network.http.pipelining
类型:布尔
键值:true
作用:在http连接中,使用pipelining功能。据说能加速浏览速度
首选项名称:network.http.pipelining.maxrequests
类型:整数
键值:8 (据说上限是8)
作用:是一个实验性功能,加速浏览网站的速度。需要站点的支持。
首选项名称:nglayout.initialpaint.delay
类型:整数
键值:100 (默认值是250)单位是毫秒
作用:ff收到response后等待n毫秒,进行页面渲染
首选项名称:network.dns.disableIPv6
类型:布尔
键值:true
作用:禁用ip6功能(好像跟优化没什么关系)
首选项名称:browser.tabs.loadDivertedInBackground
类型:布尔
键值:true
作用:打开新tab时,停留在当前页面(跟优化没关系,只是符合我的浏览习惯)
关于这些首选项的意义,具体可以访问:http://kb.mozillazine.org/network.http.pipelining 得到相应参数的意义。
最后,做个广告,效果还是蛮明显的。推荐使用 :)
今天在一次会议中,有朋友问我,如何避免资源被迅雷等工具多线程下载?
确实,一些中小企业站点,尤其是个人站点,由于没有过多资金,服务器承受不了大的压力,站点提供的资源,一旦被迅雷等多线程工具下载,
对服务器的压力还是蛮客观的。
那么有什么办法避免多线程下载呢?其实最简单的办法,就是服务端根本就不要提供Content-Length值。试想一下,如果多线程下载工具得不到文件总大小值,如何分配去分配每个线程需要下载的量呢?不得已,只能通过单线程下载了。
以http下载为例,我写了一个提供下载的servlet,由于不返回Content-Length值(只返回了
ContentType值),这个serlvet返回的流,只能单线程下载。
public class Download extends HttpServlet {
private static final long serialVersionUID = 8401962046132204450L;
private static final String FILE_PATH = "/home/jones/tmp/sample.zip";
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/octet-stream");
OutputStream out = resp.getOutputStream();
FileInputStream in = new FileInputStream(FILE_PATH);
int readLength = 0;
byte[] cache = new byte[1024];
while ((readLength = in.read(cache)) != -1) {
out.write(cache, 0, readLength);
}
in.close();
out.flush();
out.close();
}
}
同样的道理,只要配置服务器不要返回Content-Length值,那么就可以有效避免多线程下载了。
摘要: CGlib概述:
cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
CGlib应用:
以一个实例在简单介绍下cglib的应用。
我...
阅读全文
常用的GC算法:
1)标记非活动对象
--何为非活动对象,通俗的讲,就是无引用的对象。
- 追踪root对象算法: 深度追踪root对象,将heap中所有被引用到的root做标志,所有未被标志的对象视为非活动对象,所占用的空间视为非活动内存。
2)清理非活动对象
- 方法:将内存分为两个区域(from space和to space)。所有的对象分配内存都分配到from space。在清理非活动对象阶段,把所有标志为活动的对象,copy到to space,之后清楚from space空间。然后互换from sapce和to space的身份。既原先的from space变成to sapce,原先的to space变成from space。每次清理,重复上述过程。
- 优点:copy算法不理会非活动对象,copy数量仅仅取决为活动对象的数量。并且在copy的同时,整理了heap空间,即,to space的空间使用始终是连续的,内存使用效率得到提高。
- 缺点:划分from space和to space,内存的使用率是1/2。
- Compaction算法:
- 方法:在清理非活动对象阶段,删除非活动对象占用内存,并且把活动对象向heap的底部移动,直到所有的活动对象被移到heap的一侧。
- 优点:无须划分from sapce和to space,提高内存的使用率。并且compaction后的内存空间也是连续分配的。
- 缺点:该算法相对比较复杂。
sun jdk gc介绍:
在减少gc之前,先来看看来自IBM的一组统计数据:
98%的java对象,在创建之后不久就变成了非活动对象;只有2%的对象,会在长时间一直处于活动状态。
如果能对这两种对象区分对象,那么会提交GC的效率。在sun jdk gc中(具体的说,是在jdk1.4之后的版本),提出了不同生命周期的GC策略。
- young generation:
- 生命周期很短的对象,归为young generation。由于生命周期很短,这部分对象在gc的时候,很大部分的对象已经成为非活动对象。因此针对young generation的对象,采用copy算法,只需要将少量的存活下来的对象copy到to space。存活的对象数量越少,那么copy算法的效率越高。
- young generation的gc称为minor gc。经过数次minor gc,依旧存活的对象,将被移出young generation,移到tenured generation(下面将会介绍)
- young generation分为:
- eden:每当对象创建的时候,总是被分配在这个区域
- survivor1:copy算法中的from space
- survivor2:copy算法中的to sapce (备注:其中survivor1和survivor2的身份在每次minor gc后被互换)
- minor gc的时候,会把eden+survivor1(2)的对象copy到survivor2(1)去。
- tenured generation:
- 生命周期较常的对象,归入到tenured generation。一般是经过多次minor gc,还 依旧存活的对象,将移入到tenured generation。(当然,在minor gc中如果存活的对象的超过survivor的容量,放不下的对象会直接移入到tenured generation)
- tenured generation的gc称为major gc,就是通常说的full gc。
- 采用compactiion算法。由于tenured generaion区域比较大,而且通常对象生命周期都比较常,compaction需要一定时间。所以这部分的gc时间比较长。
- minor gc可能引发full gc。当eden+from space的空间大于tenured generation区的剩余空间时,会引发full gc。这是悲观算法,要确保eden+from space的对象如果都存活,必须有足够的tenured generation空间存放这些对象。
- Permanet Generation:
- 该区域比较稳定,主要用于存放classloader信息,比如类信息和method信息。
- 对于spring hibernate这些需要动态类型支持的框架,这个区域需要足够的空间。
这部分内容相对比较理论,可以结合jstat,jmap等命令(当然也可以使用jconsole,jprofile,gciewer等工具),观察jdk gc的情况。