深入Java机制(一)--反射机制和动态代理机制

本系列我将会把自己的学习到Java机制的笔记整理一遍,结合各大实例,从反射机制到动态代理到类加载等等......大家看到有错麻烦请指出。今天讲讲反射机制以及动态代理机制(含代理模式、源码阅读以及对比静态代理),而且基本现在的主流框架都应用了反射机制,如spring、MyBatis、Hibernate等等,这就有非常重要的学习意义了。

文章结构:(1)反射机制概述;(2)反射的相关API;(3)反射机制相关问题;(4)基于反射的动态代理机制(源码阅读以及与静态代理对比)


一、反射机制概述:

(1)定义:

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

(2)作用:(相关面试问题在后面罗列)

(一)在运行时判断任意一个对象所属的类;

(二)在运行时构造任意一个类的对象;

(三)在运行时判断任意一个类所具有的成员变量和方法;

(四)在运行时调用任意一个对象的方法;

(五)在运行时创建新类对象

(六)生成动态代理。(这个我们最后谈)

二、反射的相关API:

此API部分以此博客为基本去扩展

(1)通过一个对象获得完整的包名和类名:

package com.fuzhu.reflect;

public class TestReflect {
    public static void main(String[] args) throws Exception {
        TestReflect testReflect = new TestReflect();
        System.out.println(testReflect.getClass().getName());
        // 结果 com.fuzhu.reflect.TestReflect
    }
}

(2)实例化Class类对象:

package com.fuzhu.reflect;
public class TestReflect1 {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        Class<?> class2 = null;
        Class<?> class3 = null;
        // 一般采用这种形式
        class1 = Class.forName("com.fuzhu.reflect.TestReflect1");
        class2 = new TestReflect1().getClass();
        class3 = TestReflect1.class;
        System.out.println("类名称   " + class1.getName());
        System.out.println("类名称   " + class2.getName());
        System.out.println("类名称   " + class3.getName());
    }
}
/*
    输出:
    类名称   com.fuzhu.reflect.TestReflect1
    类名称   com.fuzhu.reflect.TestReflect1
    类名称   com.fuzhu.reflect.TestReflect1
*/

(3)获取一个对象的父类与实现的接口

package com.fuzhu.reflect;
import com.fuzhu.reflect.TestInterface;
public class TestReflect2 implements TestInterface {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.fuzhu.reflect.TestReflect2");
        // 取得父类
        Class<?> parentClass = clazz.getSuperclass();
        System.out.println("clazz的父类为:" + parentClass.getName());
        // clazz的父类为: java.lang.Object
        // 获取所有的接口
        Class<?> intes[] = clazz.getInterfaces();
        System.out.println("clazz实现的接口有:");
        for (int i = 0; i < intes.length; i++) {
            System.out.println((i + 1) + ":" + intes[i].getName());
        }
        // clazz实现的接口有:
        // 1:com.fuzhu.reflect.TestInterface
    }
}

(4)获取某个类中的全部构造函数,通过反射机制实例化一个类的对象:

先弄个普通实体类

package com.fuzhu.reflect;
public class Goods {
    private int id;
    private String goodsName;

    public Goods() {
        super();
    }

    public Goods(int id) {
        super();
        this.id = id;
    }

    public Goods(int id, String goodsName) {
        super();
        this.id = id;
        this.goodsName = goodsName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "id=" + id +
                ", goodsName='" + goodsName + '\'' +
                '}';
    }
}

然后再写操作此实体类的

