Elton's Blog

Tag: Java

通过JNI实现Java对C/C++的调用

by on 七.29, 2011, under C/C++, Java, Linux

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

大致步骤

  1. 编写带有native声明的方法的java类
  2. 使用javac命令编译所编写的java类
  3. 使用javah命令生成扩展名为h的头文件
  4. 使用C/C++实现本地方法
  5. 将C/C++编写的文件生成动态连接库

1) 编写java程序:

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloNative{
  public native void greeting();//所有native所修饰的都是本地方法
 
  static{
    System.loadLibrary("HelloNative");//载入本地库
  }
 
  public static void main(String[] args){
    new HelloNative().greeting();
//    System.out.println(System.getProperty("java.library.path"));
  }
}

声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明该方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary(“HelloNative”);加载动态库(我们可以这样理解:我们的方法 greeting()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“HelloNative”是动态库的名字。

2) 编译

1
javac HelloNative.java

3) 生成扩展名为h的头文件

1
javah HelloNative

命令执行后会在当前目录下生产一个c的头文件,名字为HelloNative.h。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloNative */
 
#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif

这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致

4) 编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法。

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <jni.h>
#include "HelloNative.h"
 
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv * env, jobject obj)
{
  printf("Hello, Native!\n");
}

5) 生成动态库

1
gcc -fPIC -I/home/elton/jdk/include -I/home/elton/jdk/include/linux -shared -o libHelloNative.so HelloNative.c

注意,必须告知编译器jni.h所在的接口位置。linux的动态库都是以lib开头,以.so结尾的,要遵守这个命名规范。-fPIC是告诉编译器生成跟位置无关的动态链接库
命令执行后,会在当前目录生成一个libHelloNative.so的动态链接库文件

6) 运行程序

1
java -Djava.library.path=. HelloNative

必须指定java.library.path变量的内容,告诉java你的动态链接库的位置。
或者在命令行上输入

1
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

这样就不用每次调试的时候都输入-Djava.library.path=.这个参数了。

当你部署的时候,你通过System.out.println(System.getProperty(“java.library.path”));得到你系统的java.library.path位置,然后把你的动态链接库拷贝到这个目录中。我用的是ubuntu 11.04 64位版本,得到的结果是

1
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib

这样你的动态链接库就永远都会被java访问到了,不用每次指定环境变量了。

3 Comments :, , more...

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(一)

by on 五.24, 2011, under Java

Spring更新到3.0之后,其MVC框架加入了一个非常不错的东西——那就是REST。它的开放式特性,与Spring的无缝集成,以及Spring框架的优秀表现,使得现在很多公司将其作为新的系统开发框架。大象根据实际的项目经验,以之前SSH2例子为基础,对其进行一次大改造,详细的为大家讲解如何实现SSM3全注解式的开发。
这次大象将采取两种构建方式,一是很多人喜欢用的MyEclipse,另一个,则是用Eclipse+Maven。这一篇,将主要讲解开发环境设置与Maven构建方式。

1、开发环境

JDK1.6.0_18
Eclipse3.2.1 MyEclipse5.1.0
Eclipse-JEE-HELIOS-SR2 Maven3.0.1 m2eclipse0.12.1.20110112-1712
Tomcat6.0.10 maven-jetty-plugin6.1.26
MySQL5.0.27 Navicat Lite for MySQL 8.1.20
每个人的开发环境可能会有差异,但有一点我需要说明的是,JDK的版本不得低于1.5,因为用到了很多1.5版才支持的新特性。Tomcat、Jetty、Maven和MySQL请不要低于我所用的版本,因为我没在其它的版本上进行测试。Navicat则是MySQL数据库的图形化操作工具。接下来我将介绍如何在Eclipse3.6中,使用m2eclipse插件构建web应用及测试开发环境。

2、设置Maven

Maven的安装很简单,只需要解压即可,请设置PATH变量,这样可以使用命令行进行操作,然后就要在%MAVEN_HOME%conf目录下,对settings.xml作下修改

这就是设置本地仓库,目录可以根据自己的实际情况更改,不过请使用”/”正斜杠,因为我在实际使用中,发现反斜杠有时候获取不到资源。对于个人使用,设置好这个就OK了,至于Nexus配置不在本文讨论范围内,大家如有兴趣可以去看看juven xu的博客,他是目前公认的Maven专家。

