| 宇凡's profileBreak the LoopPhotosBlogLists | Help |
|
|
Break the LoopJune 23 ORM单元测试技巧A variety of bugs can lurk in the O/R mapping, including the following:
http://www.theserverside.com/tt/articles/article.tss?l=PersistentDomain May 22 使用mencoder/ffmpeg处理Flash Video的几个常用命令将一下代码分别保存为flv_43.bat, flv_169.bat, screentshot.bat即可,当然,你得有最新版的mencoder/ffmpeg及相关视频码包。
一、压缩4:3的视频
mencoder.exe %1 -srate 22050 -oac mp3lame -lameopts br=32 -ovc lavc -lavcopts vcodec=flv:vbitrate=240 -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -ffourcc FLV1 -of lavf -vop scale=320:240 -o %2
二、压缩16:9的视频,自动加上下黑框
mencoder.exe %1 -srate 22050 -oac mp3lame -lameopts br=32 -ovc lavc -lavcopts vcodec=flv:vbitrate=240 -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -ffourcc FLV1 -of lavf -vf scale=320:180,expand=320:240 -o %2
三、截图
ffmpeg -i %1 -ss 3 -vframes 1 -r 1 -ac 1 -ab 2 -f image2 %2 一段使用正则表达式提取中文的代码 String f = "abakdia中文akakji ji 你好 釹號"; Pattern p = Pattern.compile("[\\u4E00-\\u9FFF]+"); Matcher matcher = p.matcher(f); while (matcher.find()) { System.out.println(matcher.group()); } May 18 使用AOP及Annotation实现半自动实质化Hibernate Entity熟悉Hibernate的朋友应该都知道,Hibernate支持对集合的懒加载(最新版也支持属性的懒加载),但这样的懒加载在一些分层的架构会带来一些问题,如果只是在Web中应用,可以通过使用OSIV来保持Hibernate Session直到view渲染完毕,但在分布式的应用中,这显然不是一个合理的方案,甚至在一些严禁的web应用中,我们也不应该使用OSIV(原因可参看Without EJB中相关章节),这时候,又要依赖懒加载提高性能,又想将Hibernate Entity传递到Service层之外的时候,就会出现很多代码来做一件事情:
Hibernate.initialize(collection), 这样的与业务逻辑无关的代码实在是一种臭味。
我想到的一个办法是结合使用AOP和Annotation来半自动化的完成这件工作:
1、创建一个指导AOP拦截器工作的Annotation,主要用来告诉拦截器应该实质化哪一部分属性
/**
* @author Yufan Shi */
package yufan.annotation; import java.lang.annotation.Retention;
import java.lang.annotation.Target; /**
* Use this Annotation to mark sevice methods which wanna its returned result to * be materialized(initialize lazy load hibernate collections). * * <p> * <b>e.g:</b> * * @Materialize(select = { "suburbs" }) <br > * @Materialize(select = { "#root.{suburbs}" }) * </p> * * Place this annotation in service interfaces is recommended. * * @author Yufan Shi * @see yufan.aop.interceptor.MaterializeEntityInterceptor */ @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Target( { java.lang.annotation.ElementType.METHOD }) public @interface Materialize { /**
* <p> * OGNL expressions to select properties from target entity.<br> * For example:<br> * one method like this was marked by this annotation:<br> * <b>City getCity(String id);</b><br> * and City got a suburbs list which is lazy loaded. By using select * {"suburbs"}, you are telling * yufan.aop.interceptor.MaterializeEntityInterceptor to materialize this * collection after getCity method returned. * * another method like this:<br> * <b>List findAllCity();</b><br> * and City got a suburbs list which is lazy loaded. By using select * {"#root.{suburbs}"}, you are telling * yufan.aop.interceptor.MaterializeEntityInterceptor to materialize suburbs * collection of every city of the list returned by findAllCity method. * The same thing works works both on City[] and Map. * </p> */ String[] value(); } 2、创建一个拦截器,来拦截业务层方法,这些方法应该返回Hibernate Entity,以及Entity的数组、List或者Map.拦截器拦截到方法后,会考察该方法是否有@Materialize注解,并且从中得到要实质化的属性OGNL表达式,根据该表达式结合OGNL,得到一批要实质化的对象,然后使用Hibernate.initialize一一实质化。
/**
* @author Yufan Shi */ package yufan.aop.interceptor; import java.util.ArrayList;
import java.util.Collection; import java.util.List; import ognl.Ognl;
import ognl.OgnlContext; import ognl.OgnlException; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.hibernate.Hibernate; import org.hibernate.SessionFactory; import yufan.annotation.Materialize;
/**
* MaterializeEntityInterceptor * * This interceptor will Materialize your hibernate entity's lazy loaded * collection properties returned by methods intercepted by it * * @author Yufan Shi (yufanshi@gmail.com) * @see yufan.annotation.Materialize */ public class MaterializeEntityInterceptor implements MethodInterceptor { private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory; } public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory; } /*
* (non-Javadoc) * * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) */ public Object invoke(MethodInvocation methodInvocation) throws Throwable { Materialize materializeConfig = methodInvocation.getMethod() .getAnnotation(Materialize.class); Object r = methodInvocation.proceed(); if (materializeConfig != null) { for (String expression : materializeConfig.value()) { for (Object o : selectObjects(r, expression)) { Hibernate.initialize(o); } } } return r; } private List selectObjects(Object result, String expression) {
List<Object> list = new ArrayList<Object>(); try {
OgnlExpression ognl = new OgnlExpression(expression); Object value = ognl.getValue(new OgnlContext(), result); if (expression.endsWith("}")) { list.addAll((Collection<? extends Object>) value); } else { list.add(value); } } catch (OgnlException e) { e.printStackTrace(System.err); } return list; } }
class OgnlExpression {
private Object expression; public OgnlExpression(String expressionString) throws OgnlException {
super(); expression = Ognl.parseExpression(expressionString); } public Object getExpression() {
return expression; } public Object getValue(OgnlContext context, Object rootObject)
throws OgnlException { return Ognl.getValue(getExpression(), context, rootObject); } public void setValue(OgnlContext context, Object rootObject, Object value)
throws OgnlException { Ognl.setValue(getExpression(), context, rootObject, value); } } 3、在Service接口中需要实质化返回值的方法声明上放置@Materialize注解,示例如:
package test.business.service;
import java.util.List;
import java.util.Map; import test.entity.City;
import yufan.annotation.Materialize; public interface IEchoService {
String echo(String msg); @Materialize( { "suburbs" }) // 实质化city的suburbs属性
City getRandomCity(); @Materialize( { "#root.{suburbs}" }) // 实质化city数组中每个city元素的suburbs属性
City[] findCities(); @Materialize( { "#root.{suburbs}" })// 实质化city列表中每个city元素的suburbs属性
List findCityList(); @Materialize( { "#root.{suburbs}" })// 实质化city图value集合中每个city元素的suburbs属性
Map findCityMap(); } 4、在Spring中定义拦截器及其他
<bean id="materializeIntercpetor"
class="yufan.aop.interceptor.MaterializeEntityInterceptor" autowire="byName" /> <bean name="echoServiceTarget"
class="test.business.service.EchoServiceImpl" autowire="byName" /> <bean id="echoService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>test.business.service.IEchoService</value> </property> <property name="interceptorNames"> <value>transactionInterceptor,materializeIntercpetor,echoServiceTarget</value> </property> </bean> 5、如果已经使用OSIV,去掉OSIV在web.xml中的定义,然后尝试在试图中遍历suburbs集合,就会发现不再出现LazyInitializeException了
这个功能能减少很多的重复的业务无关的代码,但是还是由于一点不足,就是必须注解在接口中,这个感觉有点侵扰了接口的单纯性。但我尝试在实现类里加注解却发现无法在interceptor里的通过反射得到注解。
从唯一字符串得到散列性较好的文件路径在一些大型系统中,会经常在硬盘上存储文件,比如存储用户的头像,如果将所有文件存在一个目录则可能造成文件系统性能低下,甚至崩溃。比较好的做法是利用一定的算法根据唯一的值得到一个散列性较好的文件路径,将文件存放在该路径。
以下这个类使用md5得到一串32字节的hash字符串,用该字符串的第一个字符作为根目录,然后将字符串2个一组切分,最后两个字符串作为文件名。
这样的情况下,可以将所有的文件分布在16个大目录下的最底层,每个目录最多只有256个子目录。
可以得到类似这样的文件路径:
e\e4\e6\9e\5b\1e\65\48\e7\49\61\51\9b\45\31\d2\1c.jpg
/**
* @author Yufan Shi */ package com.jongo.account.service.impl; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import com.jongo.account.service.MassFilePathSelector;
/**
* 使用MD5算法得到散列性好的文件路径 * * @author Yufan Shi * */ public class MD5MassFilePathSelector implements MassFilePathSelector { private MessageDigest md5; public MD5MassFilePathSelector() {
try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } /*
* (non-Javadoc) * * @see com.jongo.account.service.MassFilePathSelector#select(java.lang.String, * java.lang.String, java.lang.String) */ public String select(String uniqueName, String ext) { byte[] hex_digest = new byte[32]; md5.update(uniqueName.getBytes()); bytesToHex(md5.digest(), hex_digest); String digest = new String(hex_digest); StringBuffer sb = new StringBuffer(); sb.append(digest.substring(0, 1)); for (int i = 0; i < digest.length(); i += 2) { sb.append("/"); sb.append(digest.substring(i, i + 2)); } sb.append("." + ext); return sb.toString(); } /**
* Turn 16-byte stream into a human-readable 32-byte hex string */ private static void bytesToHex(byte[] bytes, byte[] hex) { final char lookup[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int i, c, j, pos = 0;
for (i = 0; i < 16; i++) {
c = bytes[i] & 0xFF; j = c >> 4; hex[pos++] = (byte) lookup[j]; j = (c & 0xF); hex[pos++] = (byte) lookup[j]; } } }
比较简单且质量较高的图像缩放算法 // 构造Image对象 // 绘制缩小/放大后的图 thumbnail.getGraphics().drawImage(scaled, 0, 0, null); File out = new File("thumbnail.jpg"); // JPEG编码 May 14 适合大型网站的j2ee开发流程思考网站特别是大型网站的重要特点是,页面需求变化特别快,特别频繁,经常会调整页面或者新增页面,而很多页面也经常会被无情的丢弃,因此按照传统的j2ee开发流程似乎无法适应这种快速变化的环境。
一直在思考这样一个东西,既能照顾j2ee重视结构、重视设计的传统,又能应对频繁的界面变化。
这个东西应该是结合了技术、管理等诸多方面才能正常运转的。
1、产品经理、开发人员、页面设计人员、页面制作人员碰头会,多次,探讨网站结构及页面风格。输入来自产品经理的需求,开发输出系统概要设计,设计输出网页整体风格设计,制作输出静态网站原形(xhtml+简单样式+js效果,基本无图,可导航,可套用其他产品的风格)。
2、经审核通过后,制作和设计确定页面结构,制作和开发确定各个页面所需的数据和页面之间导航流程。
3、web层开发人员和业务层开发人员确定接口,web开发开始编写页面action,业务层开始开发接口即实现,初期web开发可能需要使用一些伪装数据,业务层需要编写单元及功能测试。
4、web层提交新的action后,由制作人员负责将action和界面整合,提交给测试组及产品经理做用户测试等,测试的反馈结果将反馈给web开发人员及制作人员。
5、业务层提交新的接口实现后,由web开发整合到action,并且提交给测试组及产品经理做用户测试等,测试结果反馈给业务开发人员。
6、设计人员提交新的页面设计后,由制作人员切割并且利用css等整合到界面,交给产品经理及测试组做测试,测试结果反馈给制作人员及设计人员。
整个过程是迭代进行,各组人员都能并些行工作,由测试组及产品经理提交反馈意见,反馈意见会及时反馈给具体负责人,并且和原有的开发计划协调后加以处理。 昨天测试了Tapestry里使用Hibernate PO,果然果然,当我将第一个页面中的City属性通过DirectLink传递给ShowCity页面后,当使用For组件显示City下的Suburb列表的时候,出现了LazyLoad异常。
不过,也发现Tapestry的DirectLink似乎是把整个业务对象都encode到url里,不知道具体的实现如何,还有这样的link要是被用户bookmark了,下次访问的时候又会怎么样?似乎这样,即使使用了Long Conversation也没办法保证用户下次从收藏夹返回的时候,这个hibernate session还在,这样自然还是无法正确地得到他所需要的数据,照样会出异常。
看来要想直接将Hibernate Entity传递到页面层已经不是一个正确的选择,似乎还应该使用DTO或者在使用session.lock()来重新attach实体对象。
不过,session.lock向界面层透露了太多的底层信息,并且将web层和hibernate紧密地耦合在一起,自然不是一个好主意,这里DTO不应该再被当成反模式了,还是应该适当的使用。 May 13 Tapestry学习笔记第一篇1、Tapestry不是面向操作的、而是面向组件的。要理解tapestry,首先得理解组件对象模型(component object model),以及这段话:
The "mantra" of Tapestry is "objects, methods and properties". That is, rather than have developers concerned about the paraphanlia of the Servlet API: requests, responses, sessions, attributes, parameters, URLs and so on, Tapestry focuses the developer on objects (including Tapestry pages and components, but also including the domain objects of the application), methods on those objects, and JavaBeans properties of those objects. That is, in a Tapestry application, the actions of the user (clicking links and submitting forms) results in changes to object properties combined with the invocation of user-supplied methods (containing application logic).
2、Tapestry虽然让web开发变得和桌面应用开发十分相似,但由于HTTP协议本身的限制,还是有很多特殊的地方,有时候要结合两方面的经验来理解。
3、在Tapestry中,一个Web APP由若干Page组成,而Page由若干组件组成,组件还可以包含其它组件,并没有层次的限制。Tapestry引擎会维护一个Page对象池,用户的每次请求都会从池中取得一个对象来处理。但正如前面所说,Tapestry的Page对象都是可以拥有状态,即你可以在Page类中定义属性,Tapestry4中的做法是定义一对abstract的getter/setter,然后利用Annotation指定属性的状态维持机制,如
@Persist 默认在session中维持属性状态
@Persist("client") 在client中维持属性状态,一般是把属性编码后存储在请求url中,不是很安全,也可能会造成集群环境中状态不一致(这里不是很理解具体原因,可能是我理解错误)。
如果不指定@Persist,则必须由开发人员负责做好属性的初始化工作,一般是通过PageBeginRenderListener,然后在pageBeginRender事件中初始化,比如常见的做法是从业务层获取数据对象。注意,如果不做好初始化工作,可能会造成程序的行为非常怪异。
个人觉得Tapestry中的属性和生命周期还是不太好理解,可能需要多做点试验,才能更好的理解。比如,定义一个具体的java bean属性,或者定义一个getXXX()方法,实际是从业务层得到数据对象,是不是效果和前面的方法一样?
4、另外,利用H.L.S另外的一个在JavaForge上的Tapestry-Spring项目已经非常方便在页面注入业务层对象了,只需要定义个抽象的getXXX()方法,然后使用@InjectSpring("xxx")就能注入了。 5、另外和Hibernate结合方面,总感觉没有找到好的办法,如果仅仅依靠OSIV,据说将PO定义为页面的属性,然后通过DirectLink传递给其他的页面使用时(是的,不用怀疑,Tapestry里可以在页面之间直接传递对像,而不需要你去重新组装对象,是不是感觉很OO?),会造成LazyLoad方面的异常,初步总结有三种办法,第一种是tapestry上提供的方法,但不太优雅,后两种是我自己在思考的,没有论证过:
1、在pageBeginRender里,从新attach实体,使用session.lock(obj)
2、利用DTO?, 虽然多了一层,但可能会省掉很多隐藏的问题。
3、不使用OSIV,改为使用Long Session per conversation,这样用户的整个会话周期内都使用同一个hibernate session,就不会有这个问题。
我思考的两种方法,前一种是将hibernate session的作用边界缩小到了业务层方法,往往是一个事物一个session,第二种是扩大到整个会话,一个会话一个session.不过,long conversation的方法使用起来比较复杂。如果不想让web层得到太多底层的细节,可能还是DTO比较好。 May 12 Google Trends, 真有趣!Allen发给我这个东西,真有趣,可以分析对比很多词汇的流行程度。
来几个对比玩玩:
哈哈,看来我选择学习tapestry还是正确的
哪个牛一点呢?yahoo的用户群还是很大的,别小看
老外常用的单词是什么呢? 这回在决定用哪个单词才地道的时候有依据了 |
||||
|
|