elathen的博客

Oracle AQ 使用(-)

elathen | 04 九月, 2008 16:00

随着不同应用模块间的消息交互和通信成为一个关键的功能,并且变得越来越重要。Oracle引入了一种强大的队列机制,通过它程序间可以实现信息的交互,oracle把它称作为AQ - Advanced Queuing. 使用Oracle AQ,我们不需要安装额外的中间件,它是Oracle数据库的一个功能组件,只要你安装了Oracle 数据库就可以使用AQ了。接下来分两部分来介绍AQ的使用,使用之前我们要创建QUEUE.

我们创建一个自己的AQ的管理角色 "my_aq_adm_role" 和管理用户"aqadm",再把Oracle AQ 管理角色 "aq_adminstrator_role" 授权给"my_aq_adm_role".

CREATE ROLE my_aq_adm_role;

GRANT aq_adminsistator_role TO my_aq_adm_role

创建一个用户的角色 "my_aq_user_role" 普通用户"aquser" ,再把Oracle AQ的用户角色"aq_user_role"和一些基本操作需要的系统权限授权给 "my_aq_adm_role"

CREATE ROLE my_aq_user_role;

GRANT CREATE session, aq_user_role TO my_aq_user_role;

EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(

privilege => 'ENQUEUE_ANY',

grantee => 'my_aq_user_role',

admin_option => FALSE);

EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(

privilege => 'DEQUEUE_ANY',

grantee => 'my_aq_user_role',

admin_option = 'FALSE');

现在我们创建 AQ管理用户

CREATE USER aqadm IDENTIFIED BY aqadm

DEFAULT TABLESPACE elathen

TEMPORARY TABLESPACE temp;

GRANT my_aq_adm_role TO aqadm;

GRANT connect, resource TO aqadm; ----注意,resource角色一定要授给user,如果resource角色授权给

my_aq_adm_role, user将会失去 unlimited tablespace 权限

接着为我们下面example创建普通用户

CREATE USER aquser IDENTIFIED BY aquser

DEFAULET TABLESPACE elathen

TEMPORARY TABLESPACE temp;

GRANT my_aq_user_role TO aquser;

我们将在我们第一个queue中使用object type而不是NUMBER or VARCHAR2作为payload,这使我们更贴近实际应用。(payload是任何消息都使用的一种数据类型和结构).

上面做的工作都需要在DBA的权限下做,现在我们切换到AQ管理员

CONNECT aqadm/aqadm

CREATE TYPE queue_message_type AS OBJECT(

no NUBER,

title VARCHAR2(30),

text VARCHAR2(2000));

/

GRANT EXECUTE ON queue_message_type TO my_aq_user_role;

我们再创建一个叫"message_queue"queue以及相应的queue table "queue_message_table",然后启动queue,这样我们就可以使用了。

1.PL/SQL中使用AQjava使用oracle本地AQ

点对点模型(The Point-to-point Model)

在简单的系统中,我们可以认为两个系统一起使用一个或多个Queue。这种方法我们称作点对点模型。把消息输入到queue中的过程称为入列(Enqueue)相反的过程称为出列(Dequeue)。一条消息一次只能被一个使用这个queue的应用系统Dequeue,当其他应用系统可以浏览这个queue。这种模式就是点对点模式(the point-point Model)

PL/SQL中使用AQ

使用aquser连接到数据库

CONNECT aquser/aquser

现在我们Enqueue一条消息.

DECLARE

queue_options

DBMS_AQ.enqueue_options_t;

message_properties DBMS_AQ.message_properties_t;

message_id

RAW(16);

my_message

aqadm.queue_message_type;

BEGIN

my_message := aqadm.queue_message_type(1,

'This is a sample message',

'This message has been posted on' || to_char(SYSDATE,'DD.MM.YYYY HH24:MI:SS'));

DBMS_AQ.enqueue(queue_name => 'aqadm.message_queue',

enqueue_options => queue_options,

message_properties => message_properties,

payload => my_message,

msgid => message_id);

COMMIT;

END;

/

我们现在Dequeue刚才入列的消息,先执行

SET SERVEROUTPUT ON

然后

DECLARE

queue_options DBMS_AQ.DEQUEUE_OPTIONS_T;

message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;

message_id RAW(2000);

my_message aqadm.queue_message_type;

BEGIN

DBMS_AQ.DEQUEUE(

queue_name => 'aqadm.message_queue',

dequeue_options => queue_options,

message_properties => message_properties,

payload => my_message,

msgid => message_id );

COMMIT;

DBMS_OUTPUT.PUT_LINE(

'Dequeued no: ' || my_message.no);

DBMS_OUTPUT.PUT_LINE(

'Dequeued title: ' || my_message.title);

DBMS_OUTPUT.PUT_LINE(

'Dequeued text: ' || my_message.text);

END;

/

上面的PL/SQL的例子比较简单和直接,任何应用和编程环境都可以这样做。然而实际项目中,可能用编程语言来处理消息会更便利和更有实用价值。接下来我们将讨论在java中使用AQ

Java 中使用Oracle Native AQ

在前面的例子里我们为队列消息创建了一个Oracle Object type "queue_message_type",java语言中我们不能使用Oracle的数据类型,因此我们要创建一个和"queue_message_type"对应的java类。我们可以使用Oracle Jpublisher,通过它,我们可以创建一个和Oracle Object type对应的java类。(这里就不具体讨论JPublisher用法)

这里我们用JPubisher创建一个和Oracle Object type "queue_message_type"对应java class "QUEUE_MESSAGE_TYPE "

在使用Oracle Native AQjavainterface之前,我们必须通过jdbc连接到数据库,代码如下

//loads the Oracle JDBC driver

Class.forName("oracle.jdbc.driver.OracleDriver");

NativeAQ aq = new NativeAQ();

//DB connection, HOST --- 数据库所在的机器domian id SID----数据的service name

aq.connection = DriverManager.getConnection("jdbc:oracle:thin:@HOST:1521:SID","aquser","aquesr")

//

aq.connection.setAutoCommit(false);

然后我们通过传入AQ connection来获取AQ session对象

//loads the Oracle AQ driver

Class.forName("oracle.AQ.AQOracleDriver");

aq.session = AQDriverManager.createAQSession(aq.connection);

上述工作做好后,我们可以获取我们需要的queue对象了,进行出列的操作。

AQQueue queue = aq.session.getQueue("aqadm","MESSAGE_QUEUE")

AQDequeueOption dequeueOption = new AQDequeueOption();

System.out.println("Waiting for message to dequeue……...");

AQMessage message =

((AQOracleQueue)queue).dequeue(dequeueOption,QUEUE_MESSAGE_TYPE.getFactory());

raw payload 转换成我们消息类型

AQObjectPayload payload = message.getObjectPayload();

QUEUE_MESSAGE_TYPE messageData = (QUEUE_MESSAGE_TYPE) payload.getPayloadData();

aq.connection.commit();

System.out.println("Dequeued no: " + messageData.getNo());

System.out.println("Dequeued title: " + messageData.getTitle);

System.out.pritnln("Dequeued text: " + messageData.getText());

小结:Oracle Advanced Queuing是一个功能强大并且使用起来相对简单的产品。它提供的java包使我们不用编写太多的java代码就可以实现enqueuedequeue操作。


恭喜您成功申请ITPUB BLOG

elathen | 14 六月, 2007 12:57

恭喜您成功申请ITPUB BLOG。 查看全文