package com.fuzhu.reflect;
public class TestReflect4 {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        class1 = Class.forName("com.fuzhu.reflect.Goods");
        // 第一种方法,实例化默认构造方法,调用set赋值
        Goods goods = (Goods)class1.newInstance();
        goods.setId(12);
        goods.setGoodsName("床");
        System.out.println(goods);
        // 结果 Goods [id=12, goodsName=床]
        // 第二种方法 取得全部的构造函数 使用构造函数赋值
        Constructor<?> cons[] = class1.getConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0;i<cons.length;i++){
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.print("cons[" + i + "] (");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }
        // 结果
        // cons[0] (java.lang.String)
        // cons[1] (int,java.lang.String)
        // cons[2] ()
        goods  = (Goods)cons[2].newInstance();
        System.out.println(goods);
        // 结果 Goods{id=0, goodsName='null'}
        goods = (Goods) cons[1].newInstance(150);
        System.out.println(goods);
        goods = (Goods) cons[0].newInstance(20, "椅子");
        // 结果 Goods{id=150, goodsName='null'}
        System.out.println(goods);
        // 结果 Goods{id=20, goodsName='椅子'}
    }
}

注意 class1.getConstructors()获取的是构造器的栈,就是哪个构造器在前面就丢到cons[]栈底部。上面是无参构造器丢到最底部。

(5)获取某个类的全部属性:(接口和父类的属性以及本类自身的共有属性)

package com.fuzhu.reflect;
public interface ReflectInterface {
    int test2 =12;
}
package com.fuzhu.reflect;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class TestReflect5 implements ReflectInterface {
    private static final long serialVersionUID = -286258502L;
    public int test = 0;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.fuzhu.reflect.TestReflect5");
        System.out.println("===============本类属性===============");
        // 取得本类的全部属性
        Field[] field = clazz.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
        }

        System.out.println("==========实现的接口或者父类的属性以及本类的公有属性名字==========");
        // 取得实现的接口或者父类的属性以及本类的公有属性名字
        Field[] filed1 = clazz.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 权限修饰符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
        }
    }
}

(6)获取某个类的全部方法:包括父类、接口和自身方法(所有权限都可)

public class TestReflect6 implements ReflectInterface {
    @Override
    public void test() {
        System.out.println("反射调用了TestReflect6的test方法");//为一会TestReflect7的调用做准备
    }
    private static final long serialVersionUID = -255236662L;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.fuzhu.reflect.TestReflect6");
        Method method[] = clazz.getMethods();//拿到所有方法
        for (int i = 0; i < method.length; ++i) {
            Class<?> returnType = method[i].getReturnType();
            Class<?> para[] = method[i].getParameterTypes();
            int temp = method[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");//拿权限,他是包括了final、static的
            System.out.print(returnType.getName() + "  ");//返回类型
            System.out.print(method[i].getName() + " ");//方法名字
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);//拿到参数名字
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method[i].getExceptionTypes();//拿到方法异常内和抛出
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");//拿到异常名字
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }
    }
}

(7)通过反射机制调用某个类的方法:

package com.fuzhu.reflect;

import java.lang.reflect.Method;
public class TestReflect7  {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.fuzhu.reflect.TestReflect7");
        // 调用TestReflect类中的reflect1方法
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        // Java 反射机制 - 调用某个类的方法1.
        // 调用TestReflect的reflect2方法
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "辅助");
        // Java 反射机制 - 调用某个类的方法2.
        // age -> 20. name -> 辅助

        Class<?> clazz2 = Class.forName("com.fuzhu.reflect.TestReflect6");
        Method method2 = clazz2.getMethod("test");
        method2.invoke(clazz2.newInstance());
        //请看回TestReflect6 的test方法
    }
    public void reflect1() {
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    }
    public void reflect2(int age, String name) {
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }
}

另外我们顺便看下源码了解下getMethod方法

/*
name: method的名称
parameterTypes:method的参数类型的列表(参数顺序需按声明method时的参数列表排列)
返回:符合method名称和参数的method对象
抛出错误:NoSuchMethodException   
        原因:没有找到所要查询的Method对象  或  Method名称为“<init>”或“<clinit>”
        NullPointerException
        原因:所要查询的Method对象的名称为null
        SecurityException
         原因:调用的类或其父类没有调用权限
*/
  @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

parameterTypes是可变长参数。

(8)通过反射机制操作某个类的属性:

package com.fuzhu.reflect;

