当使用Java命令运行某个程序时,该命令将会启动一个Java虚拟机进程,不管该Java程序多么复杂,该程序启动了多少线程,它们都处于该Java虚拟机进程里。同一个JVM的所有线程,所有变量都处于同一个进程里,他们都使用该JVM进程的内存区。当系统出现以下几种情况时,JVM进程将被终止:

  1. 程序运行到最后正常结束。
  2. 程序运行到使用System.exit()或Runtime.getRuntime().exit()代码出结束程序。
  3. 程序执行过程中遇到未捕获的异常或错误而结束。
  4. 程序所在平台强制结束了JVM进程。

  类加载指的是将类的class文件读入内存,并为之创建一个Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个Class对象。

  当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过以下3个步骤来对类进行初始化。

  1. 加载
  2. 连接
  3. 初始化

  JVM初始化一个类包含以下步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类。
  2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类。
  3. 假如类中有初始化语句,则系统依次执行这些初始化语句。

  JVM最先初始化的总是Object类。

 

  • 类加载器

  当JVM启动时,会形成由3个类加载器组成的初始类加载器层次结构:

  1. Bootstrap ClassLoader: 根类加载器
  2. Extension ClassLoader: 扩展类加载器
  3. System ClassLoader: 系统类加载器

  Bootstrap加载器被称为引导类加载器,它负责加载Java的核心类。

package com.ivy.reflection;

import java.net.URL;

public class BootstrapDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for(int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }
    }

}

输出结果:

file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/resources.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/rt.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/sunrsasign.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/jsse.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/jce.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/charsets.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/lib/jfr.jar
file:/C:/Program%20Files%20(x86)/Java/jdk1.7.0_45/jre/classes

 

  Extension负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext或者由java.ext.dirs系统属性指定的目录)中JAR包中的类。

  System类加载器负责在JVM启动时加载来自java命令的-classpath选项/java.class.path系统属性,或CLASSPATH环境变量所指的JAR包和类路径。

 

  URLClassLoader是ClassLoader的一个实现类,它既可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加载类。

 

  •  通过反射查看类信息

获得Class对象的3种方式:

  1. Class.forName(String clazzName), clazzName必须是全限定名
  2. 调用某个类的class属性来获取该类对应的Class对象。
  3. 调用某个对象的getClass()方法

大部分时间使用第二种方式。

package com.ivy.reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

@SuppressWarnings(value="unchecked")
@Deprecated
public class ClassDemo {

    private ClassDemo() {
        
    }
    
    public ClassDemo(String name) {
        System.out.println("constructor with param");
    }
    
    public void info() {
        System.out.println("method info without param");
    }
    
    public void info(String str) {
        System.out.println("method info with a param");
    }
    
    class Inner {
        
    }
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        Class<ClassDemo> clazz = ClassDemo.class;
        Constructor[] ctors = clazz.getDeclaredConstructors();
        System.out.println("all constructors of ClassDemo : ");
        for (Constructor c : ctors) {
            System.out.println(c);
        }
        
        Constructor[] publicCtors = clazz.getConstructors();
        System.out.println("all public constructors of ClassDemo : ");
        for (Constructor c : publicCtors) {
            System.out.println(c);
        }
        
        Method[] mtds = clazz.getMethods();
        System.out.println("all public methods of ClassDemo : ");
        for (Method m : mtds) {
            System.out.println(m);
        }
        
        System.out.println("info method with a param in ClassDemo : " + clazz.getMethod("info", String.class));
        
        Annotation[] anns = clazz.getAnnotations();
        System.out.println("all annotations in ClassDemo : ");
        for (Annotation an : anns) {
            System.out.println(an);
        }
        
        System.out.println("@SuppressWarning comment in ClassDemo : " + clazz.getAnnotation(SuppressWarnings.class));
        
        Class<?>[] inners = clazz.getDeclaredClasses();
        System.out.println("all inner classes in ClassDemo : ");
        for (Class c : inners) {
            System.out.println(c);
        }
        
        Class inClazz = Class.forName("com.ivy.reflection.ClassDemo$Inner");
        System.out.println("inClazz outer class : " + inClazz.getDeclaringClass());
        System.out.println("ClassDemo package : " + clazz.getPackage());
        System.out.println("ClassDemo parent class : " + clazz.getSuperclass());
    }

}

 

  • 通过反射生成并操作对象

通过反射来生成对象有两种方式:

  1. 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器。(较常见)
  2. 使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance方法来创建实例。

 

