2010年7月25日
#
D5:ema(close,5),coloryellow;
D10:ema(close,10),colorwhite;
工作线15:ema(close,15),colorred,linethick2;
D20:ema(close,20),colorblue;
D30:ema(close,30),colorgreen;
D60:ema(close,60);
老庄线140:ema(close,140);
D280:ema(close,280);
2010年2月28日
#
使用DefaultTableCellRenderer改变JTable的默认渲染
要想改变Jtable的默认显示状态,如由默然的文本显示改变为图片,组件等,你只需要从DefaultTableCellRenderer继承出一个自己的实现类,在其中根据行列值等返回特定的渲染组件即可,当然这个渲染组件必须是Component的子类。完成之后,可以通过如下的方式将这个渲染器施加于JTable上。
JTable table=new JTable();
.
TableCellRenderer render=new MyTableCellRenderer();
try{
table.setDefaultRenderer(Class.forName("java.lang.Object"), render);
}
catch(Exception ex){
ex.printStackTrace();
}
下面是一个例子:
class PictureTableRender extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Component cell = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if(column==4){
String url=(String)value ;
PicturePanel p=new PicturePanel();
p.showPicture(url);
return p;
}
return cell;
}
}
PictureTableRender说明
这个渲染器判断当前列的下标,如果是4则使用一个面板代替默认的cell返回,这个面板会取得这个单元格的值—一张网络图片的URL,然后在自身上描绘出来。效果如下:
加上渲染器的效果图
最后对表格做的一点特殊处理
Jtable默然的列是可拖动的,如果是这样我们的渲染器就会失去作用,这时采用table.getTableHeader().setReorderingAllowed(false);
让列不可拖动就可以。
当然也可以在渲染器中通过列得到TableHeader,再取得表头的值,如果判断是“照片”二字的话再进行渲染,这部分工作就留待读者完成吧。
2010年2月27日
#
通过WebService上传文件的原理
我们都知道如何通过WebService把一个字符串形式的参数传递到服务器端的一个函数并取得返回的结果,而通过WebService上传文件的原理和上传一个字符串在根本上是一样的。
唯一不同的是,我们需要多做一点额外的工作,即先读取文件到一个字节数组中,再通过Base64将其转化为字符串。详情请看下面的代码:
// 客户端读取文件然后用Base64将其转化为字符串的函数
private static String getFileByteString(File file) throws Exception{
InputStream in = new FileInputStream(file);
// 取得文件大小
long length = file.length();
// 根据大小创建字节数组
byte[] bytes = new byte[(int) length];
// 读取文件内容到字节数组
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// 读取完毕的校验
if (offset < bytes.length) {
throw new IOException("不能完全讀取文件:"+ file.getName());
}
in.close();
String encodedFileString = Base64.encode(bytes);
return encodedFileString;
}
服务器端如何将接收到的字符串还原成文件
有了上页函数的帮助,我相信你将它传递到WebSercvice端某函数是必能做到的事,剩下的问题是,如何将接收到的字符串还原成文件呢?
答案是再用Base64将字符串还原成字节数组再将它写入一个文件即可,这样写出来的文件能保证内容和你上传的原文件一致,下面是示例程序:
WebService服务器端将接收来的字符串还原的文件的过程
// uploadedFileString是传过来的包含你上传的文件内容的字符串
byte[] bytes = Base64.decode(uploadedFileString);
// 存储路径
String path=CommonUtil.getUploadPath();
// 存储的文件名
String localFileName=getLocalFileName(parser.getUserid(),parser.getFileName());
// 写入新文件
FileOutputStream out = new FileOutputStream(path+localFileName);
out.write(bytes);
out.flush();
out.close();
客户端如何访问已上传的文件
上传只是手段,我们上传的真正目的其实是下载,就我们刚才上传的文件而言,如何能让人访问到它呢?我们可以如下办理:
1.将上传文件书写在WebService所在Web应用下的某目录中,如upload"1.jpg,这样客户就可以通过这样的URL访问到这个文件http://209.205.177.42:8080/webApp/upload/1.jpg. 上面IP地址是WebSercice应用所在机器的公网地址,webApp是该应用名。
2.在客户端上传文件完毕后,将上述地址以函数返回值的形式告知客户,客户就可以通过网络来访问它了。
如何得到WebApp下的upload目录
书写一个在WebApp启动时就启动的Servlet,在其init函数就能得知Webapp所在目录,得到upload目录再往下走一层就行了。下面的InitServlet的示例代码:
public class InitialSystemServlet extends HttpServlet {
private static final long serialVersionUID = -7444606086930580188L;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, java.io.IOException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, java.io.IOException {
doPost(request, response);
}
public void init(ServletConfig config) throws ServletException {
// 设置上传路径
CommonUtil.setUploadPath(config.getServletContext().getRealPath("/"));
}
}
其它问题
1.如何防止文件被覆盖:在生成文件时采用时间+用户ID+随机数的文件名,这样重名几率就大大降低,还不放心可以在写文件之间检验文件是否已存在了。
2.如何要把文件不放在服务器而是放到数据库怎么办:你可以把文件内容甚至字符串直接存储到数据库,需要下载时再取出来。
2010年2月26日
#
当图片上传到服务器端的某目录下后,假设是webapp/upload,图片名为1.jpg,那么我们可以使用这样的url来访问到它:
http://localhost:8080/webapp/upload/1.jpg,如果要在Swing程序中显示它,将它交给一个ImageICon类进行处理,得到其image后在面板上绘制即可。
import java.awt.Graphics;
import java.awt.Image;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
/**
* 用于显示图片的面板
*/
public class PicturePanel extends JPanel{
private static final long serialVersionUID = 64343434343L;
// 用于展示图片
private Image pictureImage=ResourceUtil.SplashWindow_BackgroundImage;
@Override
public void paintComponent(Graphics g) {
if(pictureImage!=null){
g.drawImage(pictureImage, 0, 0, getWidth(), getHeight(), 0, 0, pictureImage
.getWidth(null), pictureImage.getHeight(null), null);
}
}
/**
* 用于显示本地图片
*
* 说明:
* @param pictureImage
* 创建时间:2010-2-24 上午08:22:47
* 修改时间:2010-2-24 上午08:22:47
*/
public void showPicture(Image pictureImage){
this.pictureImage=pictureImage;
// 进行重绘
repaint();
}
/**
* 用于显示网络图片
*
* 说明:
* @param pictureUrl
* 创建时间:2010-2-25 下午10:37:57
* 修改时间:2010-2-25 下午10:37:57
*/
public void showPicture(String pictureUrl){
try {
pictureImage= new ImageIcon(new URL(pictureUrl)).getImage();
// 进行重绘
repaint();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2010年2月25日
#
预想的交易系统要做到什么?
1.信息传递方面--确保供求信息准确迅速的到达需求方。
2.信息展示方面--能充分展示商家的供给和需求。
3.信息交流方面--充分保证供求双方信息的传递。
4.信息搜索方面—使别人能够准确快速找到商家,了解商家。
四大能力被那些软件实现了?
1.信息传递方面—动态供求信息网站,大站点的广告。
2.信息展示方面—企业动态静态网站,淘宝,当当.
3.信息交流方面—QQ,MSN,Skype.
4.信息搜索方面—google/baidu.
我们的交易系统初步目标
淘宝+QQ
淘宝能展示商品,但只是被动等客上门的形式,缺乏主动性;淘宝可以进行交流,但受限于网络形式.
QQ能迅速传递信息(仅聊天),形式也较丰富,其形式风格适合我们的需求.
淘宝和QQ已经被广泛接受,如果我们各取所长,对用户来说容易接受.
我们的交易系统远景目标
除了签订纸面合同和面谈,进行交易所需要的步骤都可以在交易系统中运行.
供求信息可以直接通过系统传递.
产品展示在系统内就可完成,系统负责其网站的同步.
谈判交流方式有文字,语音,视频等多种.
信息搜索像使用搜索引擎一样简便,但更准确.
提供类比,同好,相似联系等帮助手段.
交易系统各程序组成
交易系统各程序说明
NC:最终用户使用的客户端程序.
WS:为用户存取业务数据的WebService服务器
IM:为客户端之间及服务器客户端之间提供即时信息服务的程序
Admin:管理业务数据及广播即时消息的程序.
DB:数据管理程序
交易系统各程序联系说明
1:客户端NC之间可以通过TCP/UDP进行即时通信,此项无须通过IM服务器中转.
2.3:客户端NC定时向即时信息服务器端IM索取信息,即时信息服务器IM也可主动将信息传递到客户端NC,此项也是通过的TCP/UDP.
4:客户端程序NC可以通过WebService调用向业务数据服务器WS存取信息.
5:系统管理程序Admin和WebService服务器可以向信息服务器发出广播信息.
6:系统管理程序Admin可以直接处理数据库中的数据.
7:业务数据服务器WS通过Hibernate和存储过程操作数据库中的数据.
8.数据库管理程序DB通过JDBCTemplate及存储过程操作数据库中的数据.
2010年2月24日
#
1.目录(Folder):用以容纳供求信息。它包含对父目录的引用,子目录的集合和所包含的供求信息的集合。
2.供求信息(SupplyDemandInfo):表示对商品的供应或是需求信息,供求信息=求购者+商品+数量+地点+有效截止日期。
3.商品(Commodity):用以容纳对一个商品的描述信息,包括名称,规格,产地,厂家,描述等。
4.用户(User):登录到系统的用户。
5.公司(Company):这是用户的附加信息。
【原文】
无道人之短,无说己之长。
施人慎勿念,受施慎勿忘。
世誉不足慕,唯仁为纪纲。
隐心而后动,谤议庸何伤?
无使名过实,守愚圣所臧。
在涅贵不缁,暧暧内含光。
柔弱生之徒,老氏诫刚强。
行行鄙夫志 ,悠悠故难量。
慎言节饮食,知足胜不祥。
行之苟有恒,久久自芬芳。
【译文】
不要津津乐道于人家的短处,不要夸耀自己的长处。施恩于人不要再想,接受别人的恩惠千万不要忘记。世人的赞誉不值得羡慕,只要把仁爱作为自己的行动准则就行了。审度自己的心是否合乎仁而后行动,别人的诽谤议论对自己又有何妨害?不要使自己的名声超过实际,守之以愚是圣人所赞赏的。洁白的品质,即使遇到黑色的浸染也不改变颜色,才是宝贵的。表面上暗淡无光,而内在的东西蕴含着光芒。老子曾经告诫过:柔弱是有生命力的表现,而刚强和死亡接近。庸鄙的人有刚强之志,时间久远,他的祸更重。君子要慎言,节饮食,知足不辱,故能去除不祥。如果持久地实行它(上面提到的),久而久之,自会芳香四溢。
2010年2月23日
#
有查询必有分页,天下程序无论C/S还是B/S都是如此。只要有分页就需要用到分页标签,在网页中可以用span或是div来实现,而在Swing中,我们可以从JLabel中扩展出一个自己的页标签类来实现它。
首先看看这个页标签类的外在表现是怎样的:
1.鼠标移动上去高亮,移出来恢复成灰色或是黑色。
2.鼠标点击时能产生事件响应,即产生翻页效果。
3.当标签处于失能(disable)状态时,上述两个效果无效。
基本就是这样,要求不高,下面是我们的应对之策:
1.让页标签实现MouseListener接口,当mouseEntered时让标签前景色变红,当mouseExited时让标签前景色变黑。
2.在页标签内部制作一个成员变量List<ActionListener> actionListenerList,再实现一个addActionListener方法,这样,外界的监听者就能被加入到这个链表中。同样是理由上面的接口,当mouseReleased(mouseClicked事件也可)时让所有的监听者的actionPerformed事件触发,这样,事件响应就能完成了。
3.在鼠标事件产生时(mouseEntered,mouseExited,mouseReleased),检查标签的enable状态,是false则不进行处理。
下面请看详细代码:
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.Vector;
import javax.swing.JLabel;
/**
* 用于分页的标签,鼠标移动上去变色
* 说明:
*
* 创建时间:2010-2-22 下午01:35:56
* 修改时间:2010-2-22 下午01:35:56
*/
class PagedLabel extends JLabel implements MouseListener{
private static final long serialVersionUID = 3136812079192762420L;
// 监听者列表
private List<ActionListener> actionListenerList;
/**
* 构造函数
* @param text
*/
public PagedLabel(String text){
super(text);
this.setForeground(Color.black);
actionListenerList=new Vector<ActionListener>();
this.addMouseListener(this);
}
/**
* 添加事件监听器
*
* @param actionListener
*/
public void addActionListener(ActionListener actionListener){
actionListenerList.add(actionListener);
}
/**
* 鼠标移入时变成红色
*/
public void mouseEntered(MouseEvent e) {
if(this.isEnabled() && this.isVisible()){
this.setForeground(Color.red);
}
}
/**
* 鼠标移出时变成黑色
*/
public void mouseExited(MouseEvent e) {
if(this.isEnabled() && this.isVisible()){
this.setForeground(Color.black);
}
}
/**
* 在点击松开后通知所有监听者
*/
public void mouseReleased(MouseEvent e) {
if(e.getButton()==1 && this.isEnabled() && this.isVisible()){
for(ActionListener actionListener:actionListenerList){
actionListener.actionPerformed(null);
}
}
}
public void mousePressed(MouseEvent e) {
// do nothing
}
public void mouseClicked(MouseEvent e) {
// do nothing
}
}
使用这个标签的示例代码如下:
PagedLabel refreshBtn=new PagedLabel("刷新");
refreshBtn.addActionListener(new ActionListener() {
@SuppressWarnings("deprecation")
public void actionPerformed(ActionEvent e) {
pageSearch(currentPage,url,serviceName,methodName,args,cls);
}
});
这里赘述一下,虽然也可以再次捕获标签的鼠标按下抬起事件作为点击的响应,但上面做法无疑减少了外部代码的复杂度和使用的难度,将他们降低到了使用按钮的层次。简单方便实用,这是每一个框架组件功能的设计者应该为客户程序员着想的地方。在多人合作的环境中,帮助别人就等于帮助自己。
最后分页的效果请看:
java.lang.NoClassDefFoundError: net/sf/cglib/proxy/CallbackFilte,是因为缺乏Cglib.jar中类的支持所致,将其加载到lib中即可。
2010年2月22日
#
在上一篇“
交易系统中WebService服务器端缓存的设计”中,有一个潜在的问题,请看下面代码:
if(mothodName.contains("add") || mothodName.contains("update") || mothodName.contains("delete") ){
// 写方法来了,这意味着数据变更了,缓存可能不可靠,为安全起见需要重新来过
..
}
else{
// 来的是读方法
.
}
上面的判断里,对写方法的判断作成了硬编码的形式,这对系统可不是件好事,我们可以用Java1.5中新生的Annotation把它消除掉。
首先,我们可以制作一个自己的标注:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用于标识方法对缓存影响的标注,如果isRead=true表示这个方法是读方法,需要缓存;否则是写方法,需要清空缓存
* 说明:
*
* 创建时间:2010-2-22 上午08:54:54
* 修改时间:2010-2-22 上午08:54:54
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadWriteAnnotation{
boolean isRead();
}
这个标注可以用来注释方法,在程序运行期有效,我们可以在标注方法时给isRead方法附上true和false两个值,这样,具体方法是读方法(查询数据库中的一条或者多条记录)还是写方法(更新数据库的一条或者多条记录)就能通过它标识出来了。
下面是做出了标识的接口ITmpService:
import com.dalpha.service.anno.ReadWriteAnnotation;
/**
* TmpService实现类之接口
*
* 创建时间:2010-2-19 下午10:04:34
* 修改时间:2010-2-19 下午10:04:34
*/
public interface ITmpService{
/**
* 解析参数数组,组合成一个领域对象,然后添加到数据库(写方法)
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=false)
public String add(String[] args) throws Exception;
/**
* 解析参数数组,更新领域对象的一个或多个属性,然后更新数据库中的对应记录
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=false)
public String update(String[] args)throws Exception;
/**
* 解析参数数组得到要删除的领域对象的Id,然后根据它删除数据库中的对应记录
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=false)
public String delete(String[] args) throws Exception;
/**
* 解析参数数组得到要取得的领域对象的Id,然后根据它渠道数据库中的对应记录
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=true)
public String getById(String[] args) throws Exception;
/**
* 按条件进行分页查询
* 注意这里的条件最好写全,最好根据数组内容走不同的分支,不要写各种各样的查询函数,这样不方便缓存的处理
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=true)
public String pagedSearchBy(String[] args) throws Exception;
/**
* 按条件进行查询,除了不分页要求和上面函数(pagedSearchBy)一致
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=true)
public String search(String[] args) throws Exception;
/**
* 按ID取得信息
*
* @param args
* @return
* @throws Exception
*/
@ReadWriteAnnotation(isRead=true)
public String getInfoById(String[] args) throws Exception;
}
对于实现类我们是不需要标识的,这也是使用接口编程的好处之一。
剩下来,我们在拦截器中取出标识并判断即可,请看下面的代码的粗体部分:
/**
* Service方法的拦截器
* 此拦截器用作缓存使用,每个Service配置一个。
*
* 创建时间:2010-2-19 下午10:42:42
*
* 修改时间:2010-2-19 下午10:42:42
*/
public class ServiceMethodInterceptor implements MethodInterceptor{
// 日志记录器
private static Logger logger = Logger.getLogger(ServiceMethodInterceptor.class);
// 作为缓存的哈希表
private Map<String,Object> cacheMap=new Hashtable<String,Object>();
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String className=invocation.getClass().getName();
String mothodName=invocation.getMethod().getName();
logger.info("类'"+className+"'的方法'"+mothodName+"'将得到调用!");
// 取得在方法上的标注
ReadWriteAnnotation anno=invocation.getMethod().getAnnotation(ReadWriteAnnotation.class);
if(anno.isRead()==false){
// 写方法来了,这意味着数据变更了,缓存可能不可靠,为安全起见需要重新来过
cacheMap.clear();
Object result=invocation.proceed();
logger.info("类'"+className+"'的方法'"+mothodName+"'调用完毕!");
return result;
}
else{
// 来的是读方法
// 通过组合方法名和参数来得到key
StringBuffer sb=new StringBuffer();
sb.append(mothodName+";");
Object[] arr=invocation.getArguments();
String[] arr2=(String[])arr[0];// 这一步的转化是很重要的
for(Object obj:arr2){
sb.append(obj+",");
}
String key=sb.toString();
// 拿Key查看缓存中是否有内容,有则直接返回即可
if(cacheMap.containsKey(key)){
logger.info("直接得到缓存中的结果!");
return cacheMap.get(key);
}
else{
Object result=invocation.proceed();
cacheMap.put(key, result);
logger.info("类'"+className+"'的方法'"+mothodName+"'调用完毕!");
return result;
}
}
}
}
这样,判断方法是读还是写的硬编码就消除了。虽然说花费了一些工夫,但“勿以恶小而为之,勿以善小而不为”,只要是对系统有利的事,我们还是要去做的,只要对系统不利的事,我们必须多家防范,小心杜绝。