import java.lang.reflect.Field;

public class TestReflect8 {
    private String proprety = null;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.fuzhu.reflect.TestReflect8");
        Object obj = clazz.newInstance();
        // 可以直接对 private 的属性赋值
        Field field = clazz.getDeclaredField("proprety");
        field.setAccessible(true);
        field.set(obj, "Java反射机制");
        System.out.println(field.get(obj));
    }
}

反射机制的例子:

首先我们要明白,编译后就会把泛型给擦除,还原到类与方法本质。

(1)在泛型为Integer的ArrayList中存放一个String类型的对象。

另外我们还要探讨为什么只能传入Object.class

public class TestReflect {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "Java反射机制实例。");

        System.out.println(list.get(0));

        
        //下面是探讨为什么反射的时候add方法只能传入Object.class,因为我们拿到的add方法本质上,public boolean  add (java.lang.Object arg0
        Method method1[] = list.getClass().getMethods();//拿到所有方法
        for (int i = 0; i < method1.length; ++i) {
            Class<?> returnType = method1[i].getReturnType();
            Class<?> para[] = method1[i].getParameterTypes();
            int temp = method1[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");//拿权限,他是包括了final、static的
            System.out.print(returnType.getName() + "  ");//返回类型
            System.out.print(method1[i].getName() + " ");//方法名字
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);//拿到参数名字
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            System.out.println();
        }
    }
}

(2)通过反射取得并修改数组的信息:

注意是反射包下面的类

import java.lang.reflect.Array;
public class TestReflect1 {
    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5 };
        Class<?> demo = temp.getClass().getComponentType();
        System.out.println("数组类型: " + demo.getName());
        System.out.println("数组长度  " + Array.getLength(temp));
        System.out.println("数组的第一个元素: " + Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
    }
}

(3)通过反射机制修改数组的大小:

注意是反射包下面的类

import java.lang.reflect.Array;
public class TestReflect2 {
    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        int[] newTemp = (int[]) arrayInc(temp, 15);
        print(newTemp);
        String[] atr = { "a", "b", "c" };
        String[] str1 = (String[]) arrayInc(atr, 8);
        print(str1);
    }
    // 修改数组大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        System.out.println("数组长度为: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }
}

(4)利用反射实现spring的注解注入:

1)我们框架使用的注解:本质是这样的

/*
    像SpringMVC的@controller,@Service那样,就是个注解
*/
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}


2)这里就是注解管理。包括:注入的方式、注入前的配置解析抽象等等

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ManagerAnnotation {//注解管理
    //自定义注解,此注解是注入的注解,类似spring的设值注入,抽象到此处。
    //返回了一个Class对象,在这里我们并没有像spring那样通过自定义的名字来注入,而是通过该类的类型直接注入
    public Class test() ;
}

3)我们实体类的接口:

public interface  IGoods {
    void buy();
}

4)实体类的实现类:

public class BedGoodsImpl implements IGoods {
    @Override
    public void buy() {
        System.out.println("注入了床这个bean");
    }
}

5)服务的实体类:

@Service
public class ServiceImple {

    private IGoods goodsDao;
    //设值注入
    @ManagerAnnotation(test=BedGoodsImpl.class)
    public void setUserdao(IGoods goodsDao) {
        this.goodsDao = goodsDao;
    }
    public void buy_Test(){
        goodsDao.buy();
    }
}

6)Spring容器:

spring的思想,控制反转-依赖注入,而不是主动申请创建实例。

