系统集成

更新时间:2024-05-27 05:45:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

多线程的环境中,尽量采用线程池

分类: SOCKET 2009-04-21 20:11 766人阅读 评论(0) 收藏 举报

我个人比较喜欢线程,因为线程可以同时并发处理多个请求,于是乎我在编写线程的总是以如下方式启动:

[java] view plaincopyprint?

1. ...

2. while(true){

3. server.clientSocket=server.serverSocket.accept(); 4. if(server.clientSocket!=null) 5. {

6. Thread thread=new Thread(new MsgListener(server.clientSocket)); 7. thread.start(); 8. 9. } 10. } 11. ...

...while(true){server.clientSocket=server.serverSocket.accif(server.clientSocket!=null){Thread thread=new Thread(new MsgListhread.start();}}... 没有错吗,是不是很爽,如果有多少个请求,就产生多少个线程,请像在门口待客的接待员一样,让每个客人都马上得到处理,程序绝对正确,你不信到其它地方搜索一下,启动和调用都是正确的。 可是这样的编写方式,绝对只适合小量访问的环境中,并且要保证不会有大量的并发发生才可以勉强这样写,我做过试验,如果是50个并发,还勉强可以接受,如果有1000个,也许就要丢掉一大半了,我做过如下试验: [java] view plaincopyprint? 1. public static void main(String[] args) {

2. // TODO Auto-generated method stub 3. BatClient b=new BatClient(); 4. String transq=\5. int index=0; 6. int times=100;

7. for(int j=0;j

10. transq=b.getZeroString(transq.length())+transq; 11. Thread thread=new Thread(new Client(transq)); 12. thread.start(); 13. index++; 14. } 15. try {

16. Thread.sleep(1000);

17. } catch (InterruptedException e) { 18. // TODO Auto-generated catch block 19. e.printStackTrace(); 20. } 21. } 22. }

public static void main(String[] args) {// TODO Auto-generated method stubBatClient b=new BatClient();String transq=\int index=0;int times=100;for(int j=0;j

线程池,就是在调用线程的时候初使化一定数量的线程,有线程过来的时候,先检测初使化的线程还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处理,就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用于了,那没得办法,另外的线程就只有等了,直到有新的―服务员‖为止。线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并发而因为资源不足挂掉。这个时候既然知道了线程池的优点,那么我们启动线程的时候就要换一种方式了:

[java] view plaincopyprint?

1. //初使化线程池,这是JDK5.0中自带的线程池,这里的参数依次代表: 2. //核心线程数(最小活动线程数)

3. //最大线程数及并发数【这个要注意,如果你的实际发大于该数,则有些请求这个时候虽

然被接收,但是去得不到处理,这个数据一定得根据实际情况而设定,如我这里设值为20,实际模拟并发50,如循环一次,或者是二次并发,总会有20个不能够处理,如果设为25,就有15得不到处理,如果设为50则全部可以被处理,这个可以折磨了我好几天】 4. //线程池维护线程所允许的空闲时间 5. //线程池维护线程所允许的空闲时间的单位 6. //线程池所使用的缓冲队列

7. //线程池对拒绝任务的处理策略(通常是抛出异常)

8. ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 100,

10,TimeUnit.SECONDS,

9. new ArrayBlockingQueue(10),

10. new ThreadPoolExecutor.DiscardOldestPolicy()); 11. while(true){ 12. //侦听

13. server.clientSocket=server.serverSocket.accept(); 14. if(server.clientSocket!=null) 15. {

16. //采用线程池启动线程

17. threadPool.execute(new MsgListener(server.clientSocket)); 18. } 19. }

//初使化线程池,这是JDK5.0中自带的线程池,这里的参数依次代表://核心线程数(最小活动线程数)//最大线程数及并发数【这个要注意,如果你的实际发大于该数,则有些请求这个时候虽//线程池维护线程所允许的空闲时间//线程池维护线程所允许的空闲时间的单位//线程池所使用的缓冲队列//线程池对拒绝任务的处理策略(通常是抛出异常)ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 100, 10,Timenew ArrayBlockingQueue(10),new ThreadPoolExecutor.DiscardOldestPolicy());while(true){//侦听server.clientSocket=server.serverSocket.accept();if(server.clientSocket!=null){//采用线程池启动线程threadPool.execute(new MsgListener(server.clientSocket)} 可以看出这里的启动和上面没有多大的差别,这样写还更简洁,只是需要初使化一些必须的参数而已。 参考文章:http://blog.sina.com.cn/s/blog_568c94a301000ebj.html 本文出自:冯立彬的博客 hain

Annotation 入门实例

转载出处:http://lighter.javaeye.com。

Annotation在java的世界正铺天盖地展开,有空写这一篇简单的annotations的文章,算是关于Annotation入门的文章吧,希望能各位们能抛砖,共同学习...... 不讲废话了,实践才是硬道理.

第一部分:了解一下java1.5起默认的三个annotation类型:

一个是@Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。 一个是@Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上.

一个是@SuppressWarnings:这一个类型可以来暂时把一些警告信息消息关闭.

如果不清楚上面三个类型的具体用法,各位可以baidu或google一下的,很简单的。

第二部分:讲一下annotation的概念先,再来讲一下怎样设计自己的annotation. 首先在jdk自带的java.lang.annotation包里,打开如下几个源文件:

1、源文件Target.java 代码

@Documented

@Retention (RetentionPolicy.RUNTIME)

@Target (ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }

其中的@interface是一个关键字,在设计annotations的时候必须把一个类型定义为@interface,而不能用class或interface关键字(会不会觉得sun有点吝啬,偏偏搞得与interface这么像).

2、源文件Retention.java 代码

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }

看到这里,大家可能都模糊了,都不知道在说什么,别急,往下看一下.

在上面的文件都用到了RetentionPolicy,ElementType这两个字段,你可能就会猜到这是两个java文件.的确,这两个文件的源代码如下:

3、源文件RetentionPolicy.java 代码

public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }

这是一个enum类型,共有三个值,分别是SOURCE,CLASS 和 RUNTIME. SOURCE代表的是这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译之后,Annotation的数据就会消失,并不会保留在编译好的.class文件里面。

ClASS的意思是这个Annotation类型的信息保留在程序源码里,同时也会保留在编译好

的.class文件里面,在执行的时候,并不会把这一些信息加载到虚拟机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS.

第三个,是RUNTIME,表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的.

举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息;相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated.

4、源文件ElementType.java 代码

public enum ElementType {

TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE }

@Target里面的ElementType是用来指定Annotation类型可以用在哪一些元素上的.说明一下:TYPE(类型), FIELD(属性), METHOD(方法), PARAMETER(参数), CONSTRUCTOR(构造函数),LOCAL_VARIABLE(局部变量),

ANNOTATION_TYPE,PACKAGE(包),其中的TYPE(类型)是指可以用在Class,Interface,Enum和Annotation类型上.

另外,从1的源代码可以看出,@Target自己也用了自己来声明自己,只能用在ANNOTATION_TYPE之上.

如果一个Annotation类型没有指明@Target使用在哪些元素上,那么它可以使用在任何元素之上,这里的元素指的是上面的八种类型. 举几个正确的例子:

@Target(ElementType.METHOD)

@Target(value=ElementType.METHOD)

@Target(ElementType.METHOD,ElementType.CONSTRUCTOR) 具体参考一下javadoc文档

上面一下1和2的源文件,它们都使用了@Documented,@Documented的目的就是让这一个Annotation类型的信息能够显示在javaAPI说明文档上;没有添加的话,使用javadoc生成API文档的时候就会找不到这一个类型生成的信息.

另外一点,如果需要把Annotation的数据继承给子类,那么就会用到@Inherited这一个Annotation类型.

第三部分:下面讲的设计一个最简单的Annotation例子,这一例子共用四个文件; 1、Description.java 代码

package lighter.javaeye.com;

import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME) @Documented

