2017-02-05 15 views
3

Java 9는 리플렉션베이스 Java 구성 요소를 통한 액세스를 제외하고 모듈화되어 있습니다. 이것은 classpath와 java.library.path를 프로그램 적으로 확장하는 대부분의 메소드를 렌더링합니다. 그것을 올바르게하는 방법? 또한 java.sql.DriverManager 및 javax.activation과 호환되도록 만드는 방법은 무엇입니까?Java 9 클래스 경로 및 라이브러리 경로 확장

+1

"토론 시작"은 스택 오버플로가 작동하는 방식이 아니므로 문제를 문구로 질문 한 다음 ** 솔루션에 ** 답장 **하십시오. –

+0

귀하의 요구 사항을 충족하는 답변을 삭제 취소하십시오. – user579013

+0

운영자의주의를 끌기 위해 응답을 플래그로 표시했습니다. –

답변

4

다음은 공개적으로 공개되지 않는 메소드 나 필드에 액세스하거나 리플렉션하지 않고 클래스 패스 또는 java.library.path를 프로그래밍 방식으로 "인증 된"방식으로 확장하는 방법에 대한 연구 결과입니다. JDBC 드라이버가 호출하는 클래스 인 ClassLoader와 다른 ClassLoader로 작성된 경우 java.sql.DriverManager를 "is class authorized"검사로 무시하는 방법도 보여줍니다. 이것은 Java 8 및 Java 9에서 테스트되었으며 두 환경에서 모두 작동합니다 (URLClassLoader 코드는 1.1로 돌아 가야합니다). 주요 조기

public class MiscTools 
{ 
    private static class SpclClassLoader extends URLClassLoader 
    { 
     static 
     { 
      ClassLoader.registerAsParallelCapable(); 
     } 

     private final Set<Path> userLibPaths = new CopyOnWriteArraySet<>(); 

     private SpclClassLoader() 
     { 
      super(new URL[0]); 
     } 

     @Override 
     protected void addURL(URL url) 
     { 
      super.addURL(url); 
     } 

     protected void addLibPath(String newpath) 
     { 
      userLibPaths.add(Paths.get(newpath).toAbsolutePath()); 
     } 

     @Override 
     protected String findLibrary(String libname) 
     { 
      String nativeName = System.mapLibraryName(libname); 
      return userLibPaths.stream().map(tpath -> tpath.resolve(nativeName)).filter(Files::exists).map(Path::toString).findFirst().orElse(super.findLibrary(libname));   } 
    } 
    private final static SpclClassLoader ucl = new SpclClassLoader(); 

    /** 
    * Adds a jar file or directory to the classpath. From Utils4J. 
    * 
    * @param newpaths JAR filename(s) or directory(s) to add 
    * @return URLClassLoader after newpaths added if newpaths != null 
    */ 
    public static ClassLoader addToClasspath(String... newpaths) 
    { 
     if (newpaths != null) 
      try 
      { 
       for (String newpath : newpaths) 
        if (newpath != null && !newpath.trim().isEmpty()) 
         ucl.addURL(Paths.get(newpath.trim()).toUri().toURL()); 
      } 
      catch (IllegalArgumentException | MalformedURLException e) 
      { 
       RuntimeException re = new RuntimeException(e); 
       re.setStackTrace(e.getStackTrace()); 
       throw re; 
      } 
     return ucl; 
    } 

    /** 
    * Adds to library path in ClassLoader returned by addToClassPath 
    * 
    * @param newpaths Path(s) to directory(s) holding OS library files 
    */ 
    public static void addToLibraryPath(String... newpaths) 
    { 
     for (String newpath : Objects.requireNonNull(newpaths)) 
      ucl.addLibPath(newpath); 
    } 
} 

() javax.activation의 같은 것들을 처리하기 위해 다음 코드를 넣습니다 :

는 응용 프로그램 전체에서 사용되는 기본 클래스 로더이다.

Thread.currentThread().setContextClassLoader(MiscTools.addToClasspath()); 

모든 스레드 (java.util.concurrent.Executors에 의해 생성 된 스레드 포함)는 컨텍스트 ClassLoader를 상속합니다.

확장 클래스 경로에서로드 클래스

다음 코드를 사용

try 
{ 
    Class.forName(classname, true, MiscTools.addToClasspath(cptoadd); 
} 
catch (ClassNotFoundException IllegalArgumentException | SecurityException e) 
{ 
    classlogger.log(Level.WARNING, "Error loading ".concat(props.getProperty("Class")), e); 
} 

마지막 java.sql.DriverManager의 여부를 확인하는 방법을 우회하는 경우 DriverManager.getDriver의 호출 클래스() ClassLoader는 JDBC 드라이버를로드하는 데 사용되는 것과 동일한 ClassLoader입니다 (응용 프로그램 ClassLoader에서 호출 클래스를로드했지만 드라이버가 SpclClassLoader를 사용하여로드 된 경우에는 사용되지 않음).

private final static CopyOnWriteArraySet<Driver> loadedDrivers = new CopyOnWriteArraySet<>(); 

private static Driver isLoaded(String drivername, String... classpath) throws ClassNotFoundException 
{ 
    Driver tdriver = loadedDrivers.stream().filter(d -> d.getClass().getName().equals(drivername)).findFirst().orElseGet(() -> 
    { 
     try 
     { 
      Driver itdriver = (Driver) Class.forName(drivername, true, addToClasspath(classpath)).newInstance(); 
      loadedDrivers.add(itdriver); 
      return itdriver; 
     } 
     catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) 
     { 
      return null; 
     } 
    }); 
    if (tdriver == null) 
     throw new java.lang.ClassNotFoundException(drivername + " not found."); 
    return tdriver; 
} 

isLoader는 요청 된 드라이버를 제공하면서 우리가 추가 오버 헤드 모두와 함께 같은 드라이버의 무리를로드하지 않습니다 보장합니다. 단점은 DriverManager가 수행하는 URL 검색 대신 JDBC 클래스의 클래스 이름을 알고 있어야한다는 것입니다 (모두 게시 함). DriverManager는 JDBC 클래스가 시작할 때 Class.forName 함수를 수행하지 않아도되도록로드해야합니다.

이렇게하면 다른 사람들이 필자가 쓴 애플리케이션에 대해이 접근법을 수정하는 데 많은 시간과 노력을 들이지 않아도된다. 많은 플랫폼과 여러 구성에서 사용되는 클래스 패스를 기반으로 클래스를로드 할 수 있어야한다. properties 파일을 수정하고 library.path를 확장하여 기본 library.path에없는 기본 라이브러리를 사용합니다 (또한 특성 파일에서 설명).

+0

누군가가 null이 아닌 다른 것을 반환하도록 ClassLoader.findLibrary()의 기본 동작을 변경하는 경우 super.findLibrary()를 호출하도록 findLibrary()를 업데이트했습니다. – user579013

+0

findLibrary()를 수정하여 디렉토리 경로 대신 기본 라이브러리의 절대 경로를 반환합니다. – user579013