package com.ivy.reflection;

import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


public class ObjectPoolFactory {

    private Map<String, Object> objectPool = new HashMap<>();
    
    private Object createObject(String clazzName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> clazz = Class.forName(clazzName);
        return clazz.newInstance();
    }
    
    public void initPool(String fileName) {
        try(
                FileInputStream fis = new FileInputStream(fileName))
        {
            Properties props = new Properties();
            props.load(fis);
            for(String nameString : props.stringPropertyNames()) {
                objectPool.put(nameString, createObject(props.getProperty(nameString)));
            }
        } catch (Exception ex) {
            System.out.println("read " + fileName + " exception");
        }
    }
    
    public Object getObject(String name) {
        return objectPool.get(name);
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ObjectPoolFactory pFactory = new ObjectPoolFactory();
        pFactory.initPool("obj.txt");
        System.out.println(pFactory.getObject("a"));
        System.out.println(pFactory.getObject("b"));
    }

}

package com.ivy.reflection;

import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


public class ExtendedObjectPoolFactory {

    private Map<String, Object> objectPool = new HashMap<>();
    private Properties config = new Properties();
    
    public void init(String fileName) {
        try(
                FileInputStream fis = new FileInputStream(fileName))
        {
            config.load(fis);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private Object createObject(String clazzName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> clazz = Class.forName(clazzName);
        return clazz.newInstance();
    }
    
    public void initPool() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        for(String nameString : config.stringPropertyNames()) {
            if (!nameString.contains("%")) {
                objectPool.put(nameString, createObject(config.getProperty(nameString)));
            }
        }
    }
    
    public void initProperty() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for (String name : config.stringPropertyNames()) {
            if (name.contains("%")) {
                String[] objAndProp = name.split("%");
                Object target = getObject(objAndProp[0]);
                String mtdNameString = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1);
                Class<?> targetClass = target.getClass();
                Method mtd = targetClass.getMethod(mtdNameString, String.class);
                mtd.invoke(target, config.getProperty(name));
            }
        }
    }
    
    public Object getObject(String name) {
        return objectPool.get(name);
    }
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        // TODO Auto-generated method stub
        ExtendedObjectPoolFactory eFactory = new ExtendedObjectPoolFactory();
        eFactory.init("extObj.txt");
        eFactory.initPool();
        eFactory.initProperty();
        System.out.println(eFactory.getObject("a"));
    }
}

package com.ivy.reflection;

import java.lang.reflect.Field;



class Person {
    private String name;
    private int age;
    public String toString() {
        return "Person[name:" + name + ", age:" + age + " ]";
    }
}
public class FieldDemo {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        Person p = new Person();
        Class<Person> personClass = Person.class;
        Field nameField = personClass.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(p, "Ivy");
        Field ageField = personClass.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.setInt(p, 25);
        System.out.println(p);
    }

}

package com.ivy.reflection;

import java.lang.reflect.Array;

public class ArrayDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        try {
            Object arr = Array.newInstance(String.class, 10);
            Array.set(arr, 5, "Java");
            Array.set(arr, 6, "Spring");
            
            Object book1 = Array.get(arr, 5);
            Object book2 = Array.get(arr, 6);
            
            System.out.println(book1);
            System.out.println(book2);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

 

  • 动态代理

代理模式

定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。

要理解代理模式很简单,其实生活当中就存在代理模式:

我们购买火车票可以去火车站买,但是也可以去火车票代售处买,此处的火车票代售处就是火车站购票的代理,即我们在代售点发出买票请求,代售点会把请求发给火车站,火车站把购买成功响应发给代售点,代售点再告诉你。
但是代售点只能买票,不能退票,而火车站能买票也能退票,因此代理对象支持的操作可能和委托对象的操作有所不同。

再举一个写程序会碰到的一个例子:

如果现在有一个已有项目(你没有源代码,只能调用它)能够调用 int compute(String exp1) 实现对于后缀表达式的计算,你想使用这个项目实现对于中缀表达式的计算,那么你可以写一个代理类,并且其中也定义一个compute(String exp2),这个exp2参数是中缀表达式,因此你需要在调用已有项目的 compute() 之前将中缀表达式转换成后缀表达式(Preprocess),再调用已有项目的compute(),当然你还可以接收到返回值之后再做些其他操作比如存入文件(Postprocess),这个过程就是使用了代理模式。

在平时用电脑也会碰到代理模式的应用:

远程代理:我们在国内因为GFW,所以不能访问 facebook。

 

因为Java的单继承性(每个代理类都继承了Proxy类),只能针对接口创建代理类,不能针对类创建代理类。

 

package com.ivy.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface People {
    void walk();
    void sayHello(String name);
}

class MyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("----running method : " + method);
        if (args != null) {
            System.out.println("args : ");
            for (Object val : args) {
                System.out.println(val);
            }
        } else {
            System.out.println("no args");
        }
        return null;
    }
    
}