在珠海的日子 - 最近二年的多的IT职业生涯 (原)

elathen | 23 三月, 2007 22:04

2005年3月5号,带着我的全部家当(一个箱子和一个包)来到珠海。这在我映像中好像是我到过的第11个城市吧。对我来说是一个
陌生的城市。令人欣慰的是公司比较人性化,提前在软件园区内给我订了一个房间。这或多或缺少给让我这个陌生人心理有些欣慰,
安定下来后,买了一张地图,熟悉了一下四周的环境。晚上回到招待所,心里有一种莫名的孤独和失落的感觉,就不停的和女朋友和
朋友发短信聊天以排除内心的那种陌生孤独的感觉。其中一个朋友问我:“你漂泊感觉的累吗?想稳定下来吗?”累呀,当然想稳
定,但有时候人在江湖身不由己。好多事情不是你决定的。去年一年大部分是在外面出差,出差就意味着加班,曾经创下连续上班一
个月,每天干到晚上11点多,最后几天还几乎通宵的记录(但老板还嫌大家工作不努力)。到最后实在无法忍受老板的为人(兼之现
在公司对我来说是一个好的机会),虽然辞职时候老板许若加薪和职位,但我还是离开深圳来到珠海(祝福以前公司的同事,希望他
们越来越好)。新公司人性化的管理和舒适的工作环境,让我坚定了自己的选择。也让我第一次有稳定的感觉。希望这是我新的一个
起点!
2005年3月7号,开始了我在珠海的职业生涯。公司是一个香港的公司,主要是做航运物流的,也许一些朋友已经猜到是什么公司。我所分在的team需要接手一个从香港transfer过来的项目,培训的同事都是来自香港的,由于香港的同事不是很会普通话,所以培训和开会主要是英语和粤语,虽然大学时候英语还行,但在国内的公司呆久了,好久不用,很生疏了,一个星期的培训,我能听懂的不操过10%。一星期后,我就开始了正式的工作,虽然很多东西还不熟悉,但由于team的同事都很热心,这样在他们的帮助下,我开始熟悉公司的整个这块的业务,同时也逐渐熟悉所维护的系统。随着时间的推移,工作已经上手,同时也熟悉整个team的同事,他们都很优秀(当然也有个别不行的),也很热心。和他们在一起,感觉自己变的平庸起来,当同时通过和他们的交流和学习,发现自己也提高了很多,特别感谢huang和hu,hu已经去oracle公司了,和他我们更多的交流,他是一个非常聪明和热心的人,希望他在新的公司一切发展顺利。另一方面收获比较大的是,对技术以外的东西更多理解,IT技术只用应用到实际的业务的中,提高人的生产率,它才能发挥它的作用,当国内好像关注更多的是这个技术是不是最新,最流行的,更多是从技术角度看问题。而忽略了它本来目地。举个例子,我们有一套系统,如果从技术角度看,你会觉的它很差,没有统一的框架,也没用用什么设计模式,没有什么OO的思想,用的语言有ASP,JSP,SP(我们team的同事也认为它很差)。但就是这样的系统跑了近10年,支撑这我们公司在物流领域的这个业务,就是围绕这样一套系统和围绕这套系统一些操作人员,每年的营业额是以亿为单位的(具体数字不能说,商业秘密),虽然它没有采用什么很新很好的技术,但它的目标是通过这套系统,除了支撑整个业务需求,同时,尽量减少了人力成本和运营成本,这才是真正的信息化,回想在国内公司工作的近几年,做了好多政府程,发觉政府搞的信息化,有一大部分都是不是为了应用IT技术提高工作效率和运营成本,更多的是搞一些政绩工程和面子工程,这也造就了国内一大部分技术人员的浮躁,盲目的去追求新的技术,新的概念。(如果说的不对,请原谅)。从来公司到06年10月份,一直在做这套系统的maintain和enhancement,同时也对整个物流领域有了更深刻的认识,同时也非常感谢现在的公司,给我提供一个这样的机会。希望公司越来越好
从2006年10月份,由于公司这边接手了一个项目的开发,我从以前的组调到了现在的项目组,和国内的公司不一样,光这个项目移交
公司需要投入100多万,移交的时间定为6个月.接下来的时间就是业务和技术框架的培训,虽然过去的一年多的时间,一直做这一块,
但对业务的理解都很零散,通过这次移交的业务培训,对整个业务有更整体的理解和认识.技术上采用的是一套自己开发的框架,由于
开发的比较早(2001年开发设计的),站在现在的角度看,有很多地方设计的不是很好,但放在当时,应该算是很好的框架.将来要开发的
系统就是基于这个框架.接下来的2个多月时间里,天天8小时都是培训,感觉很累.有时候会想,公司是不是小题大做,需要培训这么久么
这如果放在国内的公司是不可想象的.同时为了和大家更好的协作,我也从以前组的位置,搬到新的位置,远离了那些可爱的同事.虽然
新的项目组的同事大家都见过,但都不是很熟悉,感觉又来到一个新的环境,又需要一段磨合期,随着时间的推移,发现自己有点不适应
现在的氛围,在以前的项目组,大家比较open经常讨论一些技术和社会问题,但现在的项目组,大家都顾自己闷头干活,再加上项目
比较紧,心情也越来越郁闷了,在老婆和老妈嘱咐下借掉的烟,又从新回来了.转眼到了2007年了,工作5年了,年龄大了,需要考虑的事情太多了,以后的职业发展道路,在那里定居,什么时候结婚,这一切的一切都需要自己去面对和思考,自己面临的挑战和压力也越来越多.也许这就是人生!

OO设计的一些规则 - 摘自Head First Design Pattern(原)

elathen | 23 三月, 2007 21:51

1.Encapsulate what varies

2.Favor composition over inheritance

3.Program to interface, not implementation

4.Strive for loosely coupled design between objects that interact

5.Depend on abstractions. Do not depend on concrete classes

6.Only talk to you friends

7.Don't call us, we'll call you

8.A class should have only one reason to change

-摘自Head First Design Patter 一本讲设计模式的好书,Jolt2006大奖的书,很值得读! 查看全文

回车键的问题(原)

elathen | 21 二月, 2007 22:08

MS DOS/Windows 和unix中行尾回车键是不同的,在windows 中回车键返回的字符是CR/LF,2个bytes,对应的十六进制是"0D0A",而在unix中回

车的返回字符是LF,1个字节,对应的十六进制是"0A"。在java中,为了使程序的可移植性,在生成文件时,我们可以通过在每一行的结尾加

上System.getProperty("line.separator"),这样程序会根据不同平台使用不同的回车键返回字符。

^-^ 不要小看这多余的一个byte,在商业的EDI中,数据传输是以byte来算钱的。

使用CGLIB包创建动态代理(3)(原)

elathen | 16 一月, 2007 23:01

Use a MethodInterceptor为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:

package com.lizjason.cglibproxy;

import java.lang.reflect.Method;

/**
* A simple authorization service for illustration purpose.
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public interface AuthorizationService {
/**
* Authorization check for a method call. An
AuthorizationException
* will be thrown if the check fails.
*/
void authorize(Method method);
}
net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import com.lizjason.cglibproxy.AuthorizationService;

/**

* A simple MethodInterceptor implementation to

* apply authorization checks for proxy method calls.

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*

*/