public class SpringContainer {
    public static ServiceImpl getBean() {

        ServiceImpl bean = new ServiceImpl();//spring预先帮你创建实例
        boolean isAnnotation = ServiceImpl.class.isAnnotationPresent(Service.class);

        if (isAnnotation) {

            Method[] method = ServiceImpl.class.getDeclaredMethods();

            for (Method method2 : method) {
                if (method2.isAnnotationPresent(ManagerAnnotation.class)) {
                    ManagerAnnotation managerAnnotation = method2.getAnnotation(ManagerAnnotation.class);
                    System.out.println("设值注入AnnotationTest(注入的方法field=" + method2.getName()
                            + ",注入的值test=" + managerAnnotation.test() + ")");
                    try {
                        Class<?> clazz = managerAnnotation.test();//反射获取该类class
                        IGoods iGoods = (IGoods) clazz.newInstance();//构造实例
                        bean.setUserdao(iGoods);//值注入--实例的属性注入

                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                        return null;
                    }
                }
            }
        }
        return bean;
    }
}

7)接着就是用那个spring容器了嘛:

public class TestSpring {
    public static void main(String[] args) {

        ServiceImpl bean=SpringContainer.getBean();

        bean.buy_Test();

    }
}

当然,这只是个小demo,spring比这个牛逼多了。

三、反射机制的相关问题:

(1)反射的优缺点:

在理解这个前,我们要先理解静态编译和动态编译。

静态编译:在编译时确定类型,绑定对象,即通过

动态编译:运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,体现了多态的应用,有利于降低类之间的耦合性。

优点:

可以实现动态创建对象和编译,体现出很大的灵活性。

缺点:

对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求,这意味着通信,意味着又要执行多个步骤。

(2)常用到的地方:

1. 观察或操作应用程序的运行时行为。

2. 调试或测试程序,因为可以直接访问方法、构造函数和成员字段。

3. 通过名字调用不知道的方法并使用该信息来创建对象和调用方法。

另外我们要理解一个东西:

在Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型

编译时的类型由声明该对象时使用的类型决定,运行时的类型由实际赋给对象的类型决定:

如:Person p =new Student();
编译时类型为Person,而运行时为Student

如果编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,此时就必须使用反射

(3)反射的存在是为了什么??

反射是为了能够动态的加载一个类,动态的调用一个方法,动态的访问一个属性等动态要求而设计的。它的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下得API以达到各种动态需求。

(4)Class类的含义和作用是什么?

每一个Class类的对象就代表了一种被加载进入JVM的类,它代表了该类的一种信息映射。开发者可以通过3种途径获取到Class对象。

1)Class的forName()方法的返回值就是Class类型,也就是动态导入类的Class对象的引用。

public static Class<?> forName(String className) throws ClassNotFoundException

2)每个类都会有一个名称为Class的静态属性,通过它也是可以获取到Class对象的,示例代码如下:

Class<Student> clazz = Student.class;   // 访问Student类的class属性

3)Object类中有一个名为getClass的成员方法,它返回的是对象的运行时类的Class对象。因为Object类是所有类的父类,所以,所有的对象都可以使用该方法得到它运行时类的Class对象,示例代码如下:

Student stu = new Student();   
Class<Student> clazz = stu.getClass();   // 调用Student对象的getName方法

四、基于反射的动态代理机制:

(1)理解设计模式之代理模式:

定义:

为其他对象提供一种代理以控制对这个对象的访问。

角色:以打游戏、游戏代练为例子

1. Subject抽象主题角色。是抽象类或接口,是一个最普通的业务类型定义。游戏者接口。

2. RealSubject具体主题角色。被委托角色、被代理角色,业务逻辑的具体执行者。游戏者--这人想找代练。

3. Proxy代理主题角色。委托类、代理类,负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并在真实主题角色处理完毕前后做预处理和善后处理。代练者。

优点:

1.职责清晰。真实的角色就是实现实际的业务逻辑,不必关心其他非本职责的事务。

2. 高扩展性。具体主题角色可能随时发生变化,然而代理类完全可不做任何修改的情况下使用。

缺点:

无法摆脱仅支持interface代理圈子。那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。

DEMO:

//游戏者接口,代理者也是游戏者
public interface IGamePlayer{
        //登录游戏
        public void login(String user,String password);
        //杀怪,网络游戏的主要特色
        public void killBoss();
        //升级
        public void upgrade();
}

