JAVA基础之类加载器

  1. 编译器:为JVM转换源指令,将.java文件转换为.class的字节码文件;

  2. 解释器:将字节码文件翻译成目标机器的机器语言;

  3. JVM仅加载程序执行时所需要的类文件,假设程序从MyProgram.class开始运行,其过程如下:

    1. 类加载:从磁盘上读取文件或请求Web上的文件,加载MyProgram.class中的内容;

    2. 类分析:如果MyProgram.class中存在类型为另一个类的域,或者是拥有超类,那么这些类文件也会被加载;

    3. 接着,JVM执行MyProgram中的main方法(它是静态的,无需创建类的实例);

    4. 如果main方法或者main调用的方法要用到更多的类,那么接下来会加载这些类。

  4. 类加载器分类:

    1. 引导类加载器:加载系统类,通常从rt.jar开始,通常是用C语言来实现;

    2. 扩展类加载器:加载jre/lib/ext中的标准扩展类,不产生类路径

    3. 系统类加载器(应用类加载器):加载应用类,CLASSPATH配置的类文件,产生类路径

  5. 类加载器的层次结构:

    类加载器有一种父/子关系,除了引导类加载器外,每个类加载都有一个父类加载器;加载类的时候,父类有一次加载机会,只有父类加载失败才加载指定的类:

    URL url = new URL("file:///*.jar");

    URLClassLoader pluginLoader = new URLClassLoader(new URL[]{url});

     Class<?> cl = pluginLoader.loadClass("myPackage.MyClass");

    因为在URLClassLoader构造中没有指定父类加载器,因此插件类的父类就是系统类加载器(应用类)。

  6. JAVA基础之类加载器

  7. 类加载器倒置:

    Class.forName()使用的是系统类加载器(应用类),如果有一个插件类希望被类加载器加载,系统类加载器对插件类是不可见的,所以就需要使用插件类加载器。

  8. 每个线程都有一个对类加载器的引用,称为上下文类加载器。主线程的上下文类加载器是系统类加载器,所以,如果不作任何操用,所有的线程都会是系统类加载器。

  9.         URL url = new URL("file:///*.jar");
            URLClassLoader pluginLoader = new URLClassLoader(new URL[]{url});
            //设置上下文类加载器为指定的类加载器
            Thread t = Thread.currentThread();
            t.setContextClassLoader(pluginLoader);
            //调用类
            Class<?> cl = pluginLoader.loadClass("myPackage.MyClass")
  10. 在同一个JVM中,可以有两个类,它们的包名和类名都相同,这是因为类是由它人全名和类加载器来确定的。

  11. JAVA基础之类加载器

  12. 类加载器应用场景:加密软件,将类文件存储到数据库等

  13. 定制类加载器

  14. import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    public class Caesar {
    
        public static void main(String[] args) {
            if (args.length != 3) {
                System.out.println("USAGE: java classloader.Caesar in out key");
                return;
            }
    
            try {
                FileInputStream in = new FileInputStream(args[0]);
                FileOutputStream out = new FileOutputStream(args[1]);
                int key = Integer.parseInt(args[2]);
                int ch = 0;
                while ((ch = in.read()) != -1) {
                    byte c = (byte) (ch + key);
                    out.write(c);
                }
            } catch (Exception e) {
            }
        }
    
    }
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class CryptoClassLoader extends ClassLoader {
        private int key;
    
        public CryptoClassLoader(int key) {
            this.key = key;
        }
    
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] classBytes = null;
                classBytes = loadClassBytes(name);
                //调用超类ClassLoader的defineClass方法,向JVM提供字节码
                Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);
                if (cl == null)
                    throw new ClassNotFoundException(name);
                return cl;
            } catch (Exception e) {
                throw new ClassNotFoundException(name);
            }
        }
    
        private byte[] loadClassBytes(String name) throws IOException {
            String cname = name.replace('.', '/') + ".caesar";
            InputStream in = new FileInputStream(new File(cname));
            byte[] bytes = toByteArray(in);
            for (int i = 0; i < bytes.length; i++)
                bytes[i] = (byte) (bytes[i] - key);
            in.close();
            return bytes;
        }
    
        //字符流转字节流
        private byte[] toByteArray(InputStream in) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int offset = 0;
            while ((offset = in.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            out.close();
            return out.toByteArray();
        }
    
    }
    import java.awt.GridBagConstraints;
    
    public class GBC extends GridBagConstraints {
        public GBC(int gridx, int gridy) {
            this.gridx = gridx;
            this.gridy = gridy;
        }
    
        public GBC(int gridx, int gridy, int gridwidth, int gridheight) {
            this.gridx = gridx;
            this.gridy = gridy;
            this.gridwidth = gridwidth;
            this.gridheight = gridheight;
        }
    
        public GBC setSpan(int gridwidth, int gridheight) {
            this.gridwidth = gridwidth;
            this.gridheight = gridheight;
            return this;
        }
    
        public GBC setAnchor(int anchor) {
            this.anchor = anchor;
            return this;
        }
    
        public GBC setFill(int fill) {
            this.fill = fill;
            return this;
        }
    
        public GBC setWeight(double weightx, double weighty) {
            this.weightx = weightx;
            this.weighty = weighty;
            return this;
        }
    
        public GBC setInsets(int distance) {
            this.insets = new java.awt.Insets(distance, distance, distance, distance);
            return this;
        }
    
        public GBC setInsets(int top, int left, int bottom, int right) {
            this.insets = new java.awt.Insets(top, left, bottom, right);
            return this;
        }
    
        public GBC setIpad(int ipadx, int ipady) {
            this.ipadx = ipadx;
            this.ipady = ipady;
            return this;
        }
    
    }
    import java.awt.GraphicsConfiguration;
    import java.awt.GridBagLayout;
    import java.awt.HeadlessException;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.lang.reflect.Method;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    
    public class ClassLoaderFrame extends JFrame {
        private JTextField       keyField        = new JTextField("3", 4);
        private JTextField       nameField       = new JTextField("Calculator", 30);
        private static final int DEFAULT_WIDTH   = 300;
        private static final int DEFAULT_HIENGTH = 200;
    
        public ClassLoaderFrame() throws HeadlessException {
            setSize(DEFAULT_WIDTH, DEFAULT_HIENGTH);
            setLayout(new GridBagLayout());
            add(new JLabel("Class"), new GBC(0, 0).setAnchor(GBC.EAST));
            add(nameField, new GBC(1, 0).setWeight(100, 0).setAnchor(GBC.WEST));
            add(new JLabel("Key"), new GBC(0, 1).setAnchor(GBC.EAST));
            add(keyField, new GBC(1, 1).setWeight(100, 0).setAnchor(GBC.WEST));
            JButton loadButton = new JButton("Load");
            add(loadButton, new GBC(0, 2, 2, 1));
            loadButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent event) {
                    runClass(nameField.getText(), keyField.getText());
                }
            });
        }
    
        public void runClass(String name, String key) {
            try {
                //调用classloader,传递解密key
                ClassLoader loader = new CryptoClassLoader(Integer.parseInt(key));
                Class<?> c = loader.loadClass(name);
                Method m = c.getMethod("main", String[].class);
                m.invoke(null, (Object) new String[] {});
            } catch (Throwable e) {
                JOptionPane.showMessageDialog(this, e);
            }
        }
    
        public ClassLoaderFrame(GraphicsConfiguration gc) {
            super(gc);
        }
    
        public ClassLoaderFrame(String title) throws HeadlessException {
            super(title);
        }
    
        public ClassLoaderFrame(String title, GraphicsConfiguration gc) {
            super(title, gc);
        }
    
    }
    import java.awt.EventQueue;
    
    import javax.swing.JFrame;
    
    public class ClassLoaderTest {
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new ClassLoaderFrame();
                    frame.setTitle("ClassLoaderTest");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setVisible(true);
                }
            });
        }
    }
  15. 字节码校验

    当类加载器将新加载的Java平台类的字节码传递给JVM时,这些字节码首先要接受“校验器”的校验,除了系统类外,所有的类都要校验。

  16. 安全管理器

    字节码通过校验后,第二种安全机制就会启动,安全管理器是一个负责控制具体操作是否允许执行类。


你可能感兴趣的:(JAVA基础之类加载器)