国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java類加載器

2019-11-17 04:06:54
字體:
來源:轉載
供稿:網友
   如果你想對java加載類的機制有比較深入的理解的話,請你花點時間好好閱讀一下這篇東西.

    這里從三個方面講述java classloader

    1,將從web應用出發模擬一下web應用的classloader(這節是重點)

    2,通過測試2實現的classloader來加深理解

    3,翻譯一下classloader的相關技術文章(來自Core Java第二卷的Chapter 9. Security),由于我的英文水平有限,難免有不合適,請大家指出.

看看怎么實現一個自己的ClassLoader(我們以web應用的classloader為例來講解 )

大家都很熟悉tomcat,比如我們要寫一個servlet運行的話.

1,我們通常會在tomcat的webapps目錄下建一個自己的web目錄(比如myweb),然后讓自己的myweb至少具有以下目錄結構.

└─WEB-INF
    ├─classes
    └─lib

2,直接把class文件拷貝到classes目錄下,或者把自己做的servet打成jar放到lib下

3,啟動tomcat就能訪問servlet了.

再說一點:不少人了解struts,也做過基于struts的應用.其實struts對于我們來說只是幾個jar而已,我們要在自己的應用中用struts,

只不過就象上面一樣把struts的各個lib拷貝到web應用的WEB-INF/lib下就OK了.

再綜合前面的基礎部分,其實web應用是有自己的classloader的,他專門負責加載WEB-INF/lib和WEB- INF/classes的類。下面我們就來模擬一下web應用的classloader,相信看了下面的代碼你就會對classloader的理解又上一個臺階.

view plaincopy to clipboardPRint?
import java.io.File;   
import java.io.FilenameFilter;   
import java.lang.reflect.Method;   
import java.net.MalformedURLException;   
import java.net.URL;   
import java.net.URLClassLoader;   
/*  
* web應用的classloader  
* 帶你步入classloader的天堂  
*/  
public class WebAppClassLoader{   
    URLClassLoader myClassLoader = null;       
    /*  
     * 用法:new WebAppClassLoader(webRoot)  
     *   比如:new WebAppClassLoader("c:/web")  
     */  
    public WebAppClassLoader(String root) {   
        URL[] urls = null;   
        try {   
            urls = getJarURLs(root);   
        } catch (MalformedURLException e) {   
            System.out.println(e.getMessage());   
        }   
        myClassLoader = new URLClassLoader(urls);   
    }   
    /*  
     * 獲得web應用下的所有class的path的URL.  
     *  1,WEB-INF/classes目錄  
     *  2,WEB-INF/lib下的所有jar文件  
     */  
    private URL[] getJarURLs(String rootDirStr) throws MalformedURLException {   
        if (!rootDirStr.endsWith(File.separator)) {   
            rootDirStr += File.separator;   
        }   
           
        // classesDir就是web應用中的"WEB-INF/classes"目錄   
        File classesDir = new File(rootDirStr + File.separator + "WEB-INF"  
                + File.separator + "classes");   
           
        // classesDir就是web應用中的"WEB-INF/lib"目錄   
        File libDir = new File(rootDirStr + File.separator + "WEB-INF"  
                + File.separator + "lib");   
        // 找出目錄下所有的jar文件   
        File[] jarFiles = null;   
        if (libDir.isDirectory()) {   
            jarFiles = libDir.listFiles(new FilenameFilter() {   
                public boolean accept(File dir, String name) {   
                    // 注意"dir"參數指的是jar文件的父目錄,"name"才是jar文件的   
                    if (dir.isDirectory() // jar文件的父目錄必須是一個文件夾   
                            && (name.endsWith(".jar") || name.endsWith(".zip"))) // 注意zip文件也是可以的哦   
                        return true;   
                    return false;   
                }   
            });   
        }   
        int jarCount = null == jarFiles?0:jarFiles.length;   
        URL[] urls = new URL[1 + jarCount];   
        urls[0] = classesDir.toURI().toURL();//WEB-INF/classes   
        for (int i = 0; i < jarCount; i++) {//WEB-INF/lib下的所有jar文件   
            urls[i + 1] = jarFiles[i].toURI().toURL();   
        }   
           
        return urls;   
    }   
       
    /*  
     *加載class,直接調用 myClassLoader的loadClass(className)方法  
     */  
    public Class<?> loadClass(String className) throws ClassNotFoundException{   
        return myClassLoader.loadClass(className);   
    }   
       