//普通代理的游戏者
/*
*构造函数中,传递进一个IGamePlayer对象,检查谁能创建真实的角色,当然还可有其他限制,如类名必须为Proxy等
*/
public class GamePlayer implements IGamePlayer{
    private String name = "";
    //构造函数限制谁能创建对象,并同时传递姓名
    public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception{
            if(_gamePlayer ==null){
                    throw new Exception("不能创建真实角色!");
            }else{
                    this.name = _name;
            }
    }
    //打怪,最期望的就是杀boss
    public void killBoss(){
            System.out.println(this.name + "在打怪!");
    }
    //进游戏之前你肯定要登录,这是个必要条件
    public void login(String user,String password){
            System.out.println("登录名为"+user+"的用户"+this.name+"登录成功!");
    }
    //升级,升级有很多方法,花钱买是一种,做任务是一种
    public void update(){
            System.out.println(this.name + " 又升了一级");
    }
}

代练的帅哥:

//普通代理的代理者:
/*
*   仅修改构造函数,传递进来一个代理者名称,即可进行代理。调用者只需知道代理的存在,不用知道代理谁。
*/
public class GamePlayerProxy implements IGamePlayer{
     private IGamePlayer gamePlayer = null;
     //通过构造函数传递要对谁进行代练(也就是说很多人可以找这个代理者去代理)
     public GamePlayerProxy(String name){
            try{
                    gamePlayer = new GamePlayer(this.name);     //以代理者这个人去获得游戏者的账号。以对象创建对象。
            }catch(Exception e){

            }
     }
     //代练杀怪
     public void killBoss(){
            this.gamePlayer.killBoss();                 ////真正杀怪的是主题类,游戏者的账号
     }
     //代练登录
     public void login(String user,String password){
            this.gamePlayer.login(user,password);       //真正登录的是主题类,游戏者的账号
     }
     //代练升级
     public void update(){
            this.gamePlayer.upgrade();      //真正升级的是主题类,游戏者的账号
     }

}

调用场景:

//场景类-调用者
/*
*   此模式下,调用者只需知道代理而不用知道真实角色是谁,屏蔽角色的变更对高层模块的影响
*/
public class Client{
    public static void main(String []args){
            //然后再定义一个代练者
            IGamePlayer proxy = new GamePlayerProxy("张三");
            //开始打游戏,记下时间点:
            System.out.println("开始时间是: 2009-8-25  10:45");
            proxy.login("zhangsan","password");
            //开始杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录结束游戏时间
            System.out.println("结束时间是: 2017-6-6  18:33");
    }
}

(2)动态代理:

其实就是灵活运用代理模式的一种设计。

动态代理主要涉及两个类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class)。

先来看个例子:

//要这个接口来表示要被代理的对象,而且代理对象也通过接口去寻找被代理对象。
public interface ProxyInterface {
    public String say(String name, int age);
}
//真实的对象。要被代理的对象。
public class RealProxy implements ProxyInterface{

    @Override
    public String say(String name, int age) {
        return name + "  " + age;
    }
}
//动态代理类
public class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;
    //目的是把要被代理的对象set进来
    public Object setAgent(Object obj) {
        this.obj = obj;
        //Proxy这个类的作用就是用来动态创建一个代理对象的类,newProxyInstance这个方法的作用就是得到一个动态的代理对象。一会详细讲讲其源码。
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object temp = method.invoke(this.obj, args);
        return temp;
    }
}
//场景应用
public class TestProxy {
    public static void main(String[] args) throws Exception {
    //代理类
        MyInvocationHandler demo = new MyInvocationHandler();
        //把要被代理set进代理类
        ProxyInterface proxyInterface = (ProxyInterface) demo.setAgent(new RealProxy());
        //代理类去调用
        String info = proxyInterface.say("辅助", 20);
        System.out.println(info);
    }
}

(3)动态代理类相关类的源码:

我们入手的话最好按Proxy.newProxyInstance()方法跳进去看。