public @interface Description { String value(); }

说明:所有的Annotation会自动继承java.lang.annotation这一个接口,所以不能再去继承别的类或是接口.

最重要的一点,Annotation类型里面的参数该怎么设定:

第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型.

第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean和String,Enum等数据类型.例如,String value();这里的参数成员就为String.

第三,如果只有一个参数成员,参数名称一定要为\后加小括号.例:上面的例子就只胡一个参数成员.

2、Name.java 代码

package lighter.javaeye.com;

import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;

//注意这里的@Target与@Description里的不同,参数成员也不同 @Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME) @Documented

public @interface Name { String originate(); String community(); }

3、JavaEyer.java

代码

package lighter.javaeye.com;

@Description(\做最棒的软件开发交流社区\ public class JavaEyer {

@Name(originate=\创始人:robbin\ public String getName() {

return null; }

@Name(originate=\创始人:江南白衣\ public String getName2() {

return \借用两位老大的id,写这一个例子,请见谅!\ } }

4、最后,写一个可以运行提取JavaEyer信息的类TestAnnotation 代码

package lighter.javaeye.com;

import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set;

public class TestAnnotation { /**

* author lighter

* 说明:具体关天Annotation的API的用法请参见javaDoc文档 */

public static void main(String[] args) throws Exception { String CLASS_NAME = \ Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods();

boolean flag = test.isAnnotationPresent(Description.class); if(flag) {

Description des = (Description)test.getAnnotation(Description.class); System.out.println(\描述:\ System.out.println(\ }

//把JavaEyer这一类有利用到@Name的全部方法保存到Set中去 Set set = new HashSet(); for(int i=0;i

boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); }

for(Method m: set) {

Name name = m.getAnnotation(Name.class); System.out.println(name.originate());

System.out.println(\创建的社区:\ } } }

5、运行结果:

描述:javaeye,做最棒的软件开发交流社区 ----------------- 创始人:robbin

创建的社区:javaEye 创始人:江南白衣

创建的社区:springside

移动开发者大会最新议题发布,八折抢票! 2013年1月微软MVP申请开始啦! ―第一次亲密接触‖—有奖征文活动

应用HttpClient来对付各种顽固的WEB服务器

分类: Java网络编程 2006-01-06 17:08 7435人阅读 评论(5) 收藏 举报

一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中―偷‖一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到COOKIE问题的处理。我们知道目

前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie,想想当你用java.net.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的―顽固‖!再有如通过HTTP来上传文件呢?不需要头疼,这些问题有了―它‖就很容易解决了! 我们不可能列举所有可能的顽固,我们会针对几种最常见的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache开源组织中的httpclient,它隶属于Jakarta的commons项目,目前的版本是2.0RC2。commons下本来已经有一个net的子项目,但是又把httpclient单独提出来,可见http服务器的访问绝非易事。

Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯方式,告诉它你想使用HTTPS方式,剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们,为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所有问题。 1. 读取网页(HTTP/HTTPS)内容

下面是我们给出的一个简单的例子用来访问某个页面

/*

* Created on 2003-12-14 by Liudong */

package http.demo;

import java.io.IOException;

import org.apache.commons.httpclient.*;

import org.apache.commons.httpclient.methods.*; /**

* 最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面 * @author Liudong */

public class SimpleClient {

public static void main(String[] args) throws IOException {

HttpClient client = new HttpClient(); //设置代理服务器地址和端口

//client.getHostConfiguration().setProxy(\);

//使用GET方法,如果服务器需要通过HTTPS连接,那只需要将下面URL中的http

换成https }

在这个例子中首先创建一个HTTP客户端(HttpClient)的实例,然后选择提交的方法是GET或者POST,最后在HttpClient实例上执行提交的方法,最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用HttpClient的基本流程。其实用一行代码也就可以搞定整个请求的过程,非常的简单!

}

HttpMethod method = new GetMethod(\); //使用POST方法

//HttpMethod method = new PostMethod(\ client.executeMethod(method); //打印服务器返回的状态

System.out.println(method.getStatusLine()); //打印返回的信息

System.out.println(method.getResponseBodyAsString()); //释放连接

method.releaseConnection();

2. 以GET或者POST方式向网页提交参数

其实前面一个最简单的示例中我们已经介绍了如何使用GET或者POST方式来请求一个页面,本小节与之不同的是多了提交时设定页面所需的参数,我们知道如果是GET的请求方式,那么所有参数都直接放到页面的URL后面用问号与页面地址隔开,每个参数用&隔开,例如:http://java.sun.com?name=liudong&mobile=123456,但是当使用POST方法时就会稍微有一点点麻烦。本小节的例子演示向如何查询手机号码所在的城市,代码如下:

/*

* Created on 2003-12-7 by Liudong */

package http.demo;

import java.io.IOException;

import org.apache.commons.httpclient.*;

import org.apache.commons.httpclient.methods.*; /**

* 提交参数演示

* 该程序连接到一个用于查询手机号码所属地的页面 * 以便查询号码段1330227所在的省份以及城市 * @author Liudong */

public class SimpleHttpClient { public static void main(String[] args) throws IOException {

HttpClient client = new HttpClient();

client.getHostConfiguration().setHost(\, \); HttpMethod method = getPostMethod();//使用POST方式提交数据 client.executeMethod(method); //打印服务器返回的状态

System.out.println(method.getStatusLine()); //打印结果页面 String response =

new

String(method.getResponseBodyAsString().getBytes(\)); //打印返回的信息

System.out.println(response); method.releaseConnection();

} /**

* 使用GET方式提交数据 * @return */

private static HttpMethod getGetMethod(){ return new GetMethod(\); } /**

* 使用POST方式提交数据

* @return

80,

} */ private static HttpMethod getPostMethod(){ } PostMethod post = new PostMethod(\); NameValuePair simcard = new NameValuePair(\,\); post.setRequestBody(new NameValuePair[] { simcard}); return post; 在上面的例子中页面http://www.imobile.com.cn/simcard.php需要一个参数是simcard,这个参数值为手机号码段,即手机号码的前七位,服务器会返回提交的手机号码对应的省份、城市以及其他详细信息。GET的提交方法只需要在URL后加入参数信息,而POST则需要通过NameValuePair类来设置参数名称和它所对应的值 3. 处理页面重定向 在JSP/Servlet编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的的区别在于后者是在服务器中实现页面的跳转,也就是说应用容器加载了所要跳转的页面的内容并返回给客户端;而前者是返回一个状态码,这些状态码的可能值见下表,然后客户端读取需要跳转到的页面的URL并重新加载新的页面。就是这样一个过程,所以我们编程的时候就要通过HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可以通过读取HTTP头中的location属性来获取新的地址。 状态码 对应HttpServletResponse的常量 301 302 303 307 SC_MOVED_PERMANENTLY SC_MOVED_TEMPORARILY SC_SEE_OTHER SC_TEMPORARY_REDIRECT 详细描述 页面已经永久移到另外一个新地址 页面暂时移动到另外一个新的地址 客户端请求的地址必须通过另外的URL来访问 同SC_MOVED_TEMPORARILY 下面的代码片段演示如何处理页面的重定向 client.executeMethod(post); System.out.println(post.getStatusLine().toString()); post.releaseConnection(); //检查是否重定向 int statuscode = post.getStatusCode(); if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode == HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { //读取新的URL地址

Header header = post.getResponseHeader(\); if (header != null) {

String newuri = header.getValue();

if ((newuri == null) || (newuri.equals(\))) newuri = \;

GetMethod redirect = new GetMethod(newuri); client.executeMethod(redirect);

System.out.println(\+ redirect.getStatusLine().toString()); redirect.releaseConnection(); } else

System.out.println(\); }

我们可以自行编写两个JSP页面,其中一个页面用response.sendRedirect方法重定向到另外一个页面用来测试上面的例子。 4. 模拟输入用户名和口令进行登录

本小节应该说是HTTP客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可浏览到想要的页面。因为HTTP协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以JSP/Servlet为例,当浏览器请求一个JSP或者是Servlet的页面时,应用服务器会返回一个参数,名为jsessionid(因不同应用服务器而异),值是一个较长的唯一字符串的Cookie,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息,应用服务器根据读取这个会话标识来获取对应的会话信息。

对于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的Cookie中读取当前请求对应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。

这样一来,对于HTTP的客户端来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取Cookie值;再次请求登录页面并加入登录页所需的每个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多,可是如果你使用httpclient的话,你甚至连一行代码都无需增加,你只需要先传递登录信息执行登录过程,

然后直接访问想要的页面,跟访问一个普通的页面没有任何区别,因为类HttpClient已经帮你做了所有该做的事情了,太棒了!下面的例子实现了这样一个访问的过程。 /*

* Created on 2003-12-7 by Liudong */

package http.demo;

import org.apache.commons.httpclient.*;

import org.apache.commons.httpclient.cookie.*; import org.apache.commons.httpclient.methods.*; /**

* 用来演示登录表单的示例 * @author Liudong */

public class FormLoginDemo {

static final String LOGON_SITE = \; static final int LOGON_PORT = 8080;

public static void main(String[] args) throws Exception{

post.setRequestBody(new NameValuePair[]{name,pass}); int status = client.executeMethod(post);

System.out.println(post.getResponseBodyAsString()); post.releaseConnection();

//查看cookie信息

CookieSpec cookiespec = CookiePolicy.getDefaultSpec();

Cookie[] cookies = cookiespec.match(LOGON_SITE, LOGON_PORT, \,

HttpClient client = new HttpClient();

client.getHostConfiguration().setHost(LOGON_SITE, LOGON_PORT);

//模拟登录页面login.jsp->main.jsp

PostMethod post = new PostMethod(\); NameValuePair name = new NameValuePair(\, \);

NameValuePair pass = new NameValuePair(\, \);

false, client.getState().getCookies());

}

}

if (cookies.length == 0) {

System.out.println(\);

} else { }

//访问所需的页面main2.jsp

GetMethod get = new GetMethod(\); client.executeMethod(get);

System.out.println(get.getResponseBodyAsString()); get.releaseConnection();

for (int i = 0; i < cookies.length; i++) { }

System.out.println(cookies[i].toString());

5. 提交XML格式参数

提交XML格式的参数很简单,仅仅是一个提交时候的ContentType问题,下面的例子演示从文件文件中读取XML信息并提交给服务器的过程,该过程可以用来测试Web服务。 import java.io.File;

import java.io.FileInputStream;

import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.PostMethod; /**

* 用来演示提交XML格式数据的例子 */

public class PostXMLClient {

public static void main(String[] args) throws Exception { File input = new File(“test.xml”); PostMethod

post

=

new

PostMethod(“http://localhost:8080/httpclient/xml.jsp”); // 设置请求的内容直接从文件中读取

post.setRequestBody(new FileInputStream(input)); if (input.length() < Integer.MAX_VALUE) post.setRequestContentLength(input.length()); else

post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);

// 指定请求内容的类型

post.setRequestHeader(\, \); HttpClient httpclient = new HttpClient(); int result = httpclient.executeMethod(post);

System.out.println(\ + result); System.out.println(\);

System.out.println(post.getResponseBodyAsString()); post.releaseConnection(); } }

6. 通过HTTP上传文件

httpclient使用了单独的一个HttpMethod子类来处理文件的上传,这个类就是

MultipartPostMethod,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面的代码片段演示如何使用这个类。

MultipartPostMethod filePost = new MultipartPostMethod(targetURL); filePost.addParameter(\, targetFilePath); HttpClient client = new HttpClient();

//由于要上传的文件可能比较大,因此在此设置最大的连接超时时间

client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);

int status = client.executeMethod(filePost); 上面代码中,targetFilePath即为要上传的文件所在的路径。 7. 访问启用认证的页面

我们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略,httpclient支持三种认证方式包括:基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全;摘要认证是在HTTP 1.1中加入的认证方式,而NTLM则是微软公司定义的而不是通用的规范,最新版本的NTLM是比摘要认证还要安全的一种方式。 下面例子是从httpclient的CVS服务器中下载的,它简单演示如何访问一个认证保护的页面: import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.methods.GetMethod; public class BasicAuthenticationExample {

public BasicAuthenticationExample() { }

public static void main(String[] args) throws Exception { HttpClient client = new HttpClient(); client.getState().setCredentials( \, \,

new UsernamePasswordCredentials(\, \) ); GetMethod

get

=

new

GetMethod(\); get.setDoAuthentication( true );

int status = client.executeMethod( get );

System.out.println(status+\+ get.getResponseBodyAsString()); get.releaseConnection(); } }

8. 多线程模式下使用httpclient

多线程同时访问httpclient,例如同时从一个站点上下载多个文件。对于同一个

HttpConnection同一个时间只能有一个线程访问,为了保证多线程工作环境下不产生冲突,httpclient使用了一个多线程连接管理器的类:

MultiThreadedHttpConnectionManager,要使用这个类很简单,只需要在构造HttpClient实例的时候传入即可,代码如下:

MultiThreadedHttpConnectionManager connectionManager =

new MultiThreadedHttpConnectionManager();

HttpClient client = new HttpClient(connectionManager); 以后尽管访问client实例即可。

这些天开发一个项目,服务器是tomcat,操作系统是xp,采用的是MVC架构,模式是采用Facade模式,总是出现乱码,自己也解决了好多天,同事也帮忙解决,也参考了网上众多网友的文章和意见,总算是搞定。但是好记性不如烂笔杆,所以特意记下,以防止自己遗忘,同时也给那些遇到同样问题的人提供一个好的参考途径: (一) JSP页面上是中文,但是看的是后是乱码:

解决的办法就是在JSP页面的编码的地方<%@ page language=\

contentType=\,因为Jsp转成Java文件时的编码问题,默认的话有的服务器是ISO-8859-1,如果一个JSP中直接输入了中文,Jsp把它当作ISO8859-1来处理是肯定有问题的,这一点,我们可以通过查看Jasper所生成的Java中间文件来确认

(二) 当用Request对象获取客户提交的汉字代码的时候,会出现乱码: 解决的办法是:要配置一个filter,也就是一个Servelet的过滤器,代码如下: import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.UnavailableException; /**

* Example filter that sets the character encoding to be used in parsing the * incoming request */

public class SetCharacterEncodingFilter implements Filter { /**

* Take this filter out of service. */

public void destroy() { } /**

* Select and set (if specified) the character encoding to be used to

* interpret request parameters for this request. */

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {

request.setCharacterEncoding(\

// 传递控制到下一个过滤器 chain.doFilter(request, response); }

public void init(FilterConfig filterConfig) throws ServletException { } }

配置web.xml

Set Character Encoding SetCharacterEncodingFilter

Set Character Encoding /*

如果你的还是出现这种情况的话你就往下看看是不是你出现了第四中情况,你的Form提交的数据是不是用get提交的,一般来说用post提交的话是没有问题的,如果是的话,你就看看第四中解决的办法。

还有就是对含有汉字字符的信息进行处理,处理的代码是: package dbJavaBean;

public class CodingConvert {

public CodingConvert() { // }

public String toGb(String uniStr){ String gbStr = \if(uniStr == null){ uniStr = \} try{

byte[] tempByte = uniStr.getBytes(\gbStr = new String(tempByte,\}

catch(Exception ex){ }

return gbStr; }

public String toUni(String gbStr){ String uniStr = \if(gbStr == null){ gbStr = \} try{

byte[] tempByte = gbStr.getBytes(%uniStr = new String(tempByte,\}catch(Exception ex){ }

return uniStr; } }

你也可以在直接的转换,首先你将获取的字符串用ISO-8859-1进行编码,然后将这个编码存放到一个字节数组中,然后将这个数组转化成字符串对象就可以了,例如: String str=request.getParameter(―girl‖); Byte B[]=str.getBytes(―ISO-8859-1‖); Str=new String(B);

通过上述转换的话,提交的任何信息都能正确的显示。

(三) 在Formget请求在服务端用request. getParameter(―name‖)时返回的是乱码;按tomcat的做法设置Filter也没有用或者用request.setCharacterEncoding(\也不管用

问题是出在处理参数传递的方法上:如果在servlet中用doGet(HttpServletRequest request, HttpServletResponse response)方法进行处理的话前面即使是写了: request.setCharacterEncoding(\

response.setContentType(\

也是不起作用的,返回的中文还是乱码!!!如果把这个函数改成doPost(HttpServletRequest request, HttpServletResponse response)一切就OK了。

同样,在用两个JSP页面处理表单输入之所以能显示中文是因为用的是post方法传递的,改成get方法依旧不行。

由此可见在servlet中用doGet()方法或是在JSP中用get方法进行处理要注意。这毕竟涉及到要通过浏览器传递参数信息,很有可能引起常用字符集的冲突或是不匹配。 解决的办法是:

1) 打开tomcat的server.xml文件,找到区块,加入如下一行: URIEncoding=‖GBK‖ 完整的应如下:

maxSpareThreads=\debug=\URIEncoding=\

2)重启tomcat,一切OK。 需要加入的原因大家可以去研究

$TOMCAT_HOME/webapps/tomcat-docs/config/http.html下的这个文件就可以知道原因了。需要注意的是:这个地方如果你要是用UTF-8的时候在传递的过程中在Tomcat中也是要出现乱码的情况,如果不行的话就换别的字符集。

(四) JSP页面上有中文,按钮上面也有中文,但是通过服务器查看页面的时候出现乱码: 解决的办法是:首先在JSP文件中不应该直接包含本地化的消息文本,而是应该通过标签从Resource Bundle中获得文本。应该把你的中文文本放到Application.properties文件中,这个文件放在WEB-INF/classes/*下,例如我在页面里有姓名,年龄两个label,我首先就是要建一个Application.properties,里面的内容应该是name=‖姓名‖ age=‖年龄‖,然后我把这个文件放到WEB-INF/classes/properties/下,接下来根据Application.properties文件,对他进行编码转化,创建一个中文资源文件,假定名字是Application_cn.properties。在JDK中提供了native2ascii命令,他能够实现字符编码的转换。在DOS环境中找到你放置Application.properties的这个文件的目录,在DOS环境中执行一下命令,将生成按GBK编码的中文资源文件Application_cn.properties:native2ascii

–encoding gbk Application.properties Application_cn.properties执行以上命令以后将生成如下内容的Application_cn.properties文件:name=/u59d3/u540d age=/u5e74/u9f84,在Struts-config.xml中配置:,这样的化在页面上出现的时候就会出现中文的姓名,年龄这个也是一样,按钮上汉字的处理也是同样的。 (五) 写入到数据库是乱码:

解决的方法:要配置一个filter,也就是一个Servelet的过滤器,代码如同第二种时候一样。 如果你是通过JDBC直接链接数据库的时候,配置的代码如下:

jdbc:mysql://localhost:3306/workshopdb?useUnicode=true&characterEncoding=GBK,这样保证到数据库中的代码是不是乱码。

如果你是通过数据源链接的化你不能按照这样的写法了,首先你就要写在配置文件中,在tomcat 5.0.19中配置数据源的地方是在C:/Tomcat 5.0/conf/Catalina/localhost这个下面,我建立的工程是workshop,放置的目录是webapp下面,workshop.xml的配置文件如下:

type=\

factory

org.apache.commons.dbcp.BasicDataSourceFactory

maxActive 100

maxIdle

30

maxWait 10000

username root

password

driverClassName com.mysql.jdbc.Driver url

粗体的地方要特别的注意,和JDBC直接链接的时候是有区别的,如果你是配置正确的化,当你输入中文的时候到数据库中就是中文了,有一点要注意的是你在显示数据的页面也是要用<%@ page language=\这行代码的。需要注意的是有的前台的人员在写代码的是后用Dreamver写的,写了一个Form的时候把他

改成了一个jsp,这样有一个地方要注意了,那就是在Dreamver中Action的提交方式是request的,你需要把他该过来,因为在jsp的提交的过程中紧紧就是POST和GET两种方式,但是这两种方式提交的代码在编码方面还是有很大不同的,这个在后面的地方进行说明。3

以上就是我在开发系统中解决中文的问题,不知道能不能解决大家的问题,时间匆忙,没有及时完善,文笔也不是很好,有些地方估计是词不达意。大家可以给我意见,希望能共同进步。

这次做的项目,涉及到6个子系统的集成,其中有2个是遗留系统,4个是新开发的系统。我负责其中一个新系统

前期还是比较顺利,提前完成了开发计划,并进行了比较充分的代码检视、单元测试和内部测试。但是在进入接口联调和系统集成以后,就变得非常痛苦,这2周平均每天工作时间超过16个小时。但是整个系统还是没有稳定下来,还是有非常多的问题,而且每天的效率非常低,大部分的时间都是花在等待中

为什么这次在开发这么顺利的情况下,在项目后期却会陷入这么糟糕的局面?怎么样才可能比较顺利地完成一个较大的系统?我从项目启动的第一个环节开始,对每个过程进行反思

1、做好需求

现状:我认为这次的需求做得不到位。一方面猜想了比较多的需求,与客户却没有充分地沟通过,这就造成工作量的浪费。更糟糕的是为了满足太多的需求,也影响了系统的架构,造成系统架构的臃肿,这个在后面会详细说明。另一方面,或许有一些客户真正关注的需求,我们却没有实现,这个可能会给后期系统的推广造成困难。

原因分析:会出现上述的2方面问题,我觉得本质的原因就是和客户的沟通太少。需求基本都是凭空猜想,或者根据部门内业务专家的经验推断。虽然业务专家确实有比较高的业务水平,但是毕竟不能完全代表真正客户的想法。所以与客户的沟通太少,也没有积极地邀请客户参与到系统的每一轮阶段性展示活动中。这使得我们系统的需求做得比较糟糕。

总结:应该采用敏捷开发的方式,在尽量小的迭代周期内拿出系统的小版本给客户演示,按照先核心需求,再逐步丰满的方式,不断地调整需求。与客户充分沟通,既保证满足核心需求,又避免工作量的浪费

2、宁可没有架构师,也不要找一个扯淡的

现状:或许大公司都会有这样那样的政治因素,这次项目的所谓“架构师”就是一个大忽悠。

这个系统,估算是200左右的并发规模,可以认为相当于不存在并发问题;然后数据量的规模也不大。如果简单处理的话,可以是一个逻辑架构和物理架构都非常清晰明了的系统。我个人认为,就一个应用服务器+一个数据库服务器,就绰绰有余了。即使以后数据库压力大了,再上数据库集群不就行了。毕竟这是一个业务系统,不是一个互联网应用,根本就不存在并发和海量数据的问题。这样简单处理,我认为是合适的

但是这个“架构师”,整上的东西就很多了:

要先上2个数据库,一个存放业务数据,一个存放资源数据。这样2个数据库就要同步来同步去。这周另外1个小组,就为了这个同步的问题熬了一周的通宵

子系统之间要走所谓“服务框架”,其实就是对CXF的一个封装,没任何意义,纯粹引入复杂性。其实每个子系统开放一个Servlet都可以了。这个服务框架带来的问题,伴随6个子系

统整个开发流程,开发时间至少增加1/4,没带来任何价值

然后来个日志框架,其实花半小时自定义一个Appender的扩展就可以解决问题

业务可能有变化的地方,整个OSGi。不好意思,开发已经延期2周了,现在大家都还没做出来。我说真有新功能,再发布一个升级的PATCH不行?何况这个系统就没热部署的需求,业务也很稳定,强行上个OSGi,这不是有病?

本来还要上分布式,幸好大领导终于意识到风险太大,制止了。。

关键是这位“架构师”,提出了上述种种方案之后,人没了?你找他谈实现,他就给你说原则。如是再三之后,我得出一个准确判断,这些方案,他自己就听说过名词。是干什么的?知道一点。怎么实现?不好意思

总结:架构要保持简单,一个数据库能解决的问题,就不要弄多个数据源同步。不准备开放给外系统调用的接口,就不要上web service。什么扯淡的分布式,OSGi,我就不评论了。当然我不是说这些技术不好,其实我对这些技术也非常感兴趣,但是毕竟是做系统,不是在玩过家家。用在合适的地方的技术,解决实际问题带来价值的技术,才是好技术。过度架构,就是200个兄弟的累死累活,就是系统的延期,就是项目跑不起来

3、其实做好上面的2项:分析清楚需求,不瞎做。保持适当架构,不折腾。一个系统到后期就不会像这次这么痛苦了,我认为上述2项是最重要的。后面的都是比较细节的问题

4、越早对齐数据库越好

服务端有2个数据库(Oracle),终端有1个数据库(SQLite),所以存在数据库字段不统一的问题。前期我们自己跑服务桩,啥问题没有,到了集成的时候,各种数据库字段对不齐,有的是类型不匹配,有的是长度限制不匹配,有的是非空约束不匹配,等等。大家都知道,到应用稳定以后,再去改数据库模型是相当痛苦的,因为从数据库字段,到模型对象,到DAO,都要改,不仅开发有工作量,测试也有工作量,越到后期,这个代价越大,所以应该尽早地对齐数据库

这个责任在我,是我系统集成经验不足造成的。这个教训我记住了 5、仅仅在文档层面对齐接口没用,对于涉及到多个子系统集成的项目,可以“接口驱动开发”

前期所有6个子系统,都是自己玩自己的。单元测试都不可谓不充分。也都模拟了实际场景,终端为了模拟测试业务场景,我还亲自写了一个完整的服务端桩。但是即使这样,到了集成的阶段,系统还是各种跑不起来。

其实前期领导就很重视对齐接口,每个子系统的负责人,花了一个星期来写接口文档,各子系统互相评审,并签字确认。到头来,没有用。因为实际运行的不是文档,是代码。文档虽然已经描述清楚了交互协议,每个字段也都对过了。但是接口太多,有的接口也相当复杂,在没有实际测试的情况下,谁也不能保证100%是按照文档来操作的,何况文档也不能保证

是100%正确的,毕竟在开发过程中,变更是绝对会发生的,但是未必文档就能及时刷新并知会对方系统。

所以这种涉及到多个子系统集成的项目,我强烈认为应该以接口来驱动开发。每个子系统,首先实现接口。当然在开发早期,每个子系统内部的业务逻辑肯定没实现,不可能有真实业务数据,但是这种时候,可以先传递符合格式的假数据,照样可以让接口跑起来。这样到系统集成阶段,就节省了非常多的工作量。我敢保证效果绝对比这次好。

6、早点集成,不能拖到最后一起集成

这条其实和上一条强相关。这次项目基本做到了每天SVN上的代码是可运行的小迭代。但是都是每个子系统自己玩自己的。应该缩短集成的周期

当然由于每个子系统的开发进度不同,很难做到像单个子系统内部一样每日集成(我怀疑是根本不可能)。但是至少也应该可以做到半个星期或者一周集成一次。把集成的工作放到前面,可以更早的暴露问题,更早的调整。效果一定也比这次强

以上是本次项目过程中个人的一点总结。此外在代码层面,也有一些心得。包括Android应用的分发模式,Android应用的简单ORM框架,Android日志规范,接口设计,Android服务管理等。待系统集成完之后,再总结一下

开发者大会最新议题发布,八折抢票! 2013年1月微软MVP申请开始啦! ―第一次亲密接触‖—有奖征文活动

使用JAVA编写报文接收端的注意事项

分类: SOCKET 2009-04-24 17:18 1182人阅读 评论(1) 收藏 举报

使用JAVA编写报文接收端的注意事项

在JAVA中编写报文服务端,是非常简单的事情,因为JAVA已经为我们封装了TCP/IP这些东西,我们不需要去关注这些;它对我们提供了方便操作的ServerSocket类,如果不考虑其它因素,那么要启动一个服务端,只需要简简单的几句代码: //以下启服务端,其中6500表示用于侦听客户端连接的端口 ServerSocket serverSocket=new ServerSocket(6500); while(true){

Socket socket=serverSocket.accept(); if(socket!=null){

System.out.println(\有客户端连接过来\); //下面对客户端做相应的动作,这里省略 } }

从上面可以看出,我们根据没有去管理那些协议去怎么样实现,走的七层网络协议还是四层网络协议,这些对我们来说都可以暂时不去管。但是,这种东西只能说你看到了希望(没有像现在的中小学奥数班一样,在你一开始的时候就让你对学习失去兴趣)、有了信心,后面要做什么事情,就是在现在的基础之上扩充,使苦适合于真实的环境中,就像大家经常说的一句话一样:社会,是现实的。我们还需要考虑以下方式的事情:

1、正确性与精确性。这句话是从软件工程上学来了,因为不管是做什么软件,写什么代码,如果连正确都谈不上,那这个东西存在的意义也不大。这里就是要能够正确处理客户过来的请求,不要将客户查询的请求搞成了更新或者是删除,那后果可就严重了,所以这是第一个要注意的。

2、性能与效率。当你写的代码能够做正确的工作的时候,不管是一分钟完成还是十分钟完成,再甚者N个小时,只要是正确的,客户都可以接受,当然你得给客户N个多可以接受的理由,如:查询的时候要关联很多表,数据量太大,计算的时间比较久,就如我现在这边的全行日终跑报表系统,客户几个小时都可以接受。但是我们不能够满足于这样,我们是程序员,在可能的情况下让自己写的东西跑得更快,而不仅限于跑得完就是了,就像马拉松,人人都可以跑,就算不得冠军也不要搞个最后一名吗!其中有一块特别需要优化的就是要采用连接池替代单一的“请求-连接”模式,成熟的、开源的、稳定的连接池网上很多,比较常用的有APACHE的DBCP、W3C的C3P0、Hibernate自带的连接池等等,性能都还不错,这样可以节省系统资源与加快运行速度。

3、能够处理的最大并发数。这时就要采用线程池的方式,不要来一个请求就新启动一个线程,这样请求越多,要求生成的线程数也越多,达不到调节系统资源的目的;采用线程池可以自动调节,如果请求少的情况,就少生成连接数,如果多的情况下就多生成,再如果超过了最大的连接数,那让就他们等到有空了再说;这同做生意一样,桌子板凳都没有了,就只有等到别人吃完了。JDK自1.5开始就自带有连接池,需要我们关心的东西,只需要调用的时候设置向个参数,如最小连接数、最大连接数、线程的生存时间及单位等,它会帮忙管理 与调用线程,以保证系统性能的最大化,启动调用和单独采用Thread启动线程没有什么区别。最简单下面两句代码就OK:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 3,TimeUnit.SECONDS,

new

ArrayBlockingQueue(10),new

ThreadPoolExecutor.DiscardOldestPolicy());

threadPool.execute(new MsgListener(clientSocket));

4、代码不一定要自己写。没错,能够不自己写的就最好不要写,因为代码是写的越多错的机会越多,并且还要花时间去调式,有够用现成的工具,如上面的线程池、连接池等等,都可以说是比较优秀的,因为他们都经过时间的检验,写这些代码的都是比较高的人,我们完全可以信赖。如果要对数据进行操作,我的方式是先用POWERDESIGNER做CDM,把关系都理好了,再生成PDM,然后写到库,然后再通过HIBERNATE的MIDDLEGEN工具,生成Hibernate映射文件,再通过Hibernate的HBM2JAVA,再生成基础的代码,这样你可以省去不少的时间,并且可以保证性,然后你再写关注自己业务方面的东西,开发起来又快又稳。 5、保证客户端过来的请求都能够被处理。请算你采用连接池,但有些时候遇到连接确定太忙,数据库服务忙不过来,这个时候去数据库操作,如你要将客户端过来的报文写到库中保存,但是HIBERNATE给你报过\,那你的这表交易就不处理了吗?不可能,这是金融系统,由人行的发过来的,你以为它还等你说,如果失败次数超过了规定了,那就等着挨批吧。所以这个时候就算没有成功,也要说我成功了,后面再把失败的给做进去。我这里采用将失败的即发生异常的交易的报文全部放到XML中,然后再通过线程定时去检测并处理,这样就可能保证只要是接到了的请求,在线程池没有严重超标的情况下,所有的请求都可以被处理;为什么说没有严重超标呢,因为如果线程池超时过久,就会被线程池给KILL掉,所以请求就莫明其妙的没有了,但也不能够将线程的生存周期设置太久,否则这样也不利用其它线程的处理,会导致更大的问题。

6、参数配置化。尽量将需要用到的变更,与客户环境有关的,都做成参数化,这样就不需要我们再去修改程序了。

7、多做测试。在完成某个功能,或者是某个模块的时候,一定要测试,否则等到后面来一运行,什么问题都一下子出来了,找问题都不知道从什么时候找起。边做边测试,这是只有好处没有坏处的。如这里你一定要有客户端,去测试你的服务端,测试并发的性能与处理的结果的正确性与快速性等。

本文出自:冯立彬的博客

Hibernate框架ORM的实现原理-不是技术的技术

分类: Hibernate JAVA转贴 JAVA实用笔记 2007-08-31 16:23 5503人阅读 评论(5) 收藏 举报

Hibernate框架ORM的实现原理

2007-08-24 来自:梁建全 梁建全:

东方标准国际软件专业讲师,曾经负责过的重大的大型项目包括:全军武器装备质量管理系统、全军办公自动化系统、日本NTT电信管理系统等。主要著作包括《ASP.NET开发答疑200问》《ASP.NET开发实例导航》等。毕业于北京大学计算机专业。 1. 什么是ORM

ORM的全称是Object Relational Mapping,即对象关系映射。它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的操作转化为对这些对象的操作。因此它的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。 2.什么是Hibernate

对于Hibernate的称呼有很多,比如工具、技术、框架以及解决方案等,这些都可以,重要的是大家要知道它的作用。在这里我习惯性称它为框架,它是一种能实现ORM的框架。能实现ORM这个功能的框架有很多,Hibernate可以说是这些框架中最流行、最受开发者关注的,甚至连JBoss公司也把它吸收进来,利用它在自己的项目中实现ORM功能。 3.ORM的实现原理

现在在Java领域大家对Hibernate的讨论很多,比如它的优缺点、如何应用、错误如何解决以及把它和Struts/Spring等框架相结合作为整个系统的解决方案。在这里我想和大家探讨一些更深层次的话题,那就是Hibernate是如何实现ORM的功能?如果让我们自己开发一款实现ORM功能的框架需要怎么做?其实这些问题就是围绕着一个词,那就是“映射”,如果我们知道如何实现这种映射那么我们也能够开发出自己的一款ORM框架。会使用Hibernate的开发人员都知道,在使用它实现ORM功能的时候,主要的文件有:映射类(*.java)、映射文件(*.hbm.xml)以及数据库配置文件(*.properties或*.cfg.xml),它们各自的作用如下。

⑴映射类:它的作用是描述数据库表的结构,表中的字段在类中被描述成属性,将来就可以实现把表中的记录映射成为该类的对象。

⑵映射文件:它的作用是指定数据库表和映射类之间的关系,包括映射类和数据库表的对应关系、表字段和类属性类型的对应关系以及表字段和类属性名称的对应关系等。

⑶数据库配置文件:它的作用是指定与数据库连接时需要的连接信息,比如连接哪中数据库、登录用户名、登录密码以及连接字符串等。

在这三种主要的文件中,映射类为普通Java源文件、映射文件为XML格式、数据库配置文件为Properties格式或者是XML格式。想理解“映射”首先我们需要知道如何解析这三种文件,即解析XML格式文件、解析Properties格式文件和解析Java类文件。下面我们来分别探讨一下如何实现这些文件的解析。 ⑴如何解析XML文件

前面我们说过映射文件是XML格式,数据库配置文件也可以是XML格式,因此如果能解析XML文件我们就可以获取这两个文件的信息。XML文件格式我简单做下介绍,比如tom这句就是一个XML格式的描述,name代表节点,节点必须有开始标记和结束标记,在开始标记中我们可以添加一些属性的声明比如sex。解析XML的技术可以分为两类那就是SAX和DOM,这两种方式的差别和优缺点大家可以上网查阅或者我们会在以后的文章中提出,请大家关注。实现解析XML文件的功能很方便,我们可以通过下载第三方的一些工具包如xml-apis.jar和xercesImpl.jar等,也可以使用JDK自带的工具类DocumentBuilderFactory、 DocumentBuilder、Document、Element等等,大家可以通过API文挡查阅这些类的说明。通过这些类我们可以把XML文件的信息读入内存并通过类中的某些方法获取指定节点的名字、值、属性名、属性值这些信息。 ⑵解析Properties文件

数据库配置文件可以是XML格式也可以是Properties格式,Properties文件一般采用“属性名=属性值”的形式描述信息。如果配置文件采用Properties文件描述,我们就需要想办法解析这种类型的文件了。想解析Properties文件大家就需要熟悉Properties这个类了,这个类有一些常用方法比如,load()加载指定文件并读取文件中的属性信息,PropertyNames()返回所有属性名,

getProperty()返回指定属性名的属性值。通过解析Properties文件我们可以得到连接数据库必要的信息,然后通过底层JDBC技术与数据库建立连接。 ⑶解析Java类文件

通过解析映射文件和数据库配置文件我们可以建立数据库的连接,可以得到映射类的名字、属性名、数据库表名、字段名以及类型等信息。要把数据库中表的数据映射成为对象,首先需要把表中的记录取出,然后将每个字段值给映射类对象

的每个属性,这个赋值过程要调用对象中的set方法。我们现在通过映射文件只知道类名和属性名,如何根据类名和属性名调用相应的set和get方法,是一个关键问题。在Java中有一种机制叫反射机制,使用这种机制我们可以得到类的信息,包括类只用的修饰符、方法、属性、继承的父类以及实现接口等信息。反射机制相关的类有Class、Field、Method以及Constructor等。通过Class的getFields()、getMethods()和getConstructors()方法得到相应的属性、方法和构造方法。通过Field类的getName()、getType()和getModifiers()方法得到相应的属性名、属性类型、属性修饰符信息。通过Method类getReturnType()可以获取方法的返回类型,invoke()方法可以根据给定的方法名和参数值执行对象中对应的方法。我们可以首先通过以上方法获取类中的属性名,然后拼写成setXXX和getXXX方法名,最后根据方法名执行对应的方法,将数据库数据加载到对象中。

此外要实现Hibernate机制还会涉及到一个技术点,那就是如何获取数据库的相关信息。要实现这个功能,就需要大家了解JDBC的DataBaseMetaData类和ResultSetMetaData类,通过这两个类的方法我们就可以获取数据库表的字段名、类型、大小等相关信息。在这里我只是给大家把实现ORM功能的一些关键技术提了出来,通过上面介绍大家对ORM机制应该有个大概了解,比如通过解析数据库配置文件获取数据库连接信息并建立连接,通过解析映射文件可以获取映射类名、属性名、表名以及字段名等信息,得到名字后通过反射机制可以得到映射类信息,调用构造方法创建对象,调用每个属性的set方法给对象设值完成数据的装载。Hibernate就是采用这个过程来实现ORM的,当然Hibernate还使用了事务控制、缓存控制等很多技术,大家有兴趣的话我们可以以后探讨。

限于时间的原因前面提到的一些技术介绍的比较简单,对于有些人可能理解有困难,没关系我会在以后的时间把各个技术分别进行详细的介绍,请大家关注,同时热情邀请大家一起讨论,通过逐步剖析Hibernate,使我们对Hibernate的理解和驾御能力更近一步。谢谢大家。 (读后感:

这篇文章,如果从操作技术层面上来讲,确实没有说什么,因为在我看了这篇文章之后,并不能够让我现在就可以使用Hibernate。但是,我至少对这个轮廓有了一个初步的了解了,假如说我们现在身处大山里面,要想从大山里面出去,一看,那里都是高山,就不知道如何走、走那个方向;而如果说我们是站在一座高山上,要去山外还是有那么远,所有的路程还是得自己走,但是我看到了方向,知道自己朝着这个方向走肯定能够走出去。我看了这篇文章就是这个感觉,真希望以后能够多看到这样的文章,而不是一来就是这里要配置什么,那里需要怎么做,还没有开始,就昏了。

Hibernate VS iBATIS首先这个题目有点哗众取宠,呵呵,故意的.参考了网上许多前辈的见解,然后加了自己一点点的理解.如果有什么板砖,欢迎来拍 chinaewolf@gmail.com简介Hibernate 是当前最流行的O/R mapping框架,当前版本是3.05。它出身于sf.net,现在已经成为Jboss的一部分了

iBATIS 是另外一种优秀的O/R mapping框架,当前版本是2.0。目前属于apache的一个子项目了。

相对Hibernate―O/R‖而言,iBATIS 是一种―Sql Mapping‖的ORM实现。

Hibernate对数据库结构提供了较为完整的封装,Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。

而iBATIS 的着力点,则在于POJO 与SQL之间的映射关系。也就是说,iBATIS并不会为程序员在运行期自动生成SQL 执行。具体的SQL 需要程序员编写,然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。使用iBATIS 提供的ORM机制,对业务逻辑实现人员而言,面对的是纯粹的Java对象,这一层与通过Hibernate 实现ORM 而言基本一致,而对于具体的数据操作,Hibernate会自动生成SQL 语句,而iBATIS 则要求开发者编写具体的SQL 语句。相对Hibernate而言,iBATIS 以SQL开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。

二者的对比:1. iBATIS非常简单易学,Hibernate相对较复杂,门槛较高。2. 二者都是比较优秀的开源产品3. 当系统属于二次开发,无法对数据库结构做到控制和修改,那iBATIS的灵活性将比Hibernate更适合4. 系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL语句(或存储过程)才能达到系统性能设计指标。在这种情况下iBATIS会有更好的可控性和表现。5. iBATIS需要手写sql语句,也可以生成一部分,Hibernate则基本上可以自动生成,偶尔会写一些Hql。同样的需求,iBATIS的工作量比Hibernate要大很多。类似的,如果涉及到数据库字段的修改,Hibernate修改的地方很少,而iBATIS要把那些sql mapping的地方一一修改。6. 以数据库字段一一对应映射得到的PO和Hibernte这种对象化映射得到的PO是截然不同的,本质区别在于这种PO是扁平化的,不像Hibernate映射的PO是可以表达立体的对象继承,聚合等等关系的,这将会直接影响到你的整个软件系统的设计思路。7. Hibernate现在已经是主流O/R Mapping框架,从文档的丰富性,产品的完善性,版本的开发速度都要强于iBATIS8. 最关键的一句话是iBATIS的作者说的:If you are starting a new project and you're in full control of your object model and database design, Hibernate is a good choice of O/R tool.If you are accessing any 3rd party databases (e.g. vendor supplied), or you're working with a legacy database, or even just a really poorly designed database, then an O/R

mapper might not be capable of handling the situation. That's were an SQL Mapper comes in handy .

动开发者大会最新议题发布,八折抢票! 2013年1月微软MVP申请开始啦! ―第一次亲密接触‖—有奖征文活动

Eclipse插件的安装与配置

分类: Eclipse 2007-07-18 15:26 5356人阅读 评论(0) 收藏 举报

原贴地址:http://blog.csdn.net/zwzhero/archive/2005/05/30/384263.aspx 安装时有些小问题值得注意:

1、下载时注意Eclipse和Lomboz版本号很重要 一般来说相同版本号的才比较兼容。

即:2.0的Eclipse对应插件就装2.0的Lomboz;Eclipse3.01就对应装插件lomboz.301,这样不容易出错,下载时最好认真看清楚网站上的说明,否则有时安装总出现莫名其妙的问题,浪费时间。

2、安装Lomboz需要gef和emf插件的支持,所以光下载安装 Eclipse3.01,和lomboz.301.zip还不够,必须下载以下四个插件: Eclipse3.01 lomboz.301.zip GEF-runtime-3.0.1.zip emf-sdo-runtime-2.0.0.zip

这一点很容易被忽视,结果安装时有时就会碰到一个问题:

按照步骤安装好后,启动Eclipse,结果【window】--->【Preference】左侧菜单上跟本找不到lomboz一项。

而在【Help】-->【About Eclipse Platform】--->【Plug-in Details】 里面查看,却又明明 显示lomboz插件已都安装好了。这个问题折腾了我半天,网上一些中文安装教程都没明确说清楚,其实要安装好lomboz,gef和emf这两个插件也 是需要装的。

3、有时启动Eclipse未加载插件,解决方法很多,总结一下:

a 、 删除整个目录/eclipse/configuration/org.eclipse.update/,重启Eclipse

b 、在启动Eclipse时带上 -clean参数 如:d:/eclipse/eclipse.exe -clean

c 、 如果Eclipse启动找不到插件了,解决办法: 在/configuration/config.ini文件中加入一行

osgi.checkConfiguration=true

这样它会寻找并安装插件,找到后可以把那行再注释掉,这样以后每次启动就不会因寻找插件而显得慢了。

4、为了便于管理众多插件,建议用links方式安装各种eclipse插件 Eclipse基础--使用links方式安装Eclipse插件

eclispe想必大家都很熟悉了,一般来说,eclipse插件都是安装在plugins目录下。不过这样一来,当安装了许多插件之后,eclipse变的很大,最主要的是不便于更新和管理众多插件。用links方式安装eclipse插件,可以解决这个问题。 当前配置XP SP1,eclipse3.0.1

现在假设我的eclipse安装目录是D:/eclipse,待安装插件目录是D:/plug-in ,我将要安装LanguagePackFeature(语言包)、emf-sdo-xsd-SDK、GEF-SDK、Lomboz这四个插件。

先把这四个插件程序全部放在D:/plug-in目录里,分别解压。如Lomboz3.0.1.zip解压成Lomboz3.0.1目录,这个目录包含一 个plugins目录,要先在Lomboz3.0.1目录中新建一个子目录eclipse,然后把plugins目录移动到刚建立的eclipse 目录中,即目录结构要是这样的:D:/plug-in/Lomboz3.0.1/eclipse/plugins

Eclipse 将会到指定的目录下去查找 eclipse/features 目录和eclipse/plugins 目录,看是否有合法的功能部件和(或)插件。也就 是说,目标目录必须包含一个 /eclipse 目录。如果找到,附加的功能部件和插件在运行期配置是将是可用的,如果链接文件是在工作区创建之后添加 的,附加的功能部件和插件会作为新的配置变更来处理。

其它压缩文件解压后若已经包含eclipse/plugins目录,则不需要建立eclipse目录。

然后在 eclipse安装目录D:/eclipse目录中建立一个子目录links,在links目录中建立一个link文件,比如 LanguagePackFeature.link,改文件内容为

path=D:/plug-in/LanguagePackFeature 即 这个link文件要对应一个刚解压后的插件目录。 说明:

1. 插件可以分别安装在多个自定义的目录中。

2. 一个自定义目录可以安装多个插件。

3. link文件的文件名及扩展名可以取任意名称,比如ddd.txt,myplugin都可以。

4. link文件中path=插件目录的path路径分隔要用//或是/

5. 在links目录也可以有多个link文件,每个link文件中的path参数都将生效。

6. 插件目录可以使用相对路径。

7. 可以在links目录中建立一个子目录,转移暂时不用的插件到此子目录中,加快eclipse启动。

8. 如果安装后看不到插件,把eclipse 目录下的configuration目录删除,重启即可。

本文来源:https://www.bwwdw.com/article/l0g7.html

Top