    /**  
     * 測試WebAppClassLoader  
     * @param args  
     */  
    public static void main(String[] mainArgs) {   
        try {   
            //請根據實際情況指定目錄和類名   
            WebAppClassLoader classLoader = new WebAppClassLoader("c:/web/");   
            Class<?> c = classLoader.loadClass("classloader.ButtonTest");   
            //用反射調用main方法   
            String[] args = new String[] {};   
            Method m = c.getMethod("main", args.getClass());   
            m.invoke(null, (Object) args);   
        } catch (Exception e) {   
            // handle exception   
            e.printStackTrace();   
        }   
    }   
}  
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
/*
* web應用的classloader
* 帶你步入classloader的天堂
*/
public class WebAppClassLoader{
URLClassLoader myClassLoader = null;
/*
  * 用法:new WebAppClassLoader(webRoot)
  *   比如:new WebAppClassLoader("c:/web")
  */
public WebAppClassLoader(String root) {
  URL[] urls = null;
  try {
   urls = getJarURLs(root);
  } catch (MalformedURLException e) {
   System.out.println(e.getMessage());
  }
  myClassLoader = new URLClassLoader(urls);
}
/*
  * 獲得web應用下的所有class的path的URL.
  *  1,WEB-INF/classes目錄
  *  2,WEB-INF/lib下的所有jar文件
  */
private URL[] getJarURLs(String rootDirStr) throws MalformedURLException {
  if (!rootDirStr.endsWith(File.separator)) {
   rootDirStr += File.separator;
  }
  
  // classesDir就是web應用中的"WEB-INF/classes"目錄
  File classesDir = new File(rootDirStr + File.separator + "WEB-INF"
    + File.separator + "classes");
  
  // classesDir就是web應用中的"WEB-INF/lib"目錄
  File libDir = new File(rootDirStr + File.separator + "WEB-INF"
    + File.separator + "lib");
  // 找出目錄下所有的jar文件
  File[] jarFiles = null;
  if (libDir.isDirectory()) {
   jarFiles = libDir.listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
     // 注意"dir"參數指的是jar文件的父目錄,"name"才是jar文件的
     if (dir.isDirectory() // jar文件的父目錄必須是一個文件夾
       && (name.endsWith(".jar") || name.endsWith(".zip"))) // 注意zip文件也是可以的哦
      return true;
     return false;
    }
   });
  }
  int jarCount = null == jarFiles?0:jarFiles.length;
  URL[] urls = new URL[1 + jarCount];
  urls[0] = classesDir.toURI().toURL();//WEB-INF/classes
  for (int i = 0; i < jarCount; i++) {//WEB-INF/lib下的所有jar文件
   urls[i + 1] = jarFiles[i].toURI().toURL();
  }
  
  return urls;
}

/*
  *加載class,直接調用 myClassLoader的loadClass(className)方法
  */
public Class<?> loadClass(String className) throws ClassNotFoundException{
  return myClassLoader.loadClass(className);
}

/**
  * 測試WebAppClassLoader
  * @param args
  */
public static void main(String[] mainArgs) {
  try {
   //請根據實際情況指定目錄和類名
   WebAppClassLoader classLoader = new WebAppClassLoader("c:/web/");
   Class<?> c = classLoader.loadClass("classloader.ButtonTest");
   //用反射調用main方法
   String[] args = new String[] {};
   Method m = c.getMethod("main", args.getClass());
   m.invoke(null, (Object) args);
  } catch (Exception e) {
   // handle exception
   e.printStackTrace();
  }
}
}


測試自己的ClassLoader

1,在c盤建一個web目錄(當然了,你可以把上面類的main改一下 使他適合自己的需求),然后子目錄如下

└─WEB-INF
    ├─classes
    └─lib

2,新寫一個帶main方法的類:classloader.ButtonTest(當然了,你可以把上面類的main改一下使他適合自己的需求),然后打包成jar文件放到web/WEB-INF/lib下(當然了,你也可以不打包,直接把你的類放到web/WEB-INF/classes下),如果你很懶,就copy 我下面的代碼吧,順便還可以了解了解AWT和SWING;##這個ButtonTest的main方法也可以不象下面這么復雜,就一個 System.out.print也可以的哦

