你真的了解ClassLoader吗?

这篇文章翻译自zeroturnaround.com的 Do You Really Get Classloaders? ,融入和补充了笔者的一些实践、经验和样例。本文的例子比原文更加具有实际意义,文字内容也更充沛一些,非常感谢作者 Jevgeni Kabanov 能够共享如此优秀的文档。

1. 为什么你需要了解和敬畏ClassLoader

ClassLoader在Java语言中占据了核心地位,Java应用服务器,OSGi,以及大量的网络框架,它们大多数都用到了ClassLoader。如果在使用过程中出现了类加载错误,你能解决它吗?

我们将从JVM和开发者两个角度讲述ClassLoader,将会选择一些典型的案例,然后演示如何解决它们。NoClassDefFoundError,LinkageError等很多错误都会有特定的表征,我们分析每个例子,然后进行解决。

2. 进入ClassLoader

每个ClassLoader对象都是一个java.lang.ClassLoader的实例。每个Class对象都被这些ClassLoader对象所加载,通过继承java.lang.ClassLoader可以扩展出自定义ClassLoader,并使用这些自定义的ClassLoader对类进行加载。

先大体了解一下ClassLoader的API:

01 package java.lang;
02  
03 public abstract class ClassLoader {
04  public Class loadClass(String name);
05  
06  protected Class defineClass(byte[] b);
07  
08  public URL getResource(String name);
09  
10  public Enumeration getResources(String name);
11  
12  public ClassLoader getParent();
13 }

最重要的是ClassLoader的loadClass方法,它接受一个全类名,然后返回一个Class类型的实例。

defineClass方法接受一组字节,然后将其具体化为一个Class类型实例,它一般从磁盘上加载一个文件,然后将文件的字节传递给JVM,通过JVM(native 方法)对于Class的定义,将其具体化,实例化为一个Class类型实例。

getParent方法返回其parent ClassLoader。

getResourcegetResources方法,从给定的repository中查找URLs,同时它们也具备类似loadClass一样的代理机制,我们可以将loadClass视为:defineClass(getResource(name).getBytes())

Java由于其晚绑定和“解释型”的特性,类型的加载是到最晚才进行,一个类型直到被调用构造函数、静态方法或者在字段上使用时才会被加载。

考虑如下代码:

1 public class A {
2      public void doSomething() {
3           B b = new B();
4           b.doSomethingElse();
5      }
6 }

代码:B b = new B();等同于B b = Class.forName(“B”, false, A.class.getClassLoader()).newInstance();

这代表着,在类型A中使用到的类型,将由加载了类型A的类加载器来进行加载。

3. ClassLoader继承体系

当启动一个JVM时,bootstrap 类加载器就会加载java的核心类,例如:rt.jar中的类。bootstrap 类加载器是其他类加载器的parent,它使唯一一个没有parent的类加载器。

接下来是extension 类加载器,它以bootstrap 类加载器作为parent,它用来从Java系统变量java.ext.dir中的jar包中加载类的。

第三个,也是最重要的一个就是开发者使用的system classpath 类加载器 。它是extension 类加载器 的child,它用来从Java系统变量java.class.path下面加载类,可以通过 -classpath 来指定这个位置。

注意类加载器的体系并不是“继承”体系,而是一个“委派”体系。大多数类加载器首先会到自己的parent中查找类或者资源,如果找不到,才会在自己的本地进行查找。事实上,类加载器被定义加载哪些在parent中无法加载到的类,这样在较高层级的类加载器上的类型能够被“赋值”为较低类加载器加载的类型。

类加载器的委托行为动机是为了避免相同的类被加载多次。回到1995年,Java的主要方向被放在Applet上,那时候网络带宽优先,所以程序中的类直到用时才会被加载。但是事实上,Java在服务器端展示了强劲的能力,但是服务器端要求类加载器能够反转委派原则,也就是先加载本地的类,如果加载不到,再到parent中加载。

JavaEE的 委派模型

每个方块都是一个类加载器,JavaEE规范推荐每个模块的类加载器先加载本类加载的内容,如果加载不到才回到parent类加载器中尝试加载。

反转委派原则的原因是应用服务器中所携带的类库并不是应用所期待的,也许不适合应用开发者,一个常见的例子就是log4j的依赖在容器和不同的应用中都存在,但是它们的版本大都不同。

Tomcat的 类加载顺序(开启了delegate模式)