3、安装m2eclipse

选择Help->Install New Software…在弹出的窗口中,点击Add…又会弹出一个小窗口,输入m2eclipse的安装地址,如下图所示:

输入完成后,点击OK,这时Eclipse就开始查找这个插件了,请耐心等一会。

插件找到后,请勾选要安装的内容,接下来就是一般的安装流程了,此处省略500字。安装完成请重新启动Eclipse,然后再对这个插件进行一番设置。

4、设置m2eclipse

进入Perferences,选择Maven,去掉Download repository index updates on startup前的勾,默认情况是会在每次启动Eclipse的时候自动去Maven中央仓库下载索引,这无疑是非常不好的,要知道Maven中央仓库所包含的jar资源非常庞大,而且每天都会有很多新的项目上传,弄不好Eclipse还没开始用,就被这些东西搞挂掉了。

接下来选择Installations将这个插件自带的Maven换成之前安装的Maven,这样就保证了版本的一致性。

设置好这个之后,再点击User Settings,用本机maven的settings.xml替换默认的配置文件,因为默认的设置是会将本地仓库放到系统盘符Documents and Settings用户目录.m2repository这个目录下面。

大家可以看到,用maven中的配置文件替换后,下面的Local Repository自动变更为settings.xml中的设置。

5、创建maven工程

做完这些,我们就可以开始创建Maven工程了。选择New->other…->Maven->Maven Project,然后选择下一步

请设置工作空间路径,大象的默认工作空间放在eclipse的根目录下面,这里的Location显示的应该为空,为了进行说明,特将路径显示出来。确定没问题后,Next>

这个列表显示的就是maven支持的所有项目创建类型,我们是开发web应用,所以请选择maven-archetype-webapp

最后一步输入我们要创建的项目,在Maven世界中,使用坐标来唯一标识一个构件,可以理解为项目,资源等等。Group Id表示当前项目所属的实际项目,Artifact Id定义实际项目中的一个Maven项目,根据名字就可以看出来,Group Id是一个大范围,而Artifact Id是一个小范围。比如大家都很熟悉的spring,就分成了spring-core.jar、spring-beans.jar、spring-context.jar等等。在maven里面,它的Group Id就是org.springframework,而Artifact Id则为spring-core、spring-beans、spring-context。怎么样?理解了没有?

到此,项目生成了,请展开src/main,在main目录下新建一个java文件夹,打开ssm3的项目属性,选择Java Build Path->Source->Add Folder…->勾选java,这样做的目的,就是将src/main/java这个路径作为源文件的文件夹,这和以往用MyEclipse做开发的目录结构是不同的。而maven的规则也是这样定义的,假如你不进行这个设置,就算你在main下面创建了java目录,再添加包或类时,就会有问题,大家试试,看会出现什么错误。

6、运行ssm3

接下来,在pom.xml里面加入maven-jetty-plugin插件,默认生成的配置都可以去掉,整个pom就只有下图所示的配置。

打开Run Configurations,这有多种方式打开,可以从菜单Run里面选,也可以从工具栏选择,还可以在项目点击右键选择。

在弹出的窗口,Maven Build里面设置运行参数,点击Browse Workspace…会弹出下图那个小窗口,设定Base directory,加入jetty:run,点击Run,启动jetty

在浏览器中输入http://localhost:8080/ssm3会显示Hello World!,调用的是ssm3/main/webapp/index.jsp,大象加了点内容,结果就是这样的

到这里,关于在Eclipse里搭建maven环境,配置,创建,运行,测试就全部讲完了,大家动手做做,熟悉一下这种开发方式,接下来就会在这个骨架上开发SSM3示例。恩,我们下次继续。
援引自:http://bolo.blogjava.net/

Leave a Comment :, , , more...

Tomcat 设计模式分析

by on 五.29, 2010, under Java

门面设计模式

门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中、Standard Wrapper 到 ServletConfig 封装中、ApplicationContext 到 ServletContext 封装中等都用到了这种设计模式。