view plaincopy to clipboardprint?
package classloader;   
import java.awt.*;   
import java.awt.event.*;   
import javax.swing.*;   
public class ButtonTest   
{   
   public static void main(String[] args)   
   {   
      ButtonFrame frame = new ButtonFrame();   
      frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);   
      frame.setVisible(true);   
   }   
}   
/**  
   A frame with a button panel  
*/  
class ButtonFrame extends JFrame   
{   
   public ButtonFrame()   
   {   
      setTitle("ButtonTest");   
      setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);   
      // add panel to frame   
      ButtonPanel panel = new ButtonPanel();   
      add(panel);   
   }   
   public static final int DEFAULT_WIDTH = 300;   
   public static final int DEFAULT_HEIGHT = 200;   
}   
/**  
   A panel with three buttons.  
*/  
class ButtonPanel extends JPanel   
{   
   public ButtonPanel()   
   {   
      // create buttons   
      JButton yellowButton = new JButton("Yellow");   
      JButton blueButton = new JButton("Blue");   
      JButton redButton = new JButton("Red");   
      // add buttons to panel   
      add(yellowButton);   
      add(blueButton);   
      add(redButton);   
      // create button actions   
      ColorAction yellowAction = new ColorAction(Color.YELLOW);   
      ColorAction blueAction = new ColorAction(Color.BLUE);   
      ColorAction redAction = new ColorAction(Color.RED);   
      // associate actions with buttons   
      yellowButton.addActionListener(yellowAction);   
      blueButton.addActionListener(blueAction);   
      redButton.addActionListener(redAction);   
   }   
   /**  
      An action listener that sets the panel's background color.  
   */  
   private class ColorAction implements ActionListener   
   {   
      public ColorAction(Color c)   
      {   
         backgroundColor = c;   
      }   
      public void actionPerformed(ActionEvent event)   
      {   
         setBackground(backgroundColor);   
      }   
      private Color backgroundColor;   
   }   
}  
package classloader;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonTest
{
   public static void main(String[] args)
   {
      ButtonFrame frame = new ButtonFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}
/**
   A frame with a button panel
*/
class ButtonFrame extends JFrame
{
   public ButtonFrame()
   {
      setTitle("ButtonTest");
      setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      // add panel to frame
      ButtonPanel panel = new ButtonPanel();
      add(panel);
   }
   public static final int DEFAULT_WIDTH = 300;
   public static final int DEFAULT_HEIGHT = 200;
}
/**
   A panel with three buttons.
*/
class ButtonPanel extends JPanel
{
   public ButtonPanel()
   {
      // create buttons
      JButton yellowButton = new JButton("Yellow");
      JButton blueButton = new JButton("Blue");
      JButton redButton = new JButton("Red");
      // add buttons to panel
      add(yellowButton);
      add(blueButton);
      add(redButton);
      // create button actions
      ColorAction yellowAction = new ColorAction(Color.YELLOW);
      ColorAction blueAction = new ColorAction(Color.BLUE);
      ColorAction redAction = new ColorAction(Color.RED);
      // associate actions with buttons
      yellowButton.addActionListener(yellowAction);
      blueButton.addActionListener(blueAction);
      redButton.addActionListener(redAction);
   }
   /**
      An action listener that sets the panel's background color.
   */
   private class ColorAction implements ActionListener
   {
      public ColorAction(Color c)
      {
         backgroundColor = c;
      }
      public void actionPerformed(ActionEvent event)
      {
         setBackground(backgroundColor);
      }
      private Color backgroundColor;
   }
}


3,運行上面的WebAppClassLoader,看看你自己寫的,放在web/WEB-INF/lib下的有沒有被調用,被調用的話就恭喜你了。

請注意:不要把你新寫的帶main方法的類放在WebAppClassLoader所在的工程目錄下,如果你這樣做的話起不到測試效果,因為這樣的話類不需要自己寫的WebAppClassLoader也能被load著的。


以下是翻譯的關于ClassLoader介紹  

ClassLoader
  JAVA編譯器把源代碼轉換成一個假想機器(就是我們所說的虛擬機)的語言.虛擬機指令被保存在class后綴的文件里.
  每一個類文件包含類和接口的定義以及實現代碼.這些類文件必須被一個程序解釋,這個程序能夠把虛擬機的指令翻譯成
  宿主機的機器語言.

  注意:虛擬機只加載執行一個程序所需要的類文件.舉個例子,比如執行MyProgram.class,虛擬機運行的步驟如下:
  1,虛擬機有一個加載類文件的機制,比如,從硬盤讀取文件或者就網絡獲得;虛擬機用這個機制加載MyProgram的類文件
  2,如果MyProgram有一個實例變量或者是超類,那么實例變量和超類的類文件也被加載.
  (加載一個類所依賴的所有類的過程叫做resolving the class-->自己理解吧)
  3,然后虛擬機執行MyProgram的main方法(因為是靜態方法,所以不需要new MyProgram的實例)
  4,如果main 方法或者main方法調用的方法需要其他的類的話,這些類也被加載.

  類加載機制不是僅僅用一個類加載器,任何一個java程序至少有以下三個類加載器(為了不影響大家的理解,這里我就不翻譯這三個類加載器的名稱了)
  The bootstrap class loader:加載系統類(有代表性的,jdk的rt.jar里的類),他是虛擬機的必要組成部分,并且一般是用C實現的.
  也有類加載器對象(就是指具體的一個類加載器)不關聯bootstrap class loader,比如String.class.getClassLoader()返回null.

  The extension class loader:加載jre/lib/ext目錄下的class,你可以把你的jar文件放到這個目錄,extension class loader
  將會加載到jar里面的類,即使你不設置classpath.(一些人建議使用這個機制以讓你不受classpath的煩擾,不過注意以下的警告)
  
  The system class loader (有時也叫應用程序加載器):加載應用程序類.
  他主要加載classpath目錄和jar/zip文件里的class,通過設置CLASSPATH環境變量或者是運行java的時候用[-classpath]選項指定classpath
  
  在SUN的java實現里,the extension and system class loaders都是用java實現的,他們都是URLClassLoader類的實例.
  
    警告:如果你把jar文件放到jre/lib/ext目錄下,并且你的jar文件中的類需要加載一個不是system or extension的類的話,
    你將遇到麻煩.擴展類加載器不使用類路徑.如果你想把類放到jre/lib/ext下進行管理的話,請牢記這一點.
    ==>怎么理解這一點:也就是說如果你把自己的x.jar放到jre/lib/ext下的話,如果你自己的x.jar里的class要用到不在
    x.jar里也不在jre/lib/ext的類的話,會導致類加載不了.不難想象吧,因為你x.jar里的類是由extension class loader
    加載的,他不會加載classpath路徑下的類.
  
    警告:把jar文件放到jre/lib/ext目錄下,還有第二個缺陷:有時侯,程序員忘記了他很久以前放在這個目錄下的類文件.
    當class loader似乎忽略了類路徑(其實沒有,因為類加載總是先讓父的類加載器加載類,只有父的類加載器加載不了的話
    才由自己來加載,"extension class loader是system class loader的父,因此..."),
    而加載了放在擴展目錄下的遺忘已久的類的時候,他們就會迷惑不解.
  
  class loader有父子關系,bootstrap class loader以外的每一個class loader都有一個父的類加載器.
  類加載器會給父的加載器一個機會加載任何給定的類,如果父加載器加載失敗的話自身才去加載.
  舉個例子,當系統class loader被要求加載一個系統類的時候(比如,java.util.ArrayList),
  那么,首先需要extension class loader加載,而extension class loader又先讓bootstrap class loader,
  最終由bootstrap class loader查找并且加載了rt.jar,其他任何類加載器不需要再搜索.

    注意:當實現一個類加載器的時候,你應該總是授權父加載器去加載類.否則,將會有一些潛在的安全隱患:自定義的類加載器
    可能避開重要的安全檢查,意外地加載了系統類.

  Applets, servlets, and RMI stubs是用戶自定義的類加載器加載的.你甚至可以根據自己的需要寫自己的類加載器.
  這種方式允許你在傳字節碼給虛擬機之前實現特殊的安全檢查.比如,你可以寫一個類加載器拒絕加載沒有用"paid for"
  表示的類.下一節將展示這么去實現.

  大多數時間,你不需要擔心類加載器.很多類因為被其他類引用而被加載,這個過程對你來說是透明的.

  如果你在程序里調用Class.forName來加載一個類,那么一個新的類被調用Class.forName的類的加載器加載.通常,
  這不會有什么問題.然而,在下面的情況下將會失敗:
   1.你的lib自己實現了一個有Class.forName方法的類的時候
   2.你的lib里的類的方法被一個不同的類加載器加載的應用程序類所調用的時候(這一先需要好好理解)
   3,被加載的類對于應用程序的類加載器來說是不可見的時候(也就是說classpath下沒有相關的類)
   
   這種情況下.庫類需要搜索應用程序類加載器(代碼如下):
   Thread t = Thread.currentThread();
   ClassLoader loader = t.getContextClassLoader();
   Class cl = loader.loadClass(className);

Using Class Loaders as Namespaces
  任何一個java程序員都知道包名是用來消除名字沖突的.在標準類庫里有兩個叫Date的類(java.util.Date and java.sql.Date).
  簡單的名字(這里指的是你在程序里直接寫Date)只是程序員方便,并且需要包含import語句.在一個運行的程序中,
  所有的class都包含他們的包名.

  這也許讓你吃驚,然而,在同一個虛擬機里面你可以有兩個類名和包名都相同的類.一個類是通過他的全名和類加載器來標識的.
  This technique is useful for loading code from multiple sources.比如,瀏覽器為每個web頁面使用單獨的
  applet class loader.這允許虛擬機分開來自不同web頁面的類,不管他們是怎么命名的.

    NOTE:這項技術還有其他的用途,比如Sevlet和EJB的"熱部署",請參照:http://java.sun.com/developer/TechTips/2000/tt1027.html
  
  
Writing Your Own Class Loader
  The loadClass method of the ClassLoader superclass takes care of the delegation to
  the parent and calls findClass only if the class hasn't already been loaded and
  if the parent class loader was unable to load the class.
  定義自己的類加載器只需要繼承ClassLoader類并且重寫findClass(String className)方法.
  ClassLoader父類的loadClass方法負責授權給父的類加載器 并且只有在還沒有加載并且父的類加載器不能加載的時候
  才調用findClass方法.
  
  NOTE:在早期版本的JDK中,程序員必須重寫loadClass方法.現在不再建議這種做法.
  
  實現findClass方法必須做下面兩件事情,
  1,從文件或者其他地方加載類的字節碼
  2,為了把字節碼提交給虛擬機,需要調用ClassLoader類的defineClass方法,
  
  以下是實現一個加載加密類文件的類加載器.

view plaincopy to clipboardprint?
/**  
* This class loader loads encrypted class files.  
*/  
class CryptoClassLoader extends ClassLoader {   
    /**  
     * Constructs a crypto class loader.  
     *   
     * @param k  
     *            the decryption key  
     */  
    public CryptoClassLoader(int k) {   
        key = k;   
    }   
    protected Class findClass(String name) throws ClassNotFoundException {   
        byte[] classBytes = null;   
        try {   
            classBytes = loadClassBytes(name);   
        } catch (IOException e) {   
            throw new ClassNotFoundException(name);   
        }   
        Class cl = defineClass(name, classBytes, 0, classBytes.length);   
        if (cl == null)   
            throw new ClassNotFoundException(name);   
        return cl;   
    }   
    /**  
     * Loads and decrypt the class file bytes.  
     *   
     * @param name  
     *            the class name  
     * @return an array with the class file bytes  
     */  
    private byte[] loadClassBytes(String name) throws IOException {   
        String cname = name.replace('.', '/') + ".caesar";   
        FileInputStream in = null;   
        in = new FileInputStream(cname);   
        try {   
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();   
            int ch;   
            while ((ch = in.read()) != -1) {   
                byte b = (byte) (ch - key);   
                buffer.write(b);   
            }   
            in.close();   
            return buffer.toByteArray();   
        } finally {   
            in.close();   
        }   
    }   
    private int key;   
}  
/**
* This class loader loads encrypted class files.
*/
class CryptoClassLoader extends ClassLoader {
/**
  * Constructs a crypto class loader.
  *
  * @param k
  *            the decryption key
  */
public CryptoClassLoader(int k) {
  key = k;
}
protected Class findClass(String name) throws ClassNotFoundException {
  byte[] classBytes = null;
  try {
   classBytes = loadClassBytes(name);
  } catch (IOException e) {
   throw new ClassNotFoundException(name);
  }
  Class cl = defineClass(name, classBytes, 0, classBytes.length);
  if (cl == null)
   throw new ClassNotFoundException(name);
  return cl;
}
/**
  * Loads and decrypt the class file bytes.
  *
  * @param name
  *            the class name
  * @return an array with the class file bytes
  */
private byte[] loadClassBytes(String name) throws IOException {
  String cname = name.replace('.', '/') + ".caesar";
  FileInputStream in = null;
  in = new FileInputStream(cname);
  try {
   ByteArrayOutputStream buffer = new ByteArrayOutputStream();
   int ch;
   while ((ch = in.read()) != -1) {
    byte b = (byte) (ch - key);
    buffer.write(b);
   }
   in.close();
   return buffer.toByteArray();
  } finally {
   in.close();
  }
}
private int key;
}  
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 丰镇市| 鹤壁市| 子洲县| 昌平区| 应城市| 逊克县| 洮南市| 昌都县| 翼城县| 三原县| 固阳县| 寻乌县| 阜城县| 社会| 天津市| 香港| 台前县| 敦化市| 芜湖市| 漳浦县| 绥棱县| 平顶山市| 平和县| 乐业县| 民丰县| 会同县| 山丹县| 文化| 伊金霍洛旗| 天柱县| 读书| 仁怀市| 昌都县| 福建省| 盐津县| 普陀区| 若羌县| 扎鲁特旗| 平和县| 岢岚县| 津南区|