在Tomcat中,默认的行为是先尝试在Bootstrap和Extension中进行类型加载,如果加载不到则在WebappClassLoader中进行加载,如果还是找不到则在Common中进行查找。在Alibaba使用的Tomcat开启了delegate模式,因此加载类型时会以parent类加载器优先。

4. NoClassDefFoundError

NoClassDefFoundError是在开发JavaEE程序中常见的一种问题。该问题会随着你所使用的JavaEE中间件环境的复杂度以及应用本身的体量变得更加复杂,尤其是现在的JavaEE服务器具有大量的类加载器。

在JavaDoc中对NoClassDefFoundError的产生是由于JVM或者类加载器实例尝试加载类型的定义,但是该定义却没有找到,影响了执行路径。换句话说,在编译时这个类是能够被找到的,但是在执行时却没有找到。

这一刻IDE是没有出错提醒的,但是在运行时却出现了错误。

看看如下示例:

01 /**
02  * @author weipeng2k 2015年3月27日 下午5:15:15
03  */
04 @WebServlet(name = "NoClassDefFoundErrorServlet", urlPatterns ="/noClassDefFoundError.do")
05 public class NoClassDefFoundErrorServlet extends HttpServlet {
06  
07     private static final long serialVersionUID = 61585757018374721L;
08  
09     @Override
10     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throwsServletException, IOException {
11         resp.getWriter().println(TestCase.class.toString());
12     }
13 }

在看pom.xml中对于依赖的定义:

01 <dependencies>
02     <dependency>
03         <groupId>junit</groupId>
04         <artifactId>junit</artifactId>
05         <version>3.8.1</version>
06         <scope>provided</scope>
07     </dependency>
08     <dependency>
09         <groupId>javax.servlet</groupId>
10         <artifactId>servlet-api</artifactId>
11         <version>3.0</version>
12         <scope>provided</scope>
13     </dependency>
14     <dependency>
15         <groupId>org.springframework</groupId>
16         <artifactId>spring</artifactId>
17         <version>2.5.6</version>
18     </dependency>
19 </dependencies>

其中对于junit的依赖是provided级别的,这里是为了能简化错误出现的条件。可以看到,在NoClassDefFoundErrorServlet中,使用了junit.jar中的TestCase,但是junit.jar在WEB-INF/lib中却没有,从而导致WebappClassLoader在进行加载TestCase时无法找到,从而抛出NoClassDefFoundError。我们需要从最终的war包中确定是否存在这个类,而不是在IDE中进行搜索。

5. NoSuchMethodError

在另一个场景中,我们可能遇到了另一个错误,也就是NoSuchMethodError。

NoSuchMethodError代表这个类型确实存在,但是一个不正确的版本被加载了。为了解决这个问题我们可以使用 ‘-verbose:class’ 来判断该JVM加载的到底是哪个版本。

看如下示例:

01 import org.springframework.beans.factory.BeanFactoryUtils;
02  
03 /**
04  * @author weipeng2k 2015年3月31日 上午9:09:58
05  */
06 @WebServlet(name = "NoSuchMethodErrorServlet", urlPatterns = { "/noSuchMethodError.do"})
07 public class NoSuchMethodErrorServlet extends HttpServlet {
08  
09     private static final long serialVersionUID = 1699609060417354821L;
10  
11     @Override
12     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throwsServletException, IOException {
13         BeanFactoryUtils.isGeneratedBeanName("xxx");
14  
15         resp.getWriter().println("done.");
16     }
17 }

在doGet方法中调用了BeanFactoryUtils.isGeneratedBeanName(”xxx“);,看一下项目的pom依赖。

01 <dependencies>
02     <dependency>
03         <groupId>junit</groupId>
04         <artifactId>junit</artifactId>
05         <version>4.11</version>
06         <scope>provided</scope>
07     </dependency>
08     <dependency>
09         <groupId>javax.servlet</groupId>
10         <artifactId>servlet-api</artifactId>
11         <version>3.0</version>
12         <scope>provided</scope>
13     </dependency>
14     <dependency>
15         <groupId>org.springframework</groupId>
16         <artifactId>org.springframework.context</artifactId>
17         <version>3.0.5.RELEASE</version>
18         <scope>provided</scope>
19     </dependency>
20     <dependency>
21         <groupId>org.apache.mina</groupId>
22         <artifactId>mina-core</artifactId>
23         <version>2.0.7</version>
24     </dependency>
25     <dependency>
26         <groupId>com.alibaba.external</groupId>
27         <artifactId>sourceforge.spring</artifactId>
28         <version>2.0.7</version>
29     </dependency>
30 </dependencies>

