http://blog.paran.com/dce/4799802

굉장히 좋은 글이라서 가져온다.



Java Class Loading Concepts

The following two basic principles will always apply:

o Each class retains an association with the class loader that loaded it. The getClassLoader() method of java.lang.Class returns the class loader that loaded the class, which cannot be changed after the class is loaded. This is the class loader that will be used if the class attempts to load classes by name.

o If the same class is loaded by two class loaders, classes loaded by the two class loaders will not be type compatible (although serialization will work).

The documentation of the java.lang.ClassLoader class further defines the following behavior for class loaders:

o Class loaders are hierarchical.
When a class loader is asked to load a class, it first asks its parent class loader to try to load the class. Only if the parent (and parent's ancestors) cannot load the class, will the original classloader attempt to load the class. The top of the class loader hierarchy is the bootstrap loader built into the JVM, which loads java.lang.Object().

o Although a class loader can see classes loaded by its parent(s), it cannot see classes loaded by its children.



Class Loading in J2EE

J2EE servers use multiple class loaders, largely because this allows dynamic application reloading. Clearly we don't want to reload all the application server's own classes on redeploying an application. This would mean that the application server would always need to be restarted. So application servers use different class loaders for application code to those they use for their own standard libraries, for example. Typically one or more new class loaders will be created for each application deployed on a server.

However, multiple class loaders are not usually used for different application-specific classes in the same application unless we use EJB (JSP pages may be given a separate class loader, but this doesn't usually affect application code).

In an EAR, the EJB class loader is often the parent of the WAR class loader. Orion and WebLogic, for example, both use this approach. This is a natural implementation approach, as WARs will typically access EJBs (and therefore need to be able to see at least EJB client classes), while EJBs do not access web components.

In this diagram the class loader hierarchy is represented by enclosing boxes. The parent-child relationship is represented by an enclosing box:

Assuming standard J2SE hierarchical class loading behavior, such a hierarchy will mean that any class can access classes in boxes that enclose its class loader. However, classes associated with the outer boxes cannot load classes in the inner boxes. Thus web application classes can see classes deployed in the application's EJBs and system classes. However, EJB classes cannot see web application classes, and classes installed at server-wide level cannot see any application-specific classes.


Class Loading in Integrated Enterprise Applications

Thus there are two basic problems relating to J2EE class loading, in the common case where EJB and web modules are included in the same enterprise application:

o Where do we hold the definitions of classes used in both EJBs and web applications?

o How do we ensure that two class loaders don't end up holding independent versions of the same class, resulting in class cast exceptions?


The Servlet 2.3 Specification's Class Loading Recommendations

The Servlet 2.3 specification (9.7.2) states that "It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs".

This clearly conflicts with the standard J2SE class loading behavior, as described in the Javadoc for the java.lang.ClassLoader class. As the WAR class loader must be a dynamic class loader, it must be the child of another class loader provided by the application server. Hence the Servlet 2.3 recommendation is the opposite of normal Java 2 class loading behavior, which clearly states that classes will be loaded from the child class loader (in this case the WAR class loader) only if they cannot be resolved by the ancestor class loaders.


The Java 1.3 Extension Mechanism Architecture in J2EE

Changes in J2SE 1.3 make it possible for JAR files to specify dependencies on other JAR files, by specifying a space-separated list of relative file paths in a Class-Path header in their /META-INF/MANIFEST.MF file.

Class-Path: Iog4j-1.2.jar 121-core.jar 121-ejbimpl.jar 121-jdbc.jar

This declares that the application-specific classes in the ticket-ejb.jar file depend on four infrastructure JARs, meaning that the EJBJAR file doesn't need to include any third party classes. These paths are relative. All these JAR files are included with the EJBJAR file in the root directly of the application EAR, as the following listing of the EAR's contents shows:

        META-INF/
        META-INF/MANIFEST.MF
        META-INF/application.xml
        121-core.jar
        i21-ejbimpl. jar
        121-jdbc.jar
        ticket-ejb.jar
        ticket.war
        Iog4j-1.2.jar

how to enable WAR manifest classpaths on Oracle 9iAS Release 2.

IBM documentation, WebSphere 4.0 supports manifest classpaths for WAR filesM

above links recommends using this when WARs and EJBs reference the same classes.

Orion and Oracle will also load manifest classpaths in EARs by default.

JBoss/Jetty does not appear to respect manifest classpaths in WARs
I haven't relied on this non-portable feature in packaging the sample application. The J2EE Reference Implementation also ignores manifest classpaths in WARs.

BEA discussing the use of manifest classpath


Thread Context Class Loader

getContextClassLoader() method on java.util.Thread.

o Class.forName (classname):
Will use the class loader of the helper class: in this case, the EJB class loader. This means that, if the EJB class loader is the parent of the WAR class loader, the helper will never be able to load classes in the WAR by name.

o Class.forName(classname, true, Thread.currentThread().getContextClassLoader()):
Will use the current container's class loader. This means that the helper will behave differently wherever it is running. If the EJB class loader is the parent of the WAR class loader, when the helper is used in the EJB container, it will only be able to load EJB classes and classes loaded by higher class loaders. If the helper is used in the WAR, it will be able to load WAR classes as well.



------------------------------------------------------------------------------------------


비슷한 예가 더 있다.

http://blog.naver.com/dipl19th?Redirect=Log&logNo=12448366



J2EE Classloading best practices

TheServerSide.com - TSS Featured Entry

자바 엔터프라이즈 어플리케이션에서 클래스로더의 작동 방식을 이해하는게 꽤나 중요하다. 기본적으로 Java2 platform에서는 클래스를 찾을때 현재 context의 클래스로더에서 먼저 찾는게 아니고 상위 context의 클래스로더에 클래스가 있는지 물어보고 없는 경우에 자기 자신의 context에 있는 클래스로더에서 클래스를 찾는게 원칙이다. 하지만 servlet spec에서는 웹 어플리케이션 context의 경우에만 클래스로더에서 먼저 찾고 없는 경우 상위 context에 물어보는게 예외적인 표준으로 되어있다.
이러한 원칙적인 배경을 알고 위의 링크를 읽어보면 웹어플리케이션을 구성하는데 큰 도움이 될 것 같다.

위의 링크를 대략 요약해보면....

Declare dependencies
Make dependencies explicit. Hidden or unknown dependencies will be left behind when you move your application to another environment. Specify the dependencies in the Manifest Class-Path entry of EJB-JAR or WAR file on the libraries packaged in the same application.

클래스들의 의존관계를 명확히 쓰라고 하는데 현재 웹 어플리케이션이 사용하는 클래스가 무엇인지 jar 또는 war의 manifest에 명시하라고 한다. 하지만 웹 어플리케이션에서 암묵적으로 WEB-INF/lib/*.jar, WEB-INF/classes를 자동으로 classpath로 본다는건 다 아는 사실인걸...


Group dependencies
Ensure that all dependencies are visible at the same level or above. If you must move a library, make sure all dependencies are still visible.

모든 클래스 의존 관계를 보이게(visible)하라.. 이 말의 의미는 위에서 java2 platform의 클래스로더 작동방식과 servlet spec에 정의된 웹어플리케이션의 클래스로더 작동방식을 안 다면 이해할 수 있는 부분이다. 어떤 클래스 A가 참조할 수 있는 클래스는 A와 같은 context에 있는 클래스 또는 상위 context의 클래스들이기 때문이다. Sibling context의 클래스는 볼 수 없다.

Minimize visibility
Dependency libraries should be placed at the lowest visibility level that satisfies all dependencies.

최소한의 사용할 클래스만 보이게 배치하라는 얘기인데, 특정 context에서 필요하지 않는 클래스들은 같은 context나 상위 context에 배치하는 걸 피하여 참조되는걸 피하라는 얘기다.


Share libraries
Avoid duplicating libraries. Duplication of libraries may make debugging classloading issues a nightmare. Share classes across a set of applications by using the configuration facilities provided by your application server. For example, use the tag in the global application.xml file in OC4J to share classes across all applications.

클래스들이 서로 다른 context에서 될 수 있으면 중복되지 않게 배치하고 공통으로 사용하는 것들은 한곳으로 모으라는 얘기이다. 이건 좀 주의해야하는데 웹 어플리케이션이 restart하여 해당 웹어플리케이션에서 사용하는 모든 클래스들이 reloading되는 상황에서도 이렇게 공통 영역으로 빠져있는 클래스들은 reloading이 되지 않는다.


Keep configurations portable
Choose configuration options in the following order:
o Standard J2EE options
o Options that can be expressed within your EAR file
o Server-level options
o J2SE extension options

이건 뭘까? 구체적으로 뭔지 번뜩 떠오르지 않는군..

=> @knight 주석, 이것은 클래스 로딩 순서를 의미한다..


Use the correct loader
If you call Class.forName(), always explicitly pass the loader returned by Thread.currentThread().getContextClassLoader. If you are loading a properties file, use Thread.currentThread().getContextClassLoader().getResourceAsStream().

설명 필요없음.


Avoid hardcoding of a specific parser in your application
Use JAXP API to access the underlying XML/XSL parsers in a vendor-neutral way instead of hard coding a specific XML parser in your application.

XML/XSL 파서를 사용할 때 J2SE 1.4의 표준 API를 이용하라는 얘기다. 부연 설명을 하자면 J2SE 1.4에서는 endorsed library라고 해서 JCP 이외의 기관에서 만들어진 표준 라이브러리에 대해서는 implementation을 교체할 수 있는 메커니즘을 제공하고 있다. Java runtime의 소스를 보면 XML/XSL 관련해서 apache에서 제공하는 crimson과 xalan을 사용하고 있는 것으로 보이는데 이걸 다른 걸로 교체할 수 있다. XML/XSL관련 라이브러리를 특정 디렉토리에 모아놓고 -Djava.endorsed.dirs 로 그 디렉토리를 지정해주면 된다.

Posted by '김용환'
,