门面设计模式的原理
这么多场合都用到了这种设计模式,那这种设计模式究竟能有什么作用呢?顾名思义,就是将一个东西封装成一个门面好与人家更容易进行交流,就像一个国家的外交部一样。
这种设计模式主要用在一个大的系统中有多个子系统组成时,这多个子系统肯定要涉及到相互通信,但是每个子系统又不能将自己的内部数据过多的暴露给其它系统,不然就没有必要划分子系统了。每个子系统都会设计一个门面,把别的系统感兴趣的数据封装起来,通过这个门面来进行访问。这就是门面设计模式存在的意义。

门面设计模式示意图如下:

图 1. 门面示意图
门面模式

Client 只能访问到 Façade 中提供的数据是门面设计模式的关键,至于 Client 如何访问 Façade 和 Subsystem 如何提供 Façade 门面设计模式并没有规定死。
Tomcat 的门面设计模式示例
Tomcat 中门面设计模式使用的很多,因为 Tomcat 中有很多不同组件,每个组件要相互交互数据,用门面模式隔离数据是个很好的方法。
下面是 Request 上使用的门面设计模式:

图 2. Request 的门面设计模式类图
Request的门面设计模式类图

从图中可以看出 HttpRequestFacade 类封装了 HttpRequest 接口能够提供数据,通过 HttpRequestFacade 访问到的数据都被代理到 HttpRequest 中,通常被封装的对象都被设为 Private 或者 Protected 访问修饰,以防止在 Façade 中被直接访问。

观察者设计模式

这种设计模式也是常用的设计方法通常也叫发布 – 订阅模式,也就是事件监听机制,通常在某个事件发生的前后会触发一些操作。

观察者模式的原理
观察者模式原理也很简单,就是你在做事的时候旁边总有一个人在盯着你,当你做的事情是它感兴趣的时候,它就会跟着做另外一些事情。但是盯着你的人必须要到你那去登记,不然你无法通知它。观察者模式通常包含下面这几个角色:
Subject 就是抽象主题:它负责管理所有观察者的引用,同时定义主要的事件操作。
ConcreteSubject 具体主题:它实现了抽象主题的所有定义的接口,当自己发生变化时,会通知所有观察者。
Observer 观察者:监听主题发生变化相应的操作接口。
Tomcat 的观察者模式示例
Tomcat 中观察者模式也有多处使用,前面讲的控制组件生命周期的 Lifecycle 就是这种模式的体现,还有对 Servlet 实例的创建、Session 的管理、Container 等都是同样的原理。下面主要看一下 Lifecycle 的具体实现。

Lifecycle 的观察者模式结构图:

图 3. Lifecycle 的观察者模式结构图
Lifecycel的观察者模式结构图

上面的结构图中,LifecycleListener 代表的是抽象观察者,它定义一个 lifecycleEvent 方法,这个方法就是当主题变化时要执行的方法。 ServerLifecycleListener 代表的是具体的观察者,它实现了 LifecycleListener 接口的方法,就是这个具体的观察者具体的实现方式。Lifecycle 接口代表的是抽象主题,它定义了管理观察者的方法和它要所做的其它方法。而 StandardServer 代表的是具体主题,它实现了抽象主题的所有方法。这里 Tomcat 对观察者做了扩展,增加了另外两个类:LifecycleSupport、LifecycleEvent,它们作为辅助类扩展了观察者的功能。LifecycleEvent 使得可以定义事件类别,不同的事件可区别处理,更加灵活。LifecycleSupport 类代理了主题对多观察者的管理,将这个管理抽出来统一实现,以后如果修改只要修改 LifecycleSupport 类就可以了,不需要去修改所有具体主题,因为所有具体主题的对观察者的操作都被代理给 LifecycleSupport 类了。这可以认为是观察者模式的改进版。
LifecycleSupport 调用观察者的方法代码如下:

清单 1. LifecycleSupport 中的 fireLifecycleEvent 方法

1
2
3
4
5
6
7
8
9
public void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
    LifecycleListener interested[] = null;
    synchronized (listeners) {
        interested = (LifecycleListener[]) listeners.clone();
    }
    for (int i = 0; i < interested.length; i++)
        interested[i].lifecycleEvent(event);
}

主题是怎么通知观察者呢?看下面代码:

清单 2. 容器中的 start 方法