这里为了方便观察到结果,将org.springframework.context的 scope 改为了 provided ,目的是不将其打包入war包,而只是使用了sourceforge.spring中定义的2.0.7版本,这个版本肯定没有isGeneratedBeanName(String name)方法,但是在IDE中,由于应用依赖到了高版本的spring从而能够编译通过,但是在运行时却没有那么好运了。这种错误,常见于 Maven坐标 的变动,使得应用依赖了多个 相同内容,不同版本 的jar包,以致在运行时选择了非期望的版本。

6. ClassCastException

NoClassDefFoundError和NoSuchMethodError是两个在 JavaEE 环境中经常出现的问题,这些问题需要 开发人员了解问题的本质,才能够被 从容 的处理。

下面我们看一下ClassCastException,在一个类加载器的情况下,一般出现这种错误都会是在转型操作时,比如:A a = (A) method();,很容易判断出来method()方法返回的类型不是类型A,但是在 JavaEE 多个类加载器的环境下就会出现一些难以定位的情况。

看如下示例:

01 package com.murdock.classloader.servlet;
02  
03 import java.io.File;
04 import java.io.IOException;
05 import java.net.URL;
06  
07 import javax.servlet.ServletException;
08 import javax.servlet.annotation.WebServlet;
09 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12  
13 import org.apache.mina.proxy.utils.MD4;
14  
15 import com.murdock.classloader.CachedClassLoader;
16  
17 /**
18  * @author weipeng2k 2015年4月4日 下午6:00:54
19  */
20 @WebServlet(name = "ClassCastExceptionServlet", urlPatterns = "/classCastException.do")
21 public class ClassCastExceptionServlet extends HttpServlet {
22     private static final long   serialVersionUID    = -8959000121057369987L;
23  
24     @Override
25     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throwsServletException, IOException {
26         String localFirst = req.getParameter("localFirst");
27         CachedClassLoader cl = null;
28         cl = new CachedClassLoader(
29                 new URL[] { new File(
30                         "/Users/weipeng2k/.m2/repository/org/apache/mina/mina-core/2.0.7/mina-core-2.0.7.jar").toURI()
31                         .toURL() }, this.getClass().getClassLoader());
32         if ("false".equals(localFirst)) {
33             cl.setLocalFirst(false);
34         }
35         try {
36             Class<?> klass = cl.loadClass("org.apache.mina.proxy.utils.MD4");
37             MD4 md4 = (MD4) klass.newInstance();
38  
39             resp.getWriter().println(md4);
40  
41         catch (Exception ex) {
42             throw new RuntimeException(ex);
43         finally {
44             cl.close();
45         }
46  
47     }
48 }

在ClassCastExceptionServlet中,构建了一个CachedClassLoader,利用这个ClassLoader加载org.apache.mina.proxy.utils.MD4,然后反射调用构造该类的实例,将其赋给MD4,最后将其打印到浏览器。

请求URLhttp://localhost:8080/classCastException.do

响应页面,出现错误:

1 java.lang.RuntimeException: java.lang.ClassCastException: org.apache.mina.proxy.utils.MD4 cannot be cast to org.apache.mina.proxy.utils.MD4
2     com.murdock.classloader.servlet.ClassCastExceptionServlet.doGet(ClassCastExceptionServlet.java:42)
3     javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
4     javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
5     org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

请求URL :http://localhost:8080/classCastException.do?localFirst=false
响应页面,输出正常:

1 org.apache.mina.proxy.utils.MD4@401c8af5

请求的URL加上了localFirst=false就可以正常的输出,而它也就是在CachedClassLoder上设置了一下,为什么有这么大的差别。org.apache.mina.proxy.utils.MD4全类名一致,为什么会出现ClassCastException呢?

在JVM中,如何确定一个类型实例?答:全类名吗?不是,是类加载器加上全类名。在JVM中,类型被定义在一个叫SystemDictionary 的数据结构中,该数据结构接受类加载器和全类名作为参数,返回类型实例。

SystemDictionary 如图所示:

类型加载时,需要传入类加载器和需要加载的全类名,如果在 SystemDictionary 中能够命中一条记录,则返回class 列上对应的类型实例引用,如果无法命中记录,则会调用loader.loadClass(name);进行类型加载。

这里不会更加深入的介绍 SystemDictionary 如何进行类型加载的过程,而是需要指出 JVM中确定一个类型的坐标是通过类加载器和全类名做到的 。回想一下MD4 md4 = (MD4) klass.newInstance();,是不是代表着等式两边的MD4是不同的类加载器加载的呢?那问题一定出在 CachedClassLoader 上。这里贴一下loadClass(String name)方法的部分逻辑。

CachedClassLoader 的loadClass逻辑:

01 if (localFirst) {
02     try {
03         clazz = findClass(name);
04         if (clazz != null) {
05             return clazz;
06         }
07     catch (ClassNotFoundException ex) {
08  
09     }
10     return super.loadClass(name);
11 else {
12     return super.loadClass(name);
13 }

可以看到在 localFirst 为true时,该类加载器会首先加载自身 repository 中的类型,如果加载不到,则会尝试默认的加载机制进行加载,也就是parent优先加载。这样就可以解释MD4 md4 = (MD4) klass.newInstance();,等式左边MD4 md4,这个类型是WebappClassLoader.org.apache.mina.proxy.utils.MD4,等式右边klass.newInstance()返回的类型是CachedClassLoader.org.apache.mina.proxy.utils.MD4,二者并不是同一个类型,所以无法完成类型转换,最终抛出 ClassCastException 。而当 localFirst 为false时,该类加载器遵循parent优先,从而会先委派给WebappClassLoader进行加载,当然转型也就不会有问题了。

在传统的双亲委派模型下,这种 ClassCastException 是不会发生的,因为它的加载顺序杜绝了出现这种问题的可能,而在 JavaEE 环境下,每个资源模块(比如一个war包)都优先使用自身的资源,正因为突破了双亲委派模型, 奇怪的问题 就发生了。

7. LinkageError

有时候事情会变得更糟,和 ClassCastException 本质一样,加载自不同位置的*相同类*在同一段逻辑(比如:方法)中交互时,会出现 LinkageError 。

我们先看一下出错的异常信息,然后分析一下它产生的条件和原因:

01 java.lang.LinkageError: loader constraint violation: when resolving overridden method"com.murdock.classloader.linkageerror.Param2.generate()Lcom/murdock/classloader/linkageerror/Param2;"the class loader (instance of com/murdock/classloader/linkageerror/LinkageErrorTest$1) of the currentclass, com/murdock/classloader/linkageerror/Param2, and its superclass loader (instance of sun/misc/Launcher$AppClassLoader), have different Class objects for the type com/murdock/classloader/linkageerror/Param2 used in the signature
02     at java.lang.Class.getDeclaredConstructors0(Native Method)
03     at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
04     at java.lang.Class.getConstructor0(Class.java:3075)
05     at java.lang.Class.newInstance(Class.java:412)
06     at com.murdock.classloader.linkageerror.LinkageErrorTest.test(LinkageErrorTest.java:34)
07     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
08     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
09     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
10     at java.lang.reflect.Method.invoke(Method.java:497)
11     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
12     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
13     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
14     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

看到一堆出错信息,但是不要紧张,慢慢的读一下出错信息,这种错误一般会让你直觉感觉不会出现。loader constraint violation表示类加载器冲突了,这句话暗示: 相同的类,由不同的ClassLoader加载,但是在这里遇到了when resolving overridden method "com.murdock.classloader.linkageerror.Param2.generate()Lcom/murdock/classloader/linkageerror/Param2;"表示在解析那条语句出现了问题,这里表示在Param2.generate()方法的解析过程中出现了问题。the class loader (instance of com/murdock/classloader/linkageerror/LinkageErrorTest$1) of the current class, com/murdock/classloader/linkageerror/Param2,表示解析的语句所在的类型Param2LinkageErrorTest$1类加载器加载的。and its superclass loader (instance of sun/misc/Launcher$AppClassLoader), have different Class objects for the type com/murdock/classloader/linkageerror/Param2 used in the signature表示Param2的超类Param中被覆盖的方法返回的类型Param2Launcher$AppClassLoader加载。

Linkage在常规情况下非常难以制造,只有在多个类加载器交互时才有可能出现,下面看一下问题代码。出现问题的类和参数:

01 package com.murdock.classloader.linkageerror;
02  
03 /**
04  * @author weipeng2k 2015年4月28日 上午10:04:26
05  */
06 public class HandleUtils {
07     public void m(Param param) {
08         param.generate();
09     }
10  
11 }
12  
13 package com.murdock.classloader.linkageerror;
14  
15 public class Param {
16     public Param2 generate() {
17         return new Param2();
18     }
19 }
20  
21 package com.murdock.classloader.linkageerror;
22  
23 public class Param2 extends Param {
24     public Param2 generate() {
25         return new Param2();
26     }
27 }

测试用例如下:

01 @Test
02 public void test() throws Exception {
03  
04     // cl1在加载HandleUtils和Param时将会使用AppClassLoader
05     URLClassLoader cl1 = new URLClassLoader(new URL[] {new File("target/test-classes").toURI().toURL()}, null) {
06  
07         @Override
08         public Class<?> loadClass(String name) throws ClassNotFoundException {
09             if ("com.murdock.classloader.linkageerror.HandleUtils".equals(name)) {
10                 return ClassLoader.getSystemClassLoader().loadClass(name);
11             }
12  
13             if ("com.murdock.classloader.linkageerror.Param".equals(name)) {
14                 return ClassLoader.getSystemClassLoader().loadClass(name);
15             }
16  
17             return super.loadClass(name);
18         }
19  
20     };
21  
22     ClassLoader.getSystemClassLoader().loadClass("com.murdock.classloader.linkageerror.Param2");
23     HandleUtils hu = (HandleUtils) cl1.loadClass("com.murdock.classloader.linkageerror.HandleUtils").newInstance();
24     hu.m((Param) cl1.loadClass("com.murdock.classloader.linkageerror.Param2").newInstance());
25 }

LinkageError 需要观察哪个类被不同的类加载器加载了,在哪个方法或者调用处发生(交汇)的,然后才能想解决方法,解决方法无外乎两种。第一,还是不同的类加载器加载,但是相互不再交汇影响,这里需要针对发生问题的地方做一些改动,比如更换实现方式,避免出现上述问题;第二,冲突的类需要由一个Parent类加载器进行加载。**LinkageError** 和**ClassCastException** 本质是一样的,加载自不同类加载器的类型,在同一个类的方法或者调用中出现,如果有转型操作那么就会抛 ClassCastException ,如果是直接的方法调用处的参数或者返回值解析,那么就会产生 LinkageError 。

8. 类加载器问题对照表

遇到类加载器问题时,可以尝试使用下面的表格进行问题排查。

类找不到 了不正确的类 多于一个类被加
ClassNotFoundException NoClassDefFoundError IncompatibleClassChangeError NoSuchMethodError NoSuchFieldError IllegalAccessError ClassCastException LinkageError
IDE class lookup (Ctrl+Shift+T in Eclipse)find . -name “*.jar” -exec jar -tf {} \; | grep DateUtils

使用middleware-detector

通过在启动参数中加 -verbose:class,观察加载的类来自哪个jar包使用middelware-detector 通过`-verbose:class`观察

9. 使用Middleware-Detector进行类查找

出现了 ClassNotFoundException 或者 NoClassDefFoundError ,需要检查一下程序的classpath下面是否存在你所预想的类。这时可以使用Middleware-Detector工具进行类查找,该工具是Alibaba中间件团队开发的一款中间件问题诊断工具,当然也包括了许多支持性质的工具。

下面我们使用Middleware-Detector进行类查找,比如我们要查找apache的Utils,我们怀疑这个类在classpath下找不到。

启动middleware-detector,查看 Pandora 提供的自定义检查器,目前编号为1的Pandora自定义检查器就是进行classpath下的指定类或者接口的查找工作。

配置classpath目录以及需要查找的类名,这里类名支持 * 号进行模糊匹配。可以看到设定当前的classpath目录到了WEB-INF/lib 下面,然后找寻*apache*comm*A*Utils是否存在,如果能够找到则会输出到终端,这里就找到了ArchiveUtils和ArrayUtils两个符合要求的类。如果无法找到,那么就可能是pom.xml的依赖配置不正确了,需要检查一下。

10. 使用Middleware-Detector进行检查类冲突

出现了 NoSuchMethodError 或者 NoSuchFieldError ,这时一般是应用的classpath下包含了多个包含了想同类的jar包,而很不幸的加载到了 不正确 的jar包。

我们可以通过使用Middleware-Detector的类查找进行定位,但是不能发现一个修复一个,这里Middleware-Detector提供了一个检查classpath下有冲突jar包的功能。只需要设置classpath的目录,然后运行cc –check tomcat#1即可。有冲突的jar就需要自己在pom.xml里面进行仲裁或者排除了。

深入浅出ClassLoader的更多相关文章

  1. JAVA基础知识之JVM-——类加载器

    类加载器负责将.class文件加载到内存,并为其创建java.lang.Class对象,这个对象就代表这个类. 在Java中,通过包名+类名来唯一标识一个类,而在JVM中,要用 类加载器实例+包名+类 ...

  2. JVM ,JIT ,GC RUNTIME 解析

    Java Class字节码知识点回顾 https://yq.aliyun.com/articles/2358?spm=5176.8067842.tagmain.105.fQdvH3 JVM Class ...

  3. Java类加载器回顾

    Java类加载采用了全盘委托机制,默认加载类时子类先会委托给父类加载,但父类加载不到时,子类才会自己尝试加载类.这种机制可以有效防止一个类被加载多次,同时也一定程度上防止重写JDK自身的类[Java自 ...

  4. 并发编程网 - ifeve.com

    并发编程网 - ifeve.com 让天下没有难学的技术 首页 JAVA 深入浅出ClassLoader 深入浅出ClassLoader Dedicate to Molly. 你真的了解ClassLo ...

  5. 深入浅出 JVM ClassLoader

    # 前言 在 JVM 综述里面,我们说,JVM 做了三件事情,Java 程序的内存管理, Java Class 二进制字节流的加载(ClassLoader),Java 程序的执行(执行引擎).我们也说 ...

  6. 【深入浅出-JVM】(76):classloader

    方法 public Class<?> loadClass(String name) throws ClassNotFoundException 通过类名发挥这个类的Class实例 prot ...

  7. 深入浅出单实例Singleton设计模式

    深入浅出单实例Singleton设计模式 陈皓 单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了,这可能也是面试中问得最多的一个设计模式了.这个设计模式主要目的是想在整个系统 ...

  8. java代理的深入浅出(二)-CGLIB

    java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...

  9. java代理的深入浅出(一)-Proxy

    java代理的深入浅出(一)-Proxy 1.什么是代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事 ...

随机推荐

  1. VS2010 创建WindowsService服务

    1.新建一个Windows 服务 2.添加Installer 这一步很重要,在处理完你的业务逻辑后需要添加一个Installer才能是你的Windows服务被安装. 在VS中添加Installer 右 ...

  2. uva 11986

    假设有n只老鼠 每只老鼠有两种状态 死或活 则n只老鼠有 2^n方种状态 所以n只老鼠可以确定2^n只瓶子 #include <cstdio> #include <cstdlib&g ...

  3. timeit统计运行时间

    import timeitt1 = timeit.timeit('sum(x*x for x in xrange(10000))',number = 10000) print t1

  4. What is XMLHTTP? How to use security zones in Internet Explorer

    Types of Security Zones Internet Zone This zone contains Web sites that are not on your computer or ...

  5. Project Euler 79:Passcode derivation

    Passcode derivation A common security method used for online banking is to ask the user for three ra ...

  6. lintcode:Compare Strings 比较字符串

    题目: 比较字符串 比较两个字符串A和B,确定A中是否包含B中所有的字符.字符串A和B中的字符都是 大写字母 样例 给出 A = "ABCD" B = "ACD" ...

  7. MyBatis,动态传入表名,字段名的解决办法

    转载:http://luoyu-ds.iteye.com/blog/1517607 今天做项目,遇到的问题就是需求修改数据表的记录,而且字段名都不是固定的,也就是说是需要通过参数传入的, 本来这也不是 ...

  8. TCL语言笔记:TCL中的String命令

    一.介绍 字符串是 Tcl 中的基本数据类型,所以有大量的字符串操作命令.一个比较重要的问题就是模式匹配,通过模式匹配将字符串与指定的模式(格式)相匹配来进行字符串的比较.搜索等操作. 二.strin ...

  9. java获取当前操作系统的信息

    java获取当前操作系统的信息 JavaOS虚拟机UnixEXT  从网上收集的一些关于java获取操作系统信息的方法,现在总结一下: 1获取本机的IP地址: private static Strin ...

  10. 转Unity 异常操作

    摘要 使用 unity 处理异常的方法可能会与你的直觉不符.本文将给出正确的处理方法,并简单剖析Unity这部分源代码. 处理异常 打算用Unity的AOP截获未处理的异常,然后写个日志什么的,于是我 ...