public class AuthorizationInterceptor implements MethodInterceptor {

private AuthorizationService authorizationService;

/**

* Create a AuthorizationInterceptor with the given

* AuthorizationService

*/

public AuthorizationInterceptor (AuthorizationService authorizationService) {

this.authorizationService = authorizationService;

}

/**

* Intercept the proxy method invocations to inject authorization check.

* The original method is invoked through MethodProxy.

* @param object the proxy object

* @param method intercepted Method

* @param args arguments of the method

* @param proxy the proxy used to invoke the original method

* @throws Throwable any exception may be thrown; if so, super method will not be invoked

* @return any value compatible with the signature of the proxied method.

*/

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {

if (authorizationService != null) {

//may throw an AuthorizationException if authorization failed

authorizationService.authorize(method);

}

return methodProxy.invokeSuper(object, args);

}

}


我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIBnet.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用
java.lang.reflect.Method
对象。


Use a CallbackFilter

net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:saveload,其中方法save需要权限检查,而方法load不需要权限检查。



package com.lizjason.cglibproxy.impl;

import com.lizjason.cglibproxy.PersistenceService;

/**

* A simple implementation of PersistenceService interface

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceImpl implements PersistenceService {

public void save(long id, String data) {

System.out.println(data + " has been saved successfully.");

}

public String load(long id) {

return "Jason Zhicheng Li";

}

}

注意到PersistenceServiceImpl类实现了PersistenceService 接口,因此没有要求要使用CGLIB创建代理。
net.sf.cglib.proxy.CallbackFilter
接口的实现如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.CallbackFilter;

/**

* An implementation of CallbackFilter for PersistenceServiceImpl

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceCallbackFilter implements CallbackFilter {

//callback index for save method

private static final int SAVE = 0;

//callback index for load method

private static final int LOAD = 1;

/**

* Specify which callback to use for the method being invoked.

* @method the method being invoked.

* @return the callback index in the callback array for this method

*/

public int accept(Method method) {

String name = method.getName();

if ("save".equals(name)) {

return SAVE;

}

// for other methods, including the load method, use the

// second callback

return LOAD;

}

}


accept
方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。


...

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(PersistenceServiceImpl.class);

CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();

enhancer.setCallbackFilter(callbackFilter);

AuthorizationService authorizationService = ...

Callback saveCallback = new AuthorizationInterceptor(authorizationService);

Callback loadCallback = NoOp.INSTANCE;

Callback[] callbacks = new Callback[]{saveCallback, loadCallback };

enhancer.setCallbacks(callbacks);

...

return (PersistenceServiceImpl)enhancer.create();

在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过
net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])
方法指定代理对象所实现的接口。

除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用,net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。你可以从http://www.lizjason.com/downloads/下载设置回调类型和接口的完整代码。

小结 GLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。 它底层使用字节码处理框架ASM。其原理是,生产一个要代理类的子类,子类覆盖要代理的类的所有不是final的方法。 它比使用java反射的JDK动态代理要快。通常情况下,你可以使用JDK的动态代理创建代理,当你要代理的类没有实现接口 或者为了更好的性能,CGLIB是一个好的选择。


使用CGLIB包创建动态代理(2)(原)

elathen | 14 一月, 2007 23:00

当net.sf.cglib.proxy.MethodInterceptor做为所有代理方法的回调(callback)时,当对基于代理的方法调用时,在调用原对象的方法的之前会调用这个方法,如图3所示。第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。
CGLIB MethodInterceptor

Figure 3: CGLIB MethodInterceptor


