banq
2004-07-30 11:31
两位争论都有道理,对于初学者,一般应用,确实没必要搞这么复杂,但是我们实际开发中,发现这个思维有点大意了。

从现在回贴来看,还没有人碰到类似的感觉的人,我也只是从Exo代码的ServiceManager找到知音了啊,这个帖子就留在这里,估计几年后会有人跟贴的。

banq
2004-07-30 12:26
kewan观点因为回的是删除贴,也被不小心删除,补充如下:


不管是什么应用,我想首先需要搞清楚的是问题域。
在AppServer的设计过程中,要考虑到服务器的类装载,应用的类装载。二者共用和排斥的关系。有些类应用能看到,而不需服务器看到;有些服务器使用的类不能让应用看到;而有些类需要两者都看到(这时两者都可以各自装入,但为了节省内存和简单,共用最好)。这都是在问题域里思考的,不只是组装ClassLoader。如果光考虑ClassLoader,则是舍本求末了。
如果要自己写框架或服务器,设计ClassLoader确实要慎重。碰巧我做过一些服务器方面的工作,对这方面体会很深。不是ClassLoader本身的复杂性,而是问题域的复杂性。如果设计出的ClassLoader很复杂,说明你对问题域的理解又问题。而如果ClassLoader很简单,而且很灵活,那说明你走在正确的路上。所以,我反对把ClassLoader搞得很复杂,而强调理解问题域的需要。

kewan
2004-07-30 14:45
> 从现在回贴来看,还没有人碰到类似的感觉的人,我也只是从
> Exo代码的ServiceManager找到知音了啊
什么感觉?
原来没有看Banq在前面写的代码,原来是自己定义一个ClassLoader。我前面的回复就有一些文不对题了。Banq写的WrapperServlet好像有些错误:应该是继承HttpServlet吧。
如要在现有的框架下定义一个新的ClassLoader,那么原框架的ClassLoader的结构就需要搞清楚,Banq是对的。这样的话,部署应用时就不会出现莫名其妙的无法装入类的现象。
> 这个帖子就留在这里,估计几年后会有人跟贴的。
???

kewan
2004-07-30 14:52
>下面一段代码 是在Web容器中,实现Servlet类或Jsp类的动态装载,一
>般Servlet类部署到Web容器,是由Web容器监测Web.xml是否变化,
>以决定是否装载,这是非常被动的,使用下面代码通过ClassLoader可以
>在自己程序中主动实现装载:
自己装载Servlet不是一个好主意,这样就得自己维护这个Servlet的生命周期,得不偿失啊。呵呵

banq
2004-07-31 15:49
多谢kewan 指点,你提供了一个思路方向,我正在考虑。

那个Servlet是我举例,在我的设计中,主要也是类似Servlet这样有生命周期的框架,我的框架是要有生命周期的,才能在容器中反复部署调试使用。设计一个有生命周期的框架不是一件很容易的事情。

banq
2004-07-31 17:40
为了说明ClassLoader策略如此重要,以Spring框架中AopProxy为例:
这个AopProxy是所有代理实现的总接口,代理实现有jdk动态代理或cglib代理等。

public interface AopProxy {
/**
* Creates a new Proxy object for the given object, proxying
* the given interface. Uses the thread context class loader.
*/
public abstract Object getProxy();
/**
* Creates a new Proxy object for the given object, proxying
* the given interface. Uses the given class loader.
*/
public abstract Object getProxy(ClassLoader cl);
}

上面两个方法都是充分重视ClassLoader。
我们看看JdkDynamicAopProxy的一个实现是如何制定ClassLoader策略的:

public Object getProxy() {
return getProxy(Thread.currentThread().getContextClassLoader());
}

/**
* Create a new Proxy object for the given object, proxying
* the given interface. Uses the given class loader.
*/
public Object getProxy(ClassLoader cl) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy for [" + this.advised.getTargetSource().getTargetClass() + "]");
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}


注意这里策略是使用Thread.currentThread().getContextClassLoader(),而不是普通的Class.forName();