1
2
3
4
5
6
7
8
9
10
11
12
public void start() throws LifecycleException {
    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;
    synchronized (services) {
        for (int i = 0; i < services.length; i++) {
            if (services[i] instanceof Lifecycle)
                ((Lifecycle) services[i]).start();
            }
        }
    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

命令设计模式

前面把 Tomcat 中两个核心组件 Connector 和 Container,比作一对夫妻。男的将接受过来的请求以命令的方式交给女主人。对应到 Connector 和 Container,Connector 也是通过命令模式调用 Container 的。

命令模式的原理
命令模式主要作用就是封装命令,把发出命令的责任和执行命令的责任分开。也是一种功能的分工。不同的模块可以对同一个命令做出不同解释。
下面是命令模式通常包含下面几个角色:
Client:创建一个命令,并决定接受者
Command 命令:命令接口定义一个抽象方法
ConcreteCommand:具体命令,负责调用接受者的相应操作
Invoker 请求者:负责调用命令对象执行请求
Receiver 接受者:负责具体实施和执行一次请求
Tomcat 中的命令模式的示例
Tomcat 中命令模式在 Connector 和 Container 组件之间有体现,Tomcat 作为一个应用服务器,无疑会接受到很多请求,如何分配和执行这些请求是必须的功能。

下面看一下 Tomcat 是如何实现命令模式的,下面是 Tomcat 命令模式的结构图:

图 4. Tomcat 命令模式的结构图
Tomcat命令模式的结构图

Connector 作为抽象请求者,HttpConnector 作为具体请求者。HttpProcessor 作为命令。Container 作为命令的抽象接受者,ContainerBase 作为具体的接受者。客户端就是应用服务器 Server 组件了。Server 首先创建命令请求者 HttpConnector 对象,然后创建命令 HttpProcessor 命令对象。再把命令对象交给命令接受者 ContainerBase 容器来处理命令。命令的最终是被 Tomcat 的 Container 执行的。命令可以以队列的方式进来,Container 也可以以不同的方式来处理请求,如 HTTP1.0 协议和 HTTP1.1 的处理方式就会不同。

责任链模式

Tomcat 中一个最容易发现的设计模式就是责任链模式,这个设计模式也是 Tomcat 中 Container 设计的基础,整个容器的就是通过一个链连接在一起,这个链一直将请求正确的传递给最终处理请求的那个 Servlet。

责任链模式的原理
责任链模式,就是很多对象有每个对象对其下家的引用而连接起来形成一条链,请求在这条链上传递,直到链上的某个对象处理此请求,或者每个对象都可以处理请求,并传给下一家,直到最终链上每个对象都处理完。这样可以不影响客户端而能够在链上增加任意的处理节点。
通常责任链模式包含下面几个角色:
Handler(抽象处理者):定义一个处理请求的接口
ConcreteHandler(具体处理者):处理请求的具体类,或者传给下家
Tomcat 中责任链模式示例
在 tomcat 中这种设计模式几乎被完整的使用,tomcat 的容器设置就是责任链模式,从 Engine 到 Host 再到 Context 一直到 Wrapper 都是通过一个链传递请求。

Tomcat 中责任链模式的类结构图如下:

图 5. Tomcat 责任链模式的结构图
Tomcat责任链模式的结构图

上图基本描述了四个子容器使用责任链模式的类结构图,对应的责任链模式的角色,Container 扮演抽象处理者角色,具体处理者由 StandardEngine 等子容器扮演。与标准的责任链不同的是,这里引入了 Pipeline 和 Valve 接口。他们有什么作用呢?
实际上 Pipeline 和 Valve 是扩展了这个链的功能,使得在链往下传递过程中,能够接受外界的干预。Pipeline 就是连接每个子容器的管子,里面传递的 Request 和 Response 对象好比管子里流的水,而 Valve 就是这个管子上开的一个个小口子,让你有机会能够接触到里面的水,做一些额外的事情。
为了防止水被引出来而不能流到下一个容器中,每一段管子最后总有一个节点保证它一定能流到下一个子容器,所以每个容器都有一个 StandardXXXValve。只要涉及到这种有链式是处理流程这是一个非常值得借鉴的模式。

引自:developerWorks 中国

Leave a Comment :, , more...

如何使用MyFace快速构建基于JSF的应用

by on 二.02, 2010, under Java

如果大家使用Apache MyFace的JSF实现来搭建JSF应用,可以利用Apache提供的便捷maven方法来快速搭建一个初始的应用。

1
2
3
4
5
6
7
8
9
10
mvn archetype:generate -DarchetypeCatalog=http://myfaces.apache.org
……
Choose archetype:
1: http://myfaces.apache.org -> myfaces-archetype-helloworld (Simple Web application using Apache Myfaces)
2: http://myfaces.apache.org -> myfaces-archetype-helloworld-facelets (Simple Web application using Apache Myfaces and Facelets)
3: http://myfaces.apache.org -> myfaces-archetype-helloworld-portlets (Simple Web application using Apache Myfaces and Portlets)
4: http://myfaces.apache.org -> myfaces-archetype-jsfcomponents (Simple JSF Component using Apache Myfaces)
5: http://myfaces.apache.org -> myfaces-archetype-trinidad (Simple Web application using Apache Myfaces and Trinidad)
Choose a number:  (1/2/3/4/5): 
……

可以看到它提供了你5个初始项目进行选择,你可以根据你的需要选择响应的选项。之后填写响应的参数后,你的应用程序框架就生成了。

然后再使用以下命令,来下载必要的依赖包,假设你的groupId=myAppId,artifactId=yourapp

1
2
cd yourapp
mvn package

之后你就搭建了一个初始框架,你可以继续使用maven来操作这个框架。

Leave a Comment :, , , , , more...

Tomcat 启动gzip压缩输出页面大小及其他优化

by on 六.20, 2009, under Java

在输出的页头中加入

1
Content-Encoding: gzip

可以有效的减少页面的大小,一般可以减小1/3左右。
对于tomcat来说修改起来也很见到, 在tomcat下的conf/server.xml文件中,修改

1
2
3
4
5
6
<connector port="8080" maxHttpHeaderSize="8192" useBodyEncodingForURI="true"
                maxThreads="1000" minSpareThreads="25" maxSpareThreads="75"
                enableLookups="false" redirectPort="8443" acceptCount="100"
                compression="on" compressionMinSize="2048"
 	        compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
                connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="UTF-8"/>

就可以对html,xml,css,javascript和纯文本进行压缩。

其中

  • maxHttpHeaderSize:Http的Header的最大限制
  • maxThreads:Tomcat可创建的最大的线程数
  • minSpareThreads:初始化创建的线程数
  • maxSpareThreads:一旦创建的线程超过这个数,Tomcat就将关闭不再需要的Socket线程
  • enableLookups:使用允许DNS查询,通常情况下设置为false
  • acceptCount:当所有可以使用的处理请求的线程树都被使用时,可以放到请求队列中的请求数,超过这个数的请求将不予处理。其实,该属性与ServerSocket(int port,int backlog)中的backlog参数意义相同,具体可参考ServerSocket的JDK API
  • connectionTimeout:网络连接超时,单位毫秒。设置为0表示永不超时
3 Comments :, , more...

EJB3入门(4)实体Bean

by on 六.17, 2009, under Java

实体Bean就是跟数据库中某个表对应的一个类。 类的每个实例对应数据库的一行记录。如果用过hibernate的人一定很熟悉这个概念。这个就是所谓的ORM模型。Jboss就是使用的Hibernate来实现的。

假设数据库中有这样一个表

mysql 表结构

mysql 表结构


我们来写一个EJB应用,来把用户的密码得到。

先对Jboss做相关配置,便于读取数据库。
设置数据源
拷贝jboss安装目录下docs/jca中的mysql-ds.xml到default/deploy目录中,对其中的参数进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>ejb</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/ejb</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password></password>
    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

再将mysql的驱动拷贝到default/lib中
在源代码目录下的META-INF中建立配置文件persistence.xml,以便ejb项目可以使用jboss的数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
    <persistence-unit name="myentity">
        <jta-data-source>java:/ejb</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="none" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
        </properties>
    </persistence-unit>
</persistence>

其中jta-data-source中java后面的值一定要跟jboss数据源的jndi-name一致

下面就可以开始写一个与上面的表对应的实体Bean了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package me.prosight.entity;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name="users")
public class User {
	private int id;
	private String name;
	private String password;
 