/**
 该方法负责集中处理动态代理类上的所有方法调用。
 第一个参数既是代理类实例,
 第二个参数是被调用的方法对象
 第三个方法是调用参数。
 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
*/
public interface InvocationHandler {
            public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

//有序列化机制的类
public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;
    //代理类的参数类型。就是说有这个InvocationHandler接口的类都是代理类。
    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
     //代理类的缓存
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
        //代理类
    protected InvocationHandler h;
    //空构造器
    private Proxy() {
    }
    //传进InvocationHandler接口下的实体类
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    //该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
     @CallerSensitive
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
        //测是否要创建新代理
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
//如果实现接口列表的代理类已经存在,那么直接从cache中拿。如果不存在, 则通过ProxyClassFactory生成一个。 
        return getProxyClass0(loader, intfs);
    }
    //检验是否需要创建代理类
    private static void checkProxyAccess(Class<?> caller,
                                         ClassLoader loader,
                                         Class<?>... interfaces)
    {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = caller.getClassLoader();
            if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
        }
    }
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //如果实现接口列表的代理类已经存在,那么直接从cache中拿。如果不存在, 则通过ProxyClassFactory生成一个。
        return proxyClassCache.get(loader, interfaces);
    }
     //一个用于代理类的第0个实现接口的密钥
     private static final Object key0 = new Object();
     //  KEY1和KEY2为动态代理的常用优化,可实现1个或2个接口。
     private static final class Key1 extends WeakReference<Class<?>> {
        private final int hash;

        Key1(Class<?> intf) {
            super(intf);
            this.hash = intf.hashCode();
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            Class<?> intf;
            return this == obj ||
                   obj != null &&
                   obj.getClass() == Key1.class &&
                   (intf = get()) != null &&
                   intf == ((Key1) obj).get();
        }
    }
//同上
private static final class Key2 extends WeakReference<Class<?>> {
        private final int hash;
        private final WeakReference<Class<?>> ref2;

        Key2(Class<?> intf1, Class<?> intf2) {
            super(intf1);
            hash = 31 * intf1.hashCode() + intf2.hashCode();
            ref2 = new WeakReference<Class<?>>(intf2);
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            Class<?> intf1, intf2;
            return this == obj ||
                   obj != null &&
                   obj.getClass() == Key2.class &&
                   (intf1 = get()) != null &&
                   intf1 == ((Key2) obj).get() &&
                   (intf2 = ref2.get()) != null &&
                   intf2 == ((Key2) obj).ref2.get();
        }
    }
   
    private static final class KeyX {
        private final int hash;
        private final WeakReference<Class<?>>[] refs;

        @SuppressWarnings("unchecked")
        KeyX(Class<?>[] interfaces) {
            hash = Arrays.hashCode(interfaces);
            refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                refs[i] = new WeakReference<>(interfaces[i]);
            }
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj ||
                   obj != null &&
                   obj.getClass() == KeyX.class &&
                   equals(refs, ((KeyX) obj).refs);
        }
        private static boolean equals(WeakReference<Class<?>>[] refs1,
                                      WeakReference<Class<?>>[] refs2) {
            if (refs1.length != refs2.length) {
                return false;
            }
            for (int i = 0; i < refs1.length; i++) {
                Class<?> intf = refs1[i].get();
                if (intf == null || intf != refs2[i].get()) {
                    return false;
                }
            }
            return true;
        }
    }
    //key的工厂
    private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }
    //终于找到我们的代理工厂啦!!!在缓存中找不到的时候就来这里创建代理类了。
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
//内部类接口方法去创建
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                //要加载指定的接口,反射拿到对应的class。  加载每一个接口运行时的信息
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //如果使用你自己的classload加载的class与你传入的class不相等,抛出异常
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                //如果传入不是一个接口类型,就抛异常
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                 //验证接口是否重复
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
             //检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。也就是说,这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常 。
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    //如果不是公开的代理接口
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
             //选择要生成的代理名称
            long num = nextUniqueNumber.getAndIncrement();
            //生成随机代理类的类名, Proxy + num
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
             /*
              * 生成代理类的class文件, 返回字节流。实际上真正生成代理的类是在这里ProxyGenerator。
                 */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    //三个参数分别是:ClassLoader,指定的接口及我们自己定义的InvocationHandler类。
    //用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
        //检查要不要弄个新的代理,就是获得是否创建代理类权限
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        // 获得与制定类装载器和一组接口相关的代理类类型对象
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
         // 通过反射获取构造函数对象并生成代理类实例
        try {
            if (sm != null) {
         //检查要不要弄个新的代理,就是获得是否创建代理类权限
        checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
//获得构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //把代理类拿下来
            final InvocationHandler ih = h;
            //看你传入的接口中有没有不是public的接口
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //如果没有就传进参数,构造新的代理类咯
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
//检查是否创建新代理类的许可
    private static void checkNewProxyPermission(Class<?> caller, Class<?> proxyClass) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
                ClassLoader ccl = caller.getClassLoader();
                ClassLoader pcl = proxyClass.getClassLoader();

                // do permission check if the caller is in a different runtime package
                // of the proxy class
                //如果调用方处于不同的运行包,就检查代理类的权限
                int n = proxyClass.getName().lastIndexOf('.');
                String pkg = (n == -1) ? "" : proxyClass.getName().substring(0, n);

                n = caller.getName().lastIndexOf('.');
                String callerPkg = (n == -1) ? "" : caller.getName().substring(0, n);

                if (pcl != ccl || !pkg.equals(callerPkg)) {
                    sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
                }
            }
        }
    }