public class ProxyDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        InvocationHandler handler = new MyInvocationHandler();
        People people = (People)Proxy.newProxyInstance(People.class.getClassLoader(), new Class[]{People.class}, handler);
        people.walk();
        people.sayHello("ivy");
    }

}

输出结果:

----running method : public abstract void com.ivy.reflection.People.walk()
no args
----running method : public abstract void com.ivy.reflection.People.sayHello(java.lang.String)
args :
ivy

package com.ivy.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

public class GenericTypeDemo {

    private Map<String, Integer> score;
    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub

        Class<GenericTypeDemo> clazz = GenericTypeDemo.class;
        Field f = clazz.getDeclaredField("score");
        Class<?> a = f.getType();
        System.out.println("score type is : " + a);
        Type gType = f.getGenericType();
        if (gType instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType)gType;
            Type rType = pType.getRawType();
            System.out.println("raw type : " + rType);
            Type[] tArgs = pType.getActualTypeArguments();
            for (int i =0; i< tArgs.length; i++) {
                System.out.println(i + ":" + tArgs[i]);
            }
        } else {
            System.out.println("error");
        }
    }

}

输出结果:

score type is : interface java.util.Map
raw type : interface java.util.Map
0:class java.lang.String
1:class java.lang.Integer

java的动态代理机制详解

 

在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制,所以本篇随笔就是对java的动态机制进行一个回顾。

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

Object invoke(Object proxy, Method method, Object[] args) throws Throwable proxy:  指代我们所代理的那个真实对象method:  指代的是我们所要调用真实对象的某个方法的Method对象args:  指代的是调用真实对象某个方法时接受的参数

如果不是很明白,等下通过一个实例会对这几个参数进行更深的讲解。

接下来我们来看看Proxy这个类:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

Java反射添加对象属性 java反射加载类_Java

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

Java反射添加对象属性 java反射加载类_Java

好了,在介绍完这两个接口(类)以后,我们来通过一个实例来看看我们的动态代理模式是什么样的:

首先我们定义了一个Subject类型的接口,为其声明了两个方法:

public interface Subject { public void rent(); public void hello(String str); }

接着,定义了一个类来实现这个接口,这个类就是我们的真实对象,RealSubject类:

Java反射添加对象属性 java反射加载类_Java

public class RealSubject implements Subject
{
    @Override
    public void rent()
    {
        System.out.println("I want to rent my house");
    }
    
    @Override
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }
}

Java反射添加对象属性 java反射加载类_Java

下一步,我们就要定义一个动态代理类了,前面说个,每一个动态代理类都必须要实现 InvocationHandler 这个接口,因此我们这个动态代理类也不例外:

Java反射添加对象属性 java反射加载类_Java

public class DynamicProxy implements InvocationHandler
{
    // 这个就是我们要代理的真实对象
    private Object subject;
    
    //    构造方法,给我们要代理的真实对象赋初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }
    
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        
        //  在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }

}

Java反射添加对象属性 java反射加载类_Java

最后,来看看我们的Client类:

public class Client
{
    public static void main(String[] args)
    {
        //    我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
    }
}

Java反射添加对象属性 java反射加载类_Java

我们先来看看控制台的输出:

Java反射添加对象属性 java反射加载类_Java

$Proxy0
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house

before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house

Java反射添加对象属性 java反射加载类_Java

我们首先来看看 $Proxy0 这东西,我们看到,这个东西是由 System.out.println(subject.getClass().getName()); 这条语句打印出来的,那么为什么我们返回的这个代理对象的类名是这样的呢?

Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号

接着我们来看看这两句 

subject.rent();
subject.hello("world");

这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行:

Java反射添加对象属性 java反射加载类_Java

public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        
        //  在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }

Java反射添加对象属性 java反射加载类_Java

我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们的这个 method 对象是这样的:

public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

这就是我们的java动态代理机制

 

本篇随笔详细的讲解了java中的动态代理机制,这个知识点非常非常的重要,包括我们Spring的AOP其就是通过动态代理的机制实现的,所以我们必须要好好的理解动态代理的机制。