	@Id
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
 
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
 
	@Column(name = "password_md5")
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

@Entity 标记表明这是一个实体bean
@Table 标记声明数据库的表的名字,如果不声明,同类名
@Id 表明这个字段是主键
@Column 标记说明这个属性对应的数据库中的字段名,不声明则同属性名。

我们需要建立一个无状态的session bean来调用实体bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package me.prosight.service;
 
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
import me.prosight.entity.User;
 
/**
 * Session Bean implementation class UserBean
 */
@Stateless
public class UserBean implements UserBeanRemote {
 
	@PersistenceContext(unitName="myentity")
	protected EntityManager em;
 
	public String getPassword() {
		User user = em.find(User.class, 1);
		return user.getPassword();
	}
 
}

其中PersistenceContext中的unitName一定要跟persistence.xml中的persistence-unit中的name一致

最后,再写个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package me.prosight.client;
 
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
import me.prosight.service.UserBeanRemote;
 
public class Client {
	public static void main(String[] args) throws NamingException {
		InitialContext ctx = new InitialContext();
		UserBeanRemote user = (UserBeanRemote)ctx.lookup("UserBean/remote");
 
		System.out.println(user.getPassword());
	}
 
}

部署好ejb后,再使用这个类测试,应该就可以得到数据库中的数据了。

Leave a Comment :, more...

EJB3入门(3)本地接口

by on 六.16, 2009, under Java

之前用到的都是远程接口,顾名思义,远程接口就是提供不再同一个虚拟机中的两端程序来访问的。适合于分布式部署。但是有的时候客户端和服务端又在一个jvm中,比如在一个tomcat或者jboss的web容器中。这个时候就没有必要用远程接口了,毕竟浪费资源。可以改用local接口。

如果你尝试将上一节中的remote直接替换成local,再执行客户端,你会发现如下错误:

1
Could not find InvokerLocator URL at JNDI address "ShoppingCartBean/local"; looking up local Proxy from Remote JVM?

这说明你试图通过远程的jvm来调用本地的端口,因为客户端执行的这个jvm与ejb容器的jvm是两个不同的虚拟机。所以本地接口调用会失败。

在这里我们再建立一个web工程,创建web工程的时候,可以顺便创建一个EAR工程。

创建web工程

创建web工程

然后在这个web工程中加入对之前shoppingcart项目的引用。注意,这里已经将shoppingcart项目中的接口改为local方式。

修改Build Path

修改Build Path

在Java build path中,点击projects标签,点击add按钮,加入shoppingcart的项目

加入相关项目

加入相关项目

之后,在web项目中新建一个jsp页面,如index.jsp,代码如下:

1
 

之后就可以将EAR项目导出到jboss的部署目录中了。

导出EAR

导出EAR

部署好以后,jboss的输出会提示:

1
19:26:53,132 INFO  [TomcatDeployment] deploy, ctxPath=/EJBWeb

之后就可以在浏览器中输入localhost:8080/EJBWeb来测试本地接口了。

Leave a Comment :, more...

EJB3入门(2)Stateful Session bean

by on 六.16, 2009, under Java

有了上节无状态session bean的基础,这回试着做一个有状态的session bean。 有状态的session bean语法上跟无状态session bean只有一个元标记的区别,把实现类里面的@Stateless替换成@Stateful就可以了。

如果使用Stateful Sessionbean,客户端在使用同一个SessionBean对象实例时可以保存状态,也就是说,在多次引用该对象时实际上在服务端是使用的同一个Sessionbean的对象实例,而无状态sessionbean使用了不同的sessionbean对象实例,因此,是无法保存状态的。

最直接的例子就是购物车,购物车是跟着用户来的,需要保持状态,否则人家挑好的东西都不见了。

先写一个接口:

1
2
3
4
5
6
7
8
9
10
11
package me.prosight;
 
import java.util.List;
import javax.ejb.Remote;
 
@Remote
public interface ShoppingCartRemote {
 