net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要,当对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型。例如:
  • net.sf.cglib.proxy.FixedValue
    为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
  • net.sf.cglib.proxy.NoOp
    NoOp回调把对方法调用直接委派到这个方法在父类中的实现。
  • net.sf.cglib.proxy.LazyLoader
    当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
  • net.sf.cglib.proxy.Dispatcher
    Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
  • net.sf.cglib.proxy.ProxyRefDispatcher
    ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

    如图3所示,代理类的所以方法经常会用到回调(callback),当是你也可以使用net.sf.cglib.proxy.CallbackFilter 有选择的对一些方法使用回调(callback),这种考虑周详的控制特性在JDK的动态代理中是没有的。在JDK代理中,对java.lang.reflect.InvocationHandler方法的调用对代理类的所以方法都有效。

    除了代理类外,CGLIB通过提供一个对java.lang.reflect.Proxy的drop-in替代来实现对对接口的代理。因为这种代理能力的替代很少被用到,因此相应的APIs也很少提到。

    CGLIB的代理包也对net.sf.cglib.proxy.Mixin提供支持。基本上,它允许多个对象被绑定到一个单个的大对象。在代理中对方法的调用委托到下面相应的对象中。

    接下来我们看看如何使用CGLIB代理APIs创建代理。

    创建一个简单的代理CGLIB代理最核心的是net.sf.cglib.proxy.Enhancer类,为了创建一个代理,最起码你要用到这个类。首先,让我们使用NoOp回调创建一个代理:
    /**
    * Create a proxy using NoOp callback. The target class
    * must have a default zero-argument constructor.
    *
    * @param targetClass the super class of the proxy
    * @return a new proxy for a target class instance
    */
    public Object createProxy(Class targetClass) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetClass);
    enhancer.setCallback(NoOp.INSTANCE);
    return enhancer.create();
    }
    返回值是target类一个实例的代理。在这个例子中,我们为net.sf.cglib.proxy.Enhancer 配置了一个单一的回调(callback)。我们可以看到很少直接创建一个简单的代理,而是创建一个net.sf.cglib.proxy.Enhancer的实例,在net.sf.cglib.proxy.Enhancer类中你可使用静态帮助方法创建一个简单的代理。一般推荐使用上面例子的方法创建代理,因为它允许你通过配置net.sf.cglib.proxy.Enhancer实例很好的控制代理的创建。

    要注意的是,target类是作为产生的代理的父类传进来的。不同于JDK的动态代理,它不能在创建代理时传target对象,target对象必须被CGLIB包来创建。在这个例子中,默认的无参数构造器时用来创建target实例的。如果你想用CGLIB来创建有参数的实例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一个参数定义了参数的类型,第二个是参数的值。在参数中,基本类型应被转化成类的类型。

    ------------
    如果要装载,请注明出处


  • 使用CGLIB包创建动态代理(1)(原)

    elathen | 12 一月, 2007 22:58

    1.CGLIB包的介绍 代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包
    CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
    CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    CGLIB and ASM
    Figure 1: CGLIB Library and ASM Bytecode Framework图一显示了和CGLIB包和一些框架和语言的关系图。需要注意的是一些框架例如Spring AOP和Hibernate,它们为了满足需要经常同时使用JDK的动态代理和CGLIB包。Hiberater使用JDK的动态代理实现一个专门为WebShere应用服务器的事务管理适配器;Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。

    2.CGLIB 代理的APIS
    CGLIB包的基本代码很少,当学起来有一定的困难,主要是缺少文档,这也是开源软件的一个不足之处。目前CGLIB的版本是(2.1.2),主要由一下部分组成:
  • net.sf.cglib.core
    Low-level bytecode manipulation classes; Most of them are related to ASM.
  • net.sf.cglib.transform
    Classes for class file transformations at runtime or build time
  • net.sf.cglib.proxy
    Classes for proxy creation and method interceptions
  • net.sf.cglib.reflect
    Classes for a faster reflection and C#-style delegates
  • net.sf.cglib.util
    Collection sorting utilities
  • net.sf.cglib.beans
    JavaBean related utilities

    大多时候,仅仅为了动态地创建代理,你仅需要使用到代理包中很少的一些API。

    正如我们先前所讨论的,CGLIB包是在ASM之上的一个高级别的层。对代理那些没有实现接口的类非常有用。本质上,它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors),这比JDK动态代理方法快多了。

    CGLIB APIs commonly used for proxying classes

  • Figure 2: CGLIB library APIs commonly used for proxying classes

    图2为我们演示了创建一个具体类的代理时,通常要用到的CGLIB包的APIs。net.sf.cglib.proxy.Callback接口在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。net.sf.cglib.pr用oxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
    public Object intercept(Object object, java.lang.reflect.Method method,
    Object[] args, MethodProxy proxy) throws Throwable;




    (原文请参照http://www.ociweb.com/jnb/jnbNov2005.html

    ------------
    如果要装载,请注明出处


  • 第16届Jolt奖获奖名单 (转)

    elathen | 14 六月, 2006 21:53

    The 16th Annual Software Development Jolt Product Excellence Awards

    Winners by Category
    (Click here to view this year's list of finalists.)

    1. Books: Practical/General Developer Interest: Computer history, best practices, project management, developer or team efficiency, and socioeconomic, regulatory or career issues relating to software.

    Jolt Winner: Prefactoring by Ken Pugh (O'Reilly)

    2. Books: Technical: Specific languages, technologies, platforms, hardware, operating systems, coding styles, modeling, architecture, source-code optimization and code-level security.

    Jolt Winner: Agile Web Development with Rails by Dave Thomas, David Hansson, Leon Breedt and Mike Clark (Pragmatic Bookshelf)

    3. Enterprise Project Management Tools: Tools for enterprise application integration (EAI), customer relationship management (CRM), business-process orchestration or integration, middleware and database applications that streamline business processes and improve integration.

    Jolt Winner: WelcomRisk 2.6 (Welcom)

    4. Database Engines and Data Tools: Tools that help facilitate, maintain and manage the storage and retrieval of data and objects, including search tools for both desktop and enterprise.

    Jolt Winner: Microsoft SQL Server 2005 (Microsoft)

    5. Defect Tracking, Change and Configuration Management Tools: Tools that track and manage software defects and enhancement requests as well as version control and change or configuration management.

    Jolt Winner: Perforce SCM 2005 (Perforce)

    6. Modeling and Design Tools: Tools for requirements gathering, modeling, automatic code generation from models, and GUI or prototype design.

    Jolt Winner: Lattix LDM 2.0 (Lattix)

    7. Development Environments: Innovative programming languages, source code editors and integrated development environments (IDEs).

    Jolt Winner: Visual Studio Team System 2005 (Microsoft)

    8. Libraries, Frameworks and Components: Libraries of methods for enhancing security, transactions, graphical user interfaces and database connectivity; also stand-alone components, component libraries or services and COTS. Frameworks and components are systems that allow for the modification and extensions to build your application.

    Jolt Winner: .NET Framework 2.0 (Microsoft)

    9. Quality and Project Management Tools: Planning, analysis and scheduling tools that aid in estimating costs, program size and development effort, and managing risks and budgets. Also tools for tracking productivity metrics, recommending software development processes or best practices, and guiding technology selection.

    Jolt Winner: Rally 5.6 (Rally Software Development)

    10. Mobile Development Tools: Application tools for mobile handheld development, including IDEs, network management and roll-out.

    Jolt Winner: Crossfire 5.6 (AppForge)

    11. Security Tools: Tools for authentication and encryption, firewalls, network monitoring, system enhancements, system monitoring and spam.

    Jolt Winner: Elemental Compliance System 1.4 (Elemental)

    12. Testing Tools: Tools for test scripting, automation, tracking and documentation; quality assurance planning; test-case design; white- and black-box testing.

    Jolt Winner: VMTN Subscription 2005 (VMware)

    13. Utilities: Code optimizers and coding standards tools, build tools, platform and device emulators, code obfuscators, messaging solutions, help-authoring tools, product-demonstration tools and reverse-engineering tools.

    Jolt Winner: Camtasia Studio 3.0 (TechSmith)

    14. Web Development Tools: Tools for developing and deploying Web-based applications, including application servers and infrastructure to help Web applications interact dynamically with other Web applications, and tools using open standards such as XML, SOAP and HTML.

    Jolt Winner: Rails 1.0 (rubyonrails.org)

     查看全文

    订婚归来!(原)

    elathen | 07 五月, 2006 22:05

    好久没有更新Blog了,3-4月份那段时间,人感觉好累,老提不起精神,所以Blog也暂停了一段时间。从04.22-05.8,是我来公司后的第一次休的长假,主要还是因为要和女友回她老家订婚,同时也顺便休息一下调整一下自己。说是休假,当我们的行程还是安排的很紧的,女友的老家在河南的鲁山的一个山村,是个美丽的地方。4.23号我们到达郑州,郑州是我和我女友上学的地方,也是我们相识、相爱的地方。中午参加我大学室友的婚礼,他们是从大学开始的,他们从开始到现在我可以算一个见证人吧,终于走进婚礼的殿堂,真心的祝福他们。晚上,和以前的同事好友相聚了一下,品尝了一下我想了好久的烩面。4.24日和女友逛商城,给她和自己买了一些衣服,最重要的是给女友买了一个白金戒指,一个上面有心形状的,99%的含金量,代表我对她的爱是真心真意的,也是我们订婚的见证,等结婚时一定给她买个钻戒。本来还看重一对和戒指很配的耳环,可是女友不让我买,其实我很想买,我知道她是为我省钱,当我不想委屈她。4.27号,我们回到我女友的家,一个山青水绣的地方,空气非常好,在这里没有城市的喧闹,很舒服。女友的母亲是一个慈祥的母亲,对我像对她自己的儿子一样,让我感到很温馨。接下来几天里,我们就是走亲戚。虽然这里地处山区,有点贫穷,当是山里人的热情、朴实、善良,让我感受挺深的。和他们一起聊天,我感觉轻松愉快,希望他们将来的生活越来越好。5.5日我先回到珠海,女友要在家多陪陪妈妈几天。
    明天开始上班了,也是新的开始,以后要更加努力的工作和学习。给老婆提供一个温馨幸福的家,让她快快乐乐的生活。


    关于Java开发中使用Oracle数据库的一点注意事项(原)

    elathen | 04 一月, 2006 22:21

    很多朋友在Java开发中,使用Oracle数据库的时候,经常会碰到有ORA-01000: maximum open cursors exceeded.的错误。 实际上,这个错误的原因,主要还是代码问题引起的。
    ora-01000: maximum open cursors exceeded.
    表示已经达到一个进程打开的最大游标数。 这样的错误很容易出现在Java代码中的主要原因是:Java代码在执行conn.createStatement()conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个cursor。尤其是,如果你的createStatementprepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因为游标一直在不停的打开,而且没有关闭。 一般来说,我们在写Java代码的时候,createStatementprepareStatement都应该要放在循环外面,而且使用了这些Statment后,及时关闭。最好是在执行了一次executeQueryexecuteUpdate等之后,如果不需要使用结果集(ResultSet)的数据,就马上将Statment关闭。 对于出现ORA-01000错误这种情况,单纯的加大open_cursors并不是好办法,那只是治标不治本。实际上,代码中的隐患并没有解除。 而且,绝大部分情况下,open_cursors只需要设置一个比较小的值,就足够使用了,除非有非常特别的要求。

    对这个问题专门研究了一下,并写了测试程序,首先说一些测试环境,我们的数据库最大可打开的cursor设置为600,操作的数据库表的记录数为30条记录。为了模拟同时打开600cursor,也就是说有600statement在运行,必须用到java的多线程。下面是测试程序:

    public class StatementTest extends Thread{
    private Connection conn;
    public StatementTest(Connection conn) {
    this.conn = conn;
    start();
    }

    public void run(){
    try {
    String strSQL = "SELECT * FROM TestTable"
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(strSQL);
    int i = 0;
    while(rs.next()){
    System.out.println("----"+i+"------");
    i = i+1;
    }
    rs.close();
    stmt.close();

    } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
    }

    }


    public static void main(String args[]){
    try{
    Connection conn = DBConnection.getConnection();

    for(int i = 0;i < 800;i++){

    new StatementTest(conn);
    }
    }catch(Exception e){
    e.printStackTrace();
    }

    }

    }

    运行这个程序,很快就会抛出ORA-01000: maximum open cursors exceeded的错误。

    这次最主要的不是测试上面的问题,而是下面的问题:1. 我们通过JDBC查询,是否一次是把所有的结果集查询出来后放到ResultSet中,

    2. 在调用ResultSet.next()方法的时候,是否还会和数据库交互.

    其实在以前我一直认为当statement执行完sql语句后,它会把结果集保存到ResultSet中,然后关闭这个cursor,当实际上并不是这样的。我们把上面的程序修改一下就可以测试这个问题了。程序如下public class StatementTest extends Thread{
    private Connection conn;
    public StatementTest(Connection conn) {
    this.conn = conn;
    start();
    }

    public void run(){
    try {
    String strSQL = "SELECT * FROM TestTable"
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(strSQL);
    int i = 0;
    while(rs.next()){
    System.out.println("----"+i+"------");
    i = i+1;
    Thread.sleep(5000);
    }
    rs.close();
    System.out.println("resultset has closed");
    Thread.sleep(10000);
    stmt.close();
    System.out.println("resultset has closed");
    Thread.sleep(10000);

    } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
    }

    }


    public static void main(String args[]){
    try{
    Connection conn = DBConnection.getConnection();
    new StatementTest(conn);
    }catch(Exception e){
    e.printStackTrace();
    }

    }

    }
    statement执行完SQL语句的时候,循环resultset的时候让线程sleep5秒钟,这样做的目的是为了有时间从数据库那方面查询这个cursor是否关闭.运行程序,java控制台中出现System.out.println("----"+i+"------")循环信息的时候,sqlplus中运行下面SQL语句,其中SID是程序运行对应数据库的进程ID

    select sql_text from v$open_cursor where sid = 35
    你会ResultSet循环的时候,运行上面的SQL,一直有结果都是"SELECT * FROM TestTable"也就是说通过statement打开的这个cursor一直没有关。可以初步判断当statement执行完sql语句后返回给resultset的结果集不是实际的结果(结果数集据不是放到JVM内存中),可能是结果集的指针,当resultset.next()的时候再通过这个指针从oracle拿数据。
    为了确认这个问题,我们选了一个记录数超过10000条记录的大表,来测试,发现在整个运行时,JVM的内存没有很大的变化。这就进一步确定了上面的判断。

    小结:通过上面测试和分析,我们可以的出结论通过JDBC查询结果集不是直接放到ResultSet中,ResultSet存在的可能是结果集的指针。
    我们在调用ResultSet.next()时仍然需要和数据库交互。


    重装windows后,修复Fedora core3的grub (原)

    elathen | 01 一月, 2006 23:03

    我的机器上装有2套系统,一是windowsxp,另一套是RedHat,我装的是Fedora core 3,由于windowsXP
    系统不小心崩溃了,从新安装XP后,linux引导界面没有了,怎么办。本人是linux的初学者,从网上查找
    资料,知道是从装xp后,linux的grub破坏了,需要修复。
    1.把安装盘的第一张放到光驱,然后重新启动机器,在BOIS中把系统用光驱来引导
    2.等安装界面出来后选择F5(Linux Rescue)(会有提示),linux以前版本好像是F4,Fedora core 3中F5是修复
    3.出现boot:提示符,在此处输入linux rescue回车,接着会提示你选择安装的语言你可以选择中文也可以是英文,
    一直回车,当出现提示配置网络的时候,你选择NO。
    4.会出现sh 3.0# 提示符,输入grub后回车,进入grub>
    5.在grub>下输入 grub>root (hdX,Y)
    grub>setup (hd0)
    (x,y的值根据实际情况赋值,我只有一个硬盘,windows和linux都安装在这个盘上,所以x=0,linux下我有2个分区hd0,
    hd1,0是交换分区,1安装linux,所以y=1)
    6.回车以后,出现提示信息,如果都是succeed,那么就修复成功了,接着我们退出和重起系统
    7.在grub>下输入reboot后,退到boot:提示符下面,接着输入exit,重启系统后,进入grub
    又出现了windows和linux的选择页面。

    第15届Jolt奖获奖名单(转)

    elathen | 22 三月, 2005 21:48

    15th Annual Software Development
    Jolt Product Excellence Awards

    Finalists

    Books: General

  • Agile Project Management: Creating Innovative Products by Jim Highsmith (Addison-Wesley Professional)
  • Crystal Clear: A Human-Powered Methodology for Small Teams by Alistair Cockburn (Addison-Wesley Professional)
  • Exploiting Software: How to Break Code by Greg Hoglund and Gary McGraw (Addison-Wesley Professional )
  • Head First Design Patterns by Elisabeth Freeman, Eric Freeman, Bert Bates and Kathy Sierra (O'Reilly)
  • Hiring the Best Knowledge Workers, Techies & Nerds: The Secrets & Science of Hiring Technical People by Johanna Rothman and Gerald M. Weinberg (Dorset House)
  • Joel on Software by Joel Spolsky (Apress)
  • Refactoring to Patterns by Joshua Kerievsky (Addison-Wesley Professional)
  • Software Factories: Assembling Applications with Patterns, Models, Frameworks and Tools by Jack Greenfield, Keith Short, Steve Cook, Stuart Kent, John Crupi (Wiley)

    Books: Technical

  • Better, Faster, Lighter Java by Bruce A. Tate and Justin Gehtland (O'Reilly)
  • C++ Coding Standards: 101 Rules, Guidelines, and Best Practices by Herb Sutter and Andrei Alexandrescu (Addison-Wesley Professional )
  • Head First Servlets & JSP by Bert Bates, Kathy Sierra and Bryan Basham (O'Reilly)
  • Hibernate: A Developer's Notebook by James Elliott (O'Reilly)
  • Java Developer's Guide to Eclipse, Second Edition by Jim D'Anjou, Scott Fairbrother, Dan Kehn, John Kellerman, Pat McCarthy (Addison-Wesley Professional)
  • Mono: A Developer's Notebook by Edd Dumbill and Niel M. Bornstein (O'Reilly)
  • Perl Medic: Transforming Legacy Code by Peter J. Scott (Addison-Wesley Professional)
  • Physically Based Rendering: Theory to Implementation (The Interactive 3d Technology Series) by Matt Pharr and Greg Humphreys (Morgan Kaufmann)

    Business Integration and Data Tools

  • Compiere 5.1 (Compiere/open source)
  • DT/Studio 2.3 (Embarcadero Technologies)
  • EnterpriseTenFold (TenFold)
  • ILOG Rules for .NET (ILOG)
  • MapForce 2005 Enterprise Edition (Altova)
  • sforce 5.0 (salesforce.com)
  • Speech Server (Microsoft)
  • Toad for Oracle8.0 (Quest Software)

    Change and Configuration Management Tools

  • AccuRev 3.5 (AccuRev)
  • CodeBeamer 3.5 (Intland Software)
  • Dimensions 9.0 (Serena Software)
  • Perforce SCM (Perforce)
  • SourceForge Enterprise Edition 4.2 (VA Software)
  • Subversion 2004 (CollabNet)

    Design Tools

  • MagicDraw UML 9.0 (No Magic)
  • MetaEdit+ 4.0 (MetaCase)
  • Rational Software Architect (IBM)
  • Smart Development Environment 2.0 (Visual Paradigm International)
  • SmartDraw 2.0 (SmartDraw.com)
  • Together Designer 2005 (Borland)
  • Visual UML 4.1 (Visual Object Modelers)
  • WayPointer Highlighter 4.0 (Jaczone AB)

    Languages and Development Environments

  • Borland Delphi 2005 (Borland)
  • CodeRush 1.1 for Visual Studio (Developer Express)
  • Eclipse 3.0 (Eclipse Foundation)
  • IntelliJ IDEA4.5 (JetBrains)
  • JBoss AOP 1.0 (JBoss)
  • Python2.4 (Python.org)
  • REALbasic 5.5 for Windows Professional Edition (REAL Software)
  • Sun Java Studio Enterprise 7 (Sun Microsystems)

    Libraries, Frameworks and Components

  • Big Faceless Java PDF Library 2.0 (Big Faceless Organization)
  • ComponentOne Studio Enterprise 4.0 (ComponentOne)
  • Hibernate 2.1 (JBoss)
  • ImageGear Professional 14 (AccuSoft)
  • LEIF 2.2 (Rogue Wave)
  • Sun Java 2 Platform Standard Edition 5.0 (Sun Microsystems)
  • Xtreme Toolkit 9.51 Professional Edition (Codejock)

    Management Tools

  • Active! Focus 2.0 (Xapware Technologies)
  • Autotask 10.0 (Autotask)
  • CaliberRM2005 (Borland)
  • Cost Xpert 3.3 (Cost Xpert Group)
  • Rational Portfolio Manager (IBM)
  • Primavera IT Project Office 4.1 (Primavera Systems)
  • SpeeDEV 4.0 - LME (Lifecycle Management Enterprise) (SpeeDEV)
  • V1: Scrum1.5 (VersionOne)

    Mobile Development Tools

  • CodeWarrior for Symbian 3.0 Professional (Nokia)
  • ComponentOne 4.0 Studio Enterprise (ComponentOne)
  • Crossfire 5.5 (AppForge)
  • Flash Lite 1.1 (Macromedia)
  • J2ME Wireless Toolkit 2.2 (Sun Microsystems)

    Security Tools

  • Aladdin eSafe4 (Aladdin Knowledge Systems)
  • Aladdin eToken (Aladdin Knowledge Systems)
  • CounterPoint 1.0 (Mirage Networks)
  • Source Code Analysis 3.0 (Fortify Software)
  • Internet Security & Acceleration (ISA) Server2004 (Microsoft Corporation)
  • POPFile .22.2 (The POPFile Project; open source)

    Test: Automated Test Tools

  • Agitar Agitator and Dashboard 2.0 (Agitar)
  • Eclipse Test & Performance Tools Platform (TPTP) and Testing Tool 3.0 (the Eclipse Foundation)
  • Jmeter 2.0 (The Apache Foundation; open source)
  • LISA 2.5 (iTKO Inc.)
  • Parasoft Jtest 5.1 (Parasoft)
  • QA Wizard 2.4(Seapine)
  • TestComplete 3.0 (AutomatedQA)

    Test – Defect Tracking Tools

  • Census 6.0 (MetaQuest Software)
  • FogBugz 3.1 (Fog Creek Software)
  • JIRA 3.0 (Atlassian Software Systems)
  • OnTime 2004 for Web and Windows (Axosoft)
  • PowerTrack 2004 Add-In for Visual Studio (Axosoft)
  • TestTrack Pro 7.0 (Seapine)

    Utilities

  • AppExpress 2.5 (Endeavors Technology)
  • Captivate 2004 (Macromedia)
  • devAdvantage 2.1 (Anticipating Minds)
  • InstallShield 10.5 (Macrovision)
  • Omea Pro 1.0 (JetBrains)
  • Quest JProbe 5.2 (Quest Software)
  • Windows Installer XML 2.0 (WiX) (Microsoft)

    Web Development Tools

  • CollabNet Enterprise Edition (CollabNet)
  • Contribute 3.0 (Macromedia)
  • FlexWiki 1.8 (Microsoft Shared Source Initiative)
  • Macromedia Flex 1.5 (Macromedia)
  • NitroX 2.0 (M7 Corporation)
  • Sun Java Studio Creator (Sun Microsystems)
  • Tomcat 5.0 (The Apache Jakarta Project and leading Tomcat contributor JBoss)
  • Zend Studio 4.0 (Zend Technologies)

    Websites and Developer Networks

  • Agile Modeling Home Page (Ambysoft Inc.)
  • developer.* (DeveloperDotStar.com) (Daniel Read)
  • eBay Developers Program (eBay)
  • gantthead.com (gantthead.com)
  • IBM developerWorks (IBM)
  • Java.net (Sun Microsystems)
  • The O'Reilly Network (O'Reilly)

    source: http://www.sdmagazine.com/jolts/15th_jolt_finalists.html


  • 利用 ThreadLocal 提高可伸缩性(转)

    elathen | 24 一月, 2005 21:45

    利用 ThreadLocal 提高可伸缩性

    ThreadLocal 类是悄悄地出现在 Java 平台版本 1.2 中的。虽然支持线程局部变量早就是许多线程工具(例如 Posix pthreads 工具)的一部分,但 Java Threads API 的最初设计却没有这项有用的功能。而且,最初的实现也相当低效。由于这些原因,ThreadLocal 极少受到关注,但对简化线程安全并发程序的开发来说,它却是很方便的。在轻松使用线程的第 3 部分,Java 软件顾问 Brian Goetz 研究了 ThreadLocal 并提供了一些使用技巧。
    参加 Brian 的多线程 Java 编程讨论论坛以获得您工程中的线程和并发问题的帮助。

    编写线程安全类是困难的。它不但要求仔细分析在什么条件可以对变量进行读写,而且要求仔细分析其它类能如何使用某个类。有时,要在不影响类的功能、易用性或性能的情况下使类成为线程安全的是很困难的。有些类保留从一个方法调用到下一个方法调用的状态信息,要在实践中使这样的类成为线程安全的是困难的。

    管理非线程安全类的使用比试图使类成为线程安全的要更容易些。非线程安全类通常可以安全地在多线程程序中使用,只要您能确保一个线程所用的类的实例不被其它线程使用。例如,JDBC Connection 类是非线程安全的 — 两个线程不能在小粒度级上安全地共享一个 Connection — 但如果每个线程都有它自己的 Connection,那么多个线程就可以同时安全地进行数据库操作。

    不使用 ThreadLocal 为每个线程维护一个单独的 JDBC 连接(或任何其它对象)当然是可能的;Thread API 给了我们把对象和线程联系起来所需的所有工具。而 ThreadLocal 则使我们能更容易地把线程和它的每线程(per-thread)数据成功地联系起来。

    什么是线程局部变量(thread-local variable)?
    线程局部变量高效地为每个使用它的线程提供单独的线程局部变量值的副本。每个线程只能看到与自己相联系的值,而不知道别的线程可能正在使用或修改它们自己的副本。一些编译器(例如 Microsoft Visual C++ 编译器或 IBM XL FORTRAN 编译器)用存储类别修饰符(像 static 或 volatile)把对线程局部变量的支持集成到了其语言中。Java 编译器对线程局部变量不提供特别的语言支持;相反地,它用 ThreadLocal 类实现这些支持,核心 Thread 类中有这个类的特别支持。

    因为线程局部变量是通过一个类来实现的,而不是作为 Java 语言本身的一部分,所以 Java 语言线程局部变量的使用语法比内建线程局部变量语言的使用语法要笨拙一些。要创建一个线程局部变量,请实例化类 ThreadLocal 的一个对象。 ThreadLocal 类的行为与 java.lang.ref 中的各种 Reference 类的行为很相似;ThreadLocal 类充当存储或检索一个值时的间接句柄。清单 1 显示了 ThreadLocal 接口。

    清单 1. ThreadLocal 接口
    public class ThreadLocal {
    public Object get();
    public void set(Object newValue);
    public Object initialValue();
    }


    get() 访问器检索变量的当前线程的值;set() 访问器修改当前线程的值。initialValue() 方法是可选的,如果线程未使用过某个变量,那么您可以用这个方法来设置这个变量的初始值;它允许延迟初始化。用一个示例实现来说明 ThreadLocal 的工作方式是最好的方法。清单 2 显示了 ThreadLocal 的一个实现方式。它不是一个特别好的实现(虽然它与最初实现非常相似),所以很可能性能不佳,但它清楚地说明了 ThreadLocal 的工作方式。

    清单 2. ThreadLocal 的糟糕实现

    public class ThreadLocal {
    private Map values = Collections.synchronizedMap(new HashMap());

    public Object get() {
    Thread curThread = Thread.currentThread();
    Object o = values.get(curThread);
    if (o == null && !values.containsKey(curThread)) {
    o = initialValue();
    values.put(curThread, o);
    }
    return o;
    }

    public void set(Object newValue) {
    values.put(Thread.currentThread(), newValue);
    }

    public Object initialValue() {
    return null;
    }
    }



    这个实现的性能不会很好,因为每个 get() 和 set() 操作都需要 values 映射表上的同步,而且如果多个线程同时访问同一个 ThreadLocal,那么将发生争用。此外,这个实现也是不切实际的,因为用 Thread 对象做 values 映射表中的关键字将导致无法在线程退出后对 Thread 进行垃圾回收,而且也无法对死线程的 ThreadLocal 的特定于线程的值进行垃圾回收。

    用 ThreadLocal 实现每线程 Singleton
    线程局部变量常被用来描绘有状态“单子”(Singleton) 或线程安全的共享对象,或者是通过把不安全的整个变量封装进 ThreadLocal,或者是通过把对象的特定于线程的状态封装进 ThreadLocal。例如,在与数据库有紧密联系的应用程序中,程序的很多方法可能都需要访问数据库。在系统的每个方法中都包含一个 Connection 作为参数是不方便的 — 用“单子”来访问连接可能是一个虽然更粗糙,但却方便得多的技术。然而,多个线程不能安全地共享一个 JDBC Connection。如清单 3 所示,通过使用“单子”中的 ThreadLocal,我们就能让我们的程序中的任何类容易地获取每线程 Connection 的一个引用。这样,我们可以认为 ThreadLocal 允许我们创建每线程单子。

    清单 3. 把一个 JDBC 连接存储到一个每线程 Singleton 中
    public class ConnectionDispenser {
    private static class ThreadLocalConnection extends ThreadLocal {
    public Object initialValue() {
    return DriverManager.getConnection(ConfigurationSingleton.getDbUrl());
    }
    }

    private ThreadLocalConnection conn = new ThreadLocalConnection();

    public static Connection getConnection() {
    return (Connection) conn.get();
    }
    }




    任何创建的花费比使用的花费相对昂贵些的有状态或非线程安全的对象,例如 JDBC Connection 或正则表达式匹配器,都是可以使用每线程单子(singleton)技术的好地方。当然,在类似这样的地方,您可以使用其它技术,例如用池,来安全地管理共享访问。然而,从可伸缩性角度看,即使是用池也存在一些潜在缺陷。因为池实现必须使用同步,以维护池数据结构的完整性,如果所有线程使用同一个池,那么在有很多线程频繁地对池进行访问的系统中,程序性能将因争用而降低。

    用 ThreadLocal 简化调试日志纪录
    其它适合使用 ThreadLocal 但用池却不能成为很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如,假设您想创建一个用于管理多线程应用程序调试信息的工具。您可以用如清单 4 所示的 DebugLogger 类作为线程局部容器来累积调试信息。在一个工作单元的开头,您清空容器,而当一个错误出现时,您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。

    清单 4. 用 ThreadLocal 管理每线程调试日志
    public class DebugLogger {
    private static class ThreadLocalList extends ThreadLocal {
    public Object initialValue() {
    return new ArrayList();
    }

    public List getList() {
    return (List) super.get();
    }
    }

    private ThreadLocalList list = new ThreadLocalList();
    private static String[] stringArray = new String[0];

    public void clear() {
    list.getList().clear();
    }

    public void put(String text) {
    list.getList().add(text);
    }

    public String[] get() {
    return list.getList().toArray(stringArray);
    }
    }





    在您的代码中,您可以调用 DebugLogger.put() 来保存您的程序正在做什么的信息,而且,稍后如果有必要(例如发生了一个错误),您能够容易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件,然后努力找出哪个日志记录来自哪个线程(还要担心线程争用日志纪录对象)相比,这种技术简便得多,也有效得多。

    ThreadLocal 在基于 servlet 的应用程序或工作单元是一个整体请求的任何多线程应用程序服务器中也是很有用的,因为在处理请求的整个过程中将要用到单个线程。您可以通过前面讲述的每线程单子技术用 ThreadLocal 变量来存储各种每请求(per-request)上下文信息。

    ThreadLocal 的线程安全性稍差的堂兄弟,InheritableThreadLocal
    ThreadLocal 类有一个亲戚,InheritableThreadLocal,它以相似的方式工作,但适用于种类完全不同的应用程序。创建一个线程时如果保存了所有 InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程。如果一个子线程调用 InheritableThreadLocal 的 get(),那么它将与它的父线程看到同一个对象。为保护线程安全性,您应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用 InheritableThreadLocal,因为对象被多个线程共享。InheritableThreadLocal 很合适用于把数据从父线程传到子线程,例如用户标识(user id)或事务标识(transaction id),但不能是有状态对象,例如 JDBC Connection。

    ThreadLocal 的性能
    虽然线程局部变量早已赫赫有名并被包括 Posix pthreads 规范在内的很多线程框架支持,但最初的 Java 线程设计中却省略了它,只是在 Java 平台的版本 1.2 中才添加上去。在很多方面,ThreadLocal 仍在发展之中;在版本 1.3 中它被重写,版本 1.4 中又重写了一次,两次都专门是为了性能问题。

    在 JDK 1.2 中,ThreadLocal 的实现方式与清单 2 中的方式非常相似,除了用同步 WeakHashMap 代替 HashMap 来存储 values 之外。(以一些额外的性能开销为代价,使用 WeakHashMap 解决了无法对 Thread 对象进行垃圾回收的问题。)不用说,ThreadLocal 的性能是相当差的。

    Java 平台版本 1.3 提供的 ThreadLocal 版本已经尽量更好了;它不使用任何同步,从而不存在可伸缩性问题,而且它也不使用弱引用。相反地,人们通过给 Thread 添加一个实例变量(该变量用于保存当前线程的从线程局部变量到它的值的映射的 HashMap)来修改 Thread 类以支持 ThreadLocal。因为检索或设置一个线程局部变量的过程不涉及对可能被另一个线程读写的数据的读写操作,所以您可以不用任何同步就实现 ThreadLocal.get() 和 set()。而且,因为每线程值的引用被存储在自已的 Thread 对象中,所以当对 Thread 进行垃圾回收时,也能对该 Thread 的每线程值进行垃圾回收。

    不幸的是,即使有了这些改进,Java 1.3 中的 ThreadLocal 的性能仍然出奇地慢。据我的粗略测量,在双处理器 Linux 系统上的 Sun 1.3 JDK 中进行 ThreadLocal.get() 操作,所耗费的时间大约是无争用同步的两倍。性能这么差的原因是 Thread.currentThread() 方法的花费非常大,占了 ThreadLocal.get() 运行时间的三分之二还多。虽然有这些缺点,JDK 1.3 ThreadLocal.get() 仍然比争用同步快得多,所以如果在任何存在严重争用的地方(可能是有非常多的线程,或者同步块被频繁地执行,或者同步块很大),ThreadLocal 可能仍然要高效得多。

    在 Java 平台的最新版本,即版本 1.4b2 中,ThreadLocal 和 Thread.currentThread() 的性能都有了很大提高。有了这些提高,ThreadLocal 应该比其它技术,如用池,更快。由于它比其它技术更简单,也更不易出错,人们最终将发现它是避免线程间出现不希望的交互的有效途径。

    ThreadLocal 的好处
    ThreadLocal 能带来很多好处。它常常是把有状态类描绘成线程安全的,或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用 ThreadLocal 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程,而且因为它不需要任何同步,所以也改善了可伸缩性。除简单之外,用 ThreadLocal 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处 — 通过使用 ThreadLocal,存储在 ThreadLocal 中的对象都是不被线程共享的是清晰的,从而简化了判断一个类是否线程安全的工作。

    我希望您从这个系列中得到了乐趣,也学到了知识,我也鼓励您到我的讨论论坛中来深入研究多线程问题。



    关于作者
    Brian Goetz 是一名软件顾问,过去 15 年来一直是专业软件开发者。他是 Quiotix 的首席顾问,该公司从事软件开发和咨询业务,位于加利福尼亚的 Los Altos。敬请查看 Brian 在流行的业界出版物中已发表和即将发表的论文列表。可通过 brian@quiotix.com 与 Brian 联系。

    jboss下通过配置log4j.xml实现web应用的日志 (原)

    elathen | 17 一月, 2005 21:32

    log4j是一个优秀的开源的java日志系统,jboss内部也集成了它,在jboss下默认的只是对server做了每日日志,并没有对你部署的项目进行每日的日志构建,但我们可以通过修改log4j.xml文件来实现。log4j.xml文件在jboss安装目录下的serverdefaultconf下,打开log4j.xml文件

    这是log4j默认的配置,我们先熟悉一下 <!-- ============================== -->
    <!-- Append messages to the console -->
    <!-- ============================== -->
    <!--
    输出方式:输出到控制台-->
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <!-- 设置通道名称:console和输出方式:org.apache.log4j.RollingFileAppender 其中输出方式appender5,分别为
    org.apache.log4j.ConsoleAppender (
    控制台)
    org.apache.log4j.FileAppender (
    文件)
    org.apache.log4j.DailyRollingFileAppender (
    每天产生一个日志文件)
    org.apache.log4j.WriterAppender (
    将日志信息以流格式发送到任意指定的地方)-->

    <param name="Target" value="System.out"/>
    <param name="Threshold" value="INFO"/>
    <!--Threshold
    是个全局的过滤器,它将把低于所设置的level的信息过滤不显示出来-->
    <!--level:
    是日记记录的优先级,优先级由高到低分为
    OFF ,FATAL ,ERROR ,WARN ,INFO ,DEBUG ,ALL

    Log4j
    建议只使用FATAL ,ERROR ,WARN ,INFO ,DEBUG这四个级别。-->

    <layout class="org.apache.log4j.PatternLayout">
    <!--
    配置日志输出的格式--> 参数都以%开始后面不同的参数代表不同的格式化信息(参数按字母表顺序列出):%c 输出所属类的全名,可在修改为 %d{Num} ,Num类名输出的围 如:"org.apache.elathen.ClassName",
    %C{2}
    将输出elathen.ClassName

    %d
    输出日志时间其格式为 %d{yyyy-MM-dd HH:mm:ss,SSS},可指定格式 如 %d{HH:mm:ss}
    %l
    输出日志事件发生位置,包括类目名、发生线程,在代码中的行数
    %n
    换行符
    %m
    输出代码指定信息,如info(“message”),输出message
    %p
    输出优先级,即 FATAL ,ERROR
    %r
    输出从启动到显示该log信息所耗费的毫秒数
    %t
    输出产生该日志事件的线程名
    <!-- The default pattern: Date Priority [Category] Message -->
    <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
    </layout>
    </appender>

    <!输出方式是:每天一个日志文件>
    <!-- A time/date based rolling appender -->
    <appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender">
    <!--设置通道名称是:file,输出方式DailyRollingFileAppender-->
    <param name="File" value="${jboss.server.home.dir}/log/server.log"/>
    <!--日志文件路径和文件名称 --> <param name="Append" value="false"/>
    <!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->

    <!-- Rollover at midnight each day -->
    <param name="DatePattern" value="'.'yyyy-MM-dd"/>

    <!-- Rollover at the top of each hour
    <param name="DatePattern" value="'.'yyyy-MM-dd-HH"/>
    -->

    <layout class="org.apache.log4j.PatternLayout">
    <!-- The default pattern: Date Priority [Category] Message -->
    <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>

    <!-- The full pattern: Date MS Priority [Category] (Thread:NDC) Message
    <param name="ConversionPattern" value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
    -->
    </layout>
    </appender>

    上面是jboss下log4j的默认配置,对jboss的server进行日志记录,接下来我们添加web项目的日志,在log4j.xml 文件中把下面的配置信息加上去就可以了

    <!-- A size based file rolling appender-->
    <appender name="com.szypt.all" class="org.jboss.logging.appender.RollingFileAppender">
    <param name="File" value="${jboss.server.home.dir}/log/elathen.log"/>
    <param name="Append" value="true"/>
    <param name="MaxFileSize" value="500KB"/>
    <param name="MaxBackupIndex" value="1"/>

    <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
    </layout>
    </appender>

    <logger name="com.szypt">
    <level value="DEBUG" />
    <appender-ref ref="com.szypt.all"/>
    </logger>


     
    Accessible and Valid XHTML 1.0 Strict and CSS
    Powered by pLog - Design by BalearWeb