欢迎对这些有兴趣者一起谈论。

kewan
2004-08-01 11:49
Thread.currentThread().getContextClassLoader()指的是取得当前线程的ClassLoader,而Class.forName()其实也是使用当前线程的ClassLoader装入一个类,二者并没有本质的区别。
Class.forName()只是为了方便装入类而写的一个静态方法,不知为何Banq总是拿来和ClassLoader作比较,二者不是同一个级别的东西。
通过调用Thread.currentThread().setContextClassLoader(ClassLaoder load)方法,Class.forName()也可以装入其他ClassLoader范围内的类。
如果还不明白的话,去看看Class.forName()的源代码,就一清二楚了。

kewan
2004-08-01 11:58
在AopProxy例子中,使用两个方法取得Proxy,第一个时使用当前线程的ClassLoader,另一个是使用指定的ClassLoader,这两个方法是一致的,所以无参数的那个有参数的那个来实现。在newProxyInstance的实现里,使用Class.forName()装入也是可以的。不相信就自己修改一下,看能不能行,呵呵。

banq
2004-08-02 09:00
>使用Class.forName()装入也是可以的
多谢kewan,我更改了之后第一次可以运行,第二次部署就不行了,我已经做过明显的实验对比。

写在这里意在提醒大家,注意这个区别,特别是碰到很头疼的问题时,还有EJB打包时,也存在这个问题,甚至发生无法部署的问题。

希望有以上经验的人来谈谈自己的实践感受。

kewan
2004-08-02 13:44
>我更改了之后第一次可以运行,第二次部署就不行了
??? 为何第二次就不行了?

另外,Class.forName()使用的ClassLoader实际上是Lancher的AppClassloader,希望这条信息能有帮助。

banq
2004-08-02 15:49
>为何第二次就不行了?
我更换了Thread.XX就可以运行,说明这两者在某些情况下是有区别的。
我本想将它总结出规律,目前我只能得出,如果你做的是框架等非最终层的软件,那么最好不用CLass.ForName, 好好地留心注意处理你的框架的CLassLoader策略。

kewan
2004-08-02 19:08
>好好地留心注意处理你的框架的CLassLoader策略
呵呵,现在和我的观点相同了。

>我更换了Thread.XX就可以运行,说明这两者在某些情况下是有区别的。
当然有区别,但本质上是一样的。

>那么最好不用CLass.ForName
其实我一般不用Class.forName(),只是对楼上的各位总是抬出Class.forName感到有些不解,所以就发了这些议论。既然大家观点一致,就没有什么好说得了,呵呵

raimundo
2004-09-14 21:05
Class.forName有两个签名Class.forName(String)和Class.forName(String, boolean, ClassLoader),最终都是调用内部native方法forName0,第一个签名实现是return forName0(className, true, ClassLoader.getCallerClassLoader()),第二个稍微复杂一些,但是大概认为是在当指定loader==null的时候使用ClassLoader.getCallerClassLoader().
再看ClassLoader.getCallerClassLoader()怎么实现,很简单通过一个native方法得到caller class然后getClassLoader()
现在结论很简单了,当caller class不是由current thread的context ClassLoader load的时候,两者的结果会不一致,但是大部分情况下是一致的。
这是一个很简单的问题,banq只要看看source,根本不用扯到具体的应用里,还弄出exo这个东西,exo里用currentThread的context classLoader是为了灵活,由Thread控制,而不需要显示的传入一个ClassLoader而已.
而且ClassLoader是一个很简单的问题,根本谈不上什么深奥,只不过大家接触的少以为很神秘罢了,banq不要自己理解了个东西,就说出什么极限考验云云,容易误导初学者.

sunsonbaby
2004-09-23 12:28
very good.

banq
2004-09-24 09:19
多谢 raimundo 解释,在嵌套ClassLoader环境中就没有这么简单了,掌握在嵌套ClassLoader环境中使用策略应该是Java高级应用,这只是经验之谈。

猜你喜欢