	public void addProduct(String name);
	public List<string> getAllProducts();
}

再写一个实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package me.prosight;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.ejb.Stateful;
 
@Stateful
public class ShoppingCartBean implements ShoppingCartRemote {
 
	private List<string> shoppingCart = new ArrayList<string>();
 
	@Override
	public void addProduct(String name) {
		shoppingCart.add(name);
 
	}
 
	@Override
	public List<string> getAllProducts() {
		return shoppingCart;
	}
 
}

最后再写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package me.prosight;
 
import java.util.List;
 
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
public class Client {
 
	public static void main(String[] args) throws NamingException {
		InitialContext ctx = new InitialContext();
		ShoppingCartRemote cart = (ShoppingCartRemote) ctx.lookup("ShoppingCartBean/remote");
		cart.addProduct("Apple");
		cart.addProduct("IBM");
		cart.addProduct("Dell");
 
		List<string> products = cart.getAllProducts();
		for (String product : products)
		{
			System.out.println(product);
		}
 
	}
 
}

在客户端调用时,需要将ShoppingCart接口复制到客户端,当然,@Remote可以去掉。别忘了引用JBoss安装目录中的client目录中的jar文件。客户端仍然使用了jndi.properties文件来配置相应的信息,该文件位于源代码目录下,内容如下;

1
2
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost

在执行上面的程序后,将输出如下的信息:

1
2
3
Apple
IBM
Dell

要注意的是,需要使用同一个sessionbean对象实例(cart)才能保存状态。如果在web应用程序中,需要将cart对象保存在Session中,这样在同一个会话中的用户就可以使用该购物车对象了。

3 Comments :, more...

EJB3入门(1)Stateless Session bean

by on 六.13, 2009, under Java

一直想学EJB,今天有空开始学习。

下载相关资源
JDK 1.6u14 http://java.sun.com
eclipse 3.4 http://www.eclipse.org
Jboss tools http://jboss.org/tools
Jboss AS 5.1.0AS http://www.jboss.org/jbossas

安装配置好后,我们开始写第一个EJB, 先写个无状态的Session Bean — Hello world

先定义接口

1
2
3
4
5
6
7
8
package me.prosight;
import javax.ejb.Remote;
 
@Remote
public interface HelloRemote {
	public String sayHello(String name);
 
}

再定义实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package me.prosight;
 
import javax.ejb.Stateless;
 
/**
 * Session Bean implementation class Hello
 */
@Stateless
public class HelloBean implements HelloRemote {
 