//判断是否是代理类中,从缓存池中判断。用于判断指定类对象是否是一个动态代理类。
    public static boolean isProxyClass(Class<?> cl) {
        return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    }
    //获取被InvocationHandler接口标记的代理类。用于获取指定代理对象所关联的调用处理器
    @CallerSensitive
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
    {
        /*
         * Verify that the object is actually a proxy instance.
         */
        if (!isProxyClass(proxy.getClass())) {
            throw new IllegalArgumentException("not a proxy instance");
        }

        final Proxy p = (Proxy) proxy;
        final InvocationHandler ih = p.h;
        if (System.getSecurityManager() != null) {
            Class<?> ihClass = ih.getClass();
            Class<?> caller = Reflection.getCallerClass();
            if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
                                                    ihClass.getClassLoader()))
            {
                ReflectUtil.checkPackageAccess(ihClass);
            }
        }

        return ih;
    }

    private static native Class<?> defineClass0(ClassLoader loader, String name,byte[] b, int off, int len);

总体流程:

1)通过实现InvocationHandler接口创建自己的调用处理器 。

InvocationHandler handler = new InvocationHandlerImpl(...);

2)通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类。就是说指定代理谁。

Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

3)通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4)通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。

生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))。

注意点:

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

2)Java中,必须通过接口去管理动态代理。

3)对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

(4)认知静态代理与动态代理:

静态代理:

若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理。

例子:

通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

//销售接口
public interface Sell {
    //销售
    void sell();
}
//经销商
public class Vendor implements Sell {

    public void sell() {
        System.out.println("In sell method");
    }
}
//代理商
public class BusinessAgent implements Sell {
    public static boolean isCollegeStudent =true;

    private Vendor vendor = new Vendor();
    @Override
    public void sell() {
//是大学生才卖,只卖给大学生
        if (isCollegeStudent) {
            vendor.sell();
        }
    }
}

动态代理

代理类在程序运行时创建的代理方式被成为 动态代理。

也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

对比:

通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。

详情例子请见上面。


好了,深入Java机制(一)--反射机制和动态代理机制讲完了,又是一篇源码机制阅读记录,以后还有内存机制、类加载机制等等这是积累的必经一步,我会继续出这个系列文章,分享经验给大家。欢迎在下面指出错误,共同学习!!你的点赞是对我最好的支持!!

更多内容,可以访问JackFrost的博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容