从现在回贴来看,还没有人碰到类似的感觉的人,我也只是从Exo代码的ServiceManager找到知音了啊,这个帖子就留在这里,估计几年后会有人跟贴的。
不管是什么应用,我想首先需要搞清楚的是问题域。
在AppServer的设计过程中,要考虑到服务器的类装载,应用的类装载。二者共用和排斥的关系。有些类应用能看到,而不需服务器看到;有些服务器使用的类不能让应用看到;而有些类需要两者都看到(这时两者都可以各自装入,但为了节省内存和简单,共用最好)。这都是在问题域里思考的,不只是组装ClassLoader。如果光考虑ClassLoader,则是舍本求末了。
如果要自己写框架或服务器,设计ClassLoader确实要慎重。碰巧我做过一些服务器方面的工作,对这方面体会很深。不是ClassLoader本身的复杂性,而是问题域的复杂性。如果设计出的ClassLoader很复杂,说明你对问题域的理解又问题。而如果ClassLoader很简单,而且很灵活,那说明你走在正确的路上。所以,我反对把ClassLoader搞得很复杂,而强调理解问题域的需要。
> Exo代码的ServiceManager找到知音了啊
什么感觉?
原来没有看Banq在前面写的代码,原来是自己定义一个ClassLoader。我前面的回复就有一些文不对题了。Banq写的WrapperServlet好像有些错误:应该是继承HttpServlet吧。
如要在现有的框架下定义一个新的ClassLoader,那么原框架的ClassLoader的结构就需要搞清楚,Banq是对的。这样的话,部署应用时就不会出现莫名其妙的无法装入类的现象。
> 这个帖子就留在这里,估计几年后会有人跟贴的。
???
>般Servlet类部署到Web容器,是由Web容器监测Web.xml是否变化,
>以决定是否装载,这是非常被动的,使用下面代码通过ClassLoader可以
>在自己程序中主动实现装载:
自己装载Servlet不是一个好主意,这样就得自己维护这个Servlet的生命周期,得不偿失啊。呵呵
那个Servlet是我举例,在我的设计中,主要也是类似Servlet这样有生命周期的框架,我的框架是要有生命周期的,才能在容器中反复部署调试使用。设计一个有生命周期的框架不是一件很容易的事情。
这个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();
欢迎对这些有兴趣者一起谈论。
Class.forName()只是为了方便装入类而写的一个静态方法,不知为何Banq总是拿来和ClassLoader作比较,二者不是同一个级别的东西。
通过调用Thread.currentThread().setContextClassLoader(ClassLaoder load)方法,Class.forName()也可以装入其他ClassLoader范围内的类。
如果还不明白的话,去看看Class.forName()的源代码,就一清二楚了。
多谢kewan,我更改了之后第一次可以运行,第二次部署就不行了,我已经做过明显的实验对比。
写在这里意在提醒大家,注意这个区别,特别是碰到很头疼的问题时,还有EJB打包时,也存在这个问题,甚至发生无法部署的问题。
希望有以上经验的人来谈谈自己的实践感受。
??? 为何第二次就不行了?
另外,Class.forName()使用的ClassLoader实际上是Lancher的AppClassloader,希望这条信息能有帮助。
我更换了Thread.XX就可以运行,说明这两者在某些情况下是有区别的。
我本想将它总结出规律,目前我只能得出,如果你做的是框架等非最终层的软件,那么最好不用CLass.ForName, 好好地留心注意处理你的框架的CLassLoader策略。
呵呵,现在和我的观点相同了。
>我更换了Thread.XX就可以运行,说明这两者在某些情况下是有区别的。
当然有区别,但本质上是一样的。
>那么最好不用CLass.ForName
其实我一般不用Class.forName(),只是对楼上的各位总是抬出Class.forName感到有些不解,所以就发了这些议论。既然大家观点一致,就没有什么好说得了,呵呵
再看ClassLoader.getCallerClassLoader()怎么实现,很简单通过一个native方法得到caller class然后getClassLoader()
现在结论很简单了,当caller class不是由current thread的context ClassLoader load的时候,两者的结果会不一致,但是大部分情况下是一致的。
这是一个很简单的问题,banq只要看看source,根本不用扯到具体的应用里,还弄出exo这个东西,exo里用currentThread的context classLoader是为了灵活,由Thread控制,而不需要显示的传入一个ClassLoader而已.
而且ClassLoader是一个很简单的问题,根本谈不上什么深奥,只不过大家接触的少以为很神秘罢了,banq不要自己理解了个东西,就说出什么极限考验云云,容易误导初学者.