	@Override
	public String sayHello(String name) {
		return "Hello " + name;
	}
 
}

启动Jboss服务器,将刚刚的EJB应用部署到jboss中。右键点击项目名称,然后选择Export–>EJB/JAR file

EJB项目部署

EJB项目部署

将这个Jar包部署到/server/default/deploy中,一会jboss就会自动将这个应用部署到jboss的容器中,后面我们就可以使用这个ejb了。

再写一个测试客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package me.prosight;
 
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
public class HelloClent {
 
	public static void main(String[] args) throws NamingException {
		Context ctx = new InitialContext();
		HelloRemote hello = (HelloRemote) ctx.lookup("HelloBean/remote");
		String msg = hello.sayHello("Elton");
		System.out.println(msg);
	}
 
}

客户端要注意两件事情

  1. 确保正确设置了JNDI
  2. 在引用远程的EJB bean的时候,要使用对应的接口来引用而不是实现类。

JNDI是Java命名和目录接口,是用来标记EJB的bean位置的。 如果你的客户端和EJB不在一个JVM中运行,就需要配置JNDI。当你部署好一个EJB的应用,会在控制台中显示出调用这个EJB的所有的JNDI名称,如:

JNDI

JNDI

在InitialContext的lookup方法中就可以使用这个名称来引用相关的Bean了。

配置JNDI有两个方法,一个就是在源文件目录中建立jndi.properties文件,我的这个文件内容如下:

1
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost:1099

provide.url指定的是EJB容器的地址和端口,可以使用IP地址来指定你的EJB容器装在哪台服务器上。

或者在程序中以编程的方式来指定,在new InitialContext()前,输入如下代码:

1
2
3
4
5
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
props.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ctx = new InitialContext(props);

运行这个程序,将会在控制台中看到:

程序输出

程序输出

第一个EJB的Hello World就完成了。还很简单吧。

工程源代码

Leave a Comment :, more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit my friends!

A few highly recommended friends...