Java高级程序员必备:反射、动态代理

1. Java类型系统

获取Java类型系统,主要有两个方式:一种是传统的RTTI(Run-Time Type Identification),它假定咱们在编译时已经知道了全部的类型信息;另外一种是反射(Reflect),它容许咱们在程序运行时获取并使用类型信息。

假若有一个简单的继承体系,让咱们看下在RTTI和Reflect不一样状况下如何获取类型信息。java

Animal为接口,定义getType以返回不一样动物的类型,Cat、Dog、Elephant等为具体实现类,均实现getType接口。通常状况下,咱们会建立一个具体的对象(Cat,Dog,Elephant等),把它向上转型为Animal,并在程序后面直接使用Animal引用。设计模式

具体样例代码以下:数组

/**
 * 动物
 */
public interface Animal {
    /**
     * 获取动物类型
     * @return
     */
    String getType();
}

/**
 * 动物具体子类 猫
 */
public class Cat implements Animal{

    @Override
    public String getType() {
        return "猫";
    }
}

/**
 * 动物具体子类 狗
 */
public class Dog implements Animal{
    @Override
    public String getType() {
        return "狗";
    }
}

/**
 * 动物具体实现 大象
 */
public class Elephant implements Animal{
    @Override
    public String getType() {
        return "大象";
    }
}

让咱们看下相同的功能经过硬编码反射两个机制如何实现。安全

1.1. 硬编码

RTTI假定在编译期,已经知道了全部的类型信息。在编码时,能够直接使用具体的类型信息,这是咱们最多见的类型用法。

在编译期,编译器经过容器、泛型保障类型系统的完整性;在运行时,由类型转换操做来确保这一点。app

硬编码样例以下:框架

public static void main(String... args){
    List<Animal> animals = createAnimals();
    for (Animal animal : animals){
        System.out.println(animal.getType());
    }

}

/**
 * RTTI假定咱们在编译时已经知道了全部的类型
 * @return
 */
private static List<Animal> createAnimals() {
    List<Animal> animals = new ArrayList<>();
    animals.add(new Cat()); // 已知类型Cat
    animals.add(new Elephant()); // 已知类型Elephant
    animals.add(new Dog()); // 已知类型 Dog
    return animals;
}

在这个例子中,咱们把Cat、Elephant、Dog等向上转型为Animal并存放于List<Animal>中,在转型过程当中丢失了具体的类型信息(只保留了接口信息Animal);当咱们从List<Animal>中取出元素时,这时容器(容器内部全部的元素都被当作Object)会自动将结果转型成Animal。这是RTTI最基本的用法,在Java中全部的类型转换都是在运行时进行有效性检查。这也是RTTI的含义,在运行时,识别一个对象的类型。ide

1.2. Reflect

Reflect容许咱们在运行时获取并使用类型信息,它主要用于在编译阶段没法得到全部的类型信息的场景,如各种框架。

反射样例以下:函数

private static final String[] ANIMAL_TYPES = new String[]{
        "com.example.reflectdemo.base.Cat",
        "com.example.reflectdemo.base.Elephant",
        "com.example.reflectdemo.base.Dog"
};

public static void main(String... args){
    List<Object> animals = createAnimals();
    for (Object animal : animals){
        System.out.println(invokeGetType(animal));
    }

}

/**
 * 利用反射API执行getType方法(等同于animal.getType)
 * @param animal
 * @return
 */
private static String invokeGetType(Object animal){
    try {
        Method getTypeMethod = Animal.class.getMethod("getType");
        return (String) getTypeMethod.invoke(animal);
    }catch (Exception e){
        return null;
    }

}

/**
 * 反射容许咱们在运行时获取类型信息
 * @return
 */
private static List<Object> createAnimals() {
    List<Object> animals = new ArrayList<>();
    for (String cls : ANIMAL_TYPES){
        animals.add(instanceByReflect(cls));
    }
    return animals;
}

/**
 * 使用反射机制,在运行时动态的实例化对象(等同于new关键字)
 * @param clsStr
 * @return
 */
private static Object instanceByReflect(String clsStr) {
    try {
        // 经过反射获取类型信息
        Class cls = Class.forName(clsStr);
        // 经过Class实例化对象
        Object object = cls.newInstance();
        return object;
    }catch (Exception e){
        e.printStackTrace();
        return null;
    }

}

反射,能够经过一组特殊的API,在运行时,动态执行全部Java硬编码完成的功能(如对象建立、方法调用等)。性能

相比硬编码,Java反射API要复杂的多,但其给咱们带来了更大的灵活性。this

2. Class对象

要理解RTTI在Java中的工做原理,首先须要知道类型信息在Java中是如何表示的。这个工做是由称为Class对象的特殊对象完成的,它包含了与类相关的全部信息。Java使用Class对象来执行RTTI。

类是程序的一部分,每一个类都会有一个Class对象。每当编写并编译一个新类(动态代理、CGLIB、运行时编译都能建立新类),就会产生一个Class对象,为了生成这个类的对象,运行这个程序的JVM将使用称为“类加载器”的子系统。

2.1. Class Loader

类加载器子系统,是JVM体系重要的一环,主要完成将class二进制文件加载到JVM中,并将其转换为Class对象的过程。

类加载器子系统其实是一条类加载器链,可是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的是可信类,包括Java API类,他们一般是从本地加载。在这条链中,一般不须要添加额外的类加载器,可是若是有特殊需求,能够挂载新的类加载器(好比Web容器)。

全部的类都是在第一次使用时,动态加载到JVM中的,当程序建立第一次对类的静态成员引用时,就会加载这个类。实际上构造函数也是类的静态方法,所以使用new关键字建立类的新对象也会被当作对类的静态引用,从而触发类加载器对类的加载。

Java程序在它开始运行以前并不是被所有加载,各个部分是在须要时按需加载的。类加载器在加载类以前,首先检查这个类的Class是否已经加载,若是还没有加载,加载器会按照类名查找class文件,并对字节码进行有效性校验,一旦Class对象被载入内存,它就用来建立这个类的全部对象。

static初始化块在类加载时调用,所以能够用于观察类在何时进行加载,样例以下:

static class C1{
    static {
        System.out.println("C1");
    }
}

static class C2{
    static {
        System.out.println("C2");
    }
}

static class C3{
    static {
        System.out.println("C3");
    }
}

public static void main(String... args) throws Exception{
    System.out.println("new start");
    // 构造函数为类的静态引用,触发类型加载
    new C1();
    new C1();
    System.out.println("new end");

    System.out.println();

    System.out.println("Class.forName start");
    // Class.forName为Class上的静态函数,用于强制加载Class
    Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2");
    Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2");
    System.out.println("Class.forName end");

    System.out.println();

    System.out.println("C3.class start");
    // Class引用,会触发Class加载,可是不会触发初始化
    Class c1 = C3.class;
    Class c2 = C3.class;
    System.out.println("C3.class end");

    System.out.println();

    System.out.println("c1.newInstance start");
    // 调用class上的方法,触发初始化逻辑
    c1.newInstance();
    System.out.println("c1.newInstance end");

}

输出结果为:

new start
C1
new end

Class.forName start
C2
Class.forName end

C3.class start
C3.class end

c1.newInstance start
C3
c1.newInstance end

看结果,C3.class的调用不会自动的初始化该Class对象(调用static块)。为了使用Class而作的准备工做主要包括三个步骤:

  1. 加载,这个是由类加载器执行。该步骤将查找字节码文件,并根据字节码建立一个Class对象。
  2. 连接,在连接阶段将验证类中的字节码,为静态域分配存储空间,若是必要的话,将解析这个类建立的对其余类的引用。
  3. 初始化,若是该类有超类,则对其进行初始化,执行静态初始化器和静态初始化块。初始化被延时到对静态方法或很是数静态域进行首次访问时才执行。

2.2. Class 实例获取

Class对象做为Java类型体系的入口,如何获取实例成为第一个要解决的问题。

Class对象的获取主要有如下几种途径:

  1. ClassName.class,获取Class对象最简单最安全的方法,其在编译时会受到编译检测,但上例中已经证明,该方法不会触发初始化逻辑。
  2. Class.forName,这是反射机制最经常使用的方法之一,能够在不知具体类型时,经过一个字符串加载所对应的Class对象。
  3. object.getClass,这也是比较经常使用的方式之一,经过一个对象获取生成该对象的Class实例。

对于基本数据类型对于的包装器类,还提供了一个TYPE字段,指向对应的基本类型的Class对象。

基本类型 TYPE类型
boolean.class Boolean.TYPE
char.class Char.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE

2.3. Class 类型信息

Class对象存储了一个class的全部类型信息,当获取到Class对象后,便能经过API获取到全部信息。

在进入Class类型信息以前,须要简单的了解下几个反射的基类,以便更好的理解反射实现体系。

2.3.1 ClassAPI 基础

Class API基础主要是为反射API提供通用特性的接口或基类。因为其通用性,现统一介绍,在具体的API中将对其进行忽略。

2.3.1.1 AnnotatedElement

AnnotatedElement为Java1.5新增接口,该接口表明程序中能够接受注解的程序元素,并提供统一的Annotation访问方式,赋予API经过反射获取Annotation的能力,当一个Annotation类型被定义为运行时后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement接口是全部注解元素(Class、Method、Field、Package和Constructor)的父接口,因此程序经过反射获取了某个类的AnnotatedElement对象以后,程序就能够调用该对象的下列方法来访问Annotation信息:

方法 含义
<T extends Annotation> T getAnnotation(Class<T> annotationClass) 返回程序元素上存在的、指定类型的注解,若是该类型注解不存在,则返回null
Annotation[] getAnnotations() 返回该程序元素上存在的全部注解
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) 判断该程序元素上是否包含指定类型的注解,存在则返回true,不然返回false
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的全部注释。与此接口中的其余方法不一样,该方法将忽略继承的注释。(若是没有注释直接存在于此元素上,则返回长度为零的一个数组)该方法的调用者能够随意修改返回的数组;这不会对其余调用者返回的数组产生任何影响。

AnnotatedElement子类涵盖全部能够出现Annotation的地方,其中包括:

  1. Constructor 构造函数
  2. Method 方法
  3. Class 类型
  4. Field 字段
  5. Package 包
  6. Parameter 参数
  7. AnnotatedParameterizedType 泛型
  8. AnnotatedTypeVariable 变量
  9. AnnotatedArrayType 数组类型
  10. AnnotatedWildcardType

样例以下:

public class AnnotatedElementTest {

    public static void main(String... args){
        System.out.println("getAnnotations:");
        for (Annotation annotation :  A.class.getAnnotations()){
            System.out.println(annotation);
        }

        System.out.println();
        System.out.println("getAnnotation:" + A.class.getAnnotation(TestAnn1.class));

        System.out.println();
        System.out.println("isAnnotationPresent:" + A.class.isAnnotationPresent(TestAnn1.class));

    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnn1{

    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnn2{

    }

    @TestAnn1
    @TestAnn2
    public class A{

    }
}

输出结果以下:

getAnnotations:
@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1()
@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn2()

getAnnotation:@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1()

isAnnotationPresent:true

2.3.1.2. Member

Member用于标记反射中简单元素。

所涉及方法以下:

方法 含义
getDeclaringClass 元素所在类
getName 元素名称
getModifiers 元素修饰
isSynthetic 是否为Synthetic,synthetic是由编译器引入的字段、方法、类或其余结构,主要用于JVM内部使用。

其子类主要包括:

  1. Class 类型
  2. Field 字段
  3. Method 方法
  4. Constructor 构造函数

2.3.1.3. AccessibleObject

AccessibleObject可访问对象,其对元素的可见性进行统一封装。同时实现AnnotatedElement接口,提供对Annotation元素的访问。

所涉及方法以下:

方法 含义
isAccessible 是否可访问
setAccessible 从新访问性

其中AccessibleObject所涉及的子类主要包括:

  1. Field 字段
  2. Constructor 构造函数
  3. Method 方法

AccessibleObject 对可见性提供了强大的支持,使咱们可以经过反射扩展访问限制,甚至能够对private成员进行访问。

样例代码以下:

public class TestBean {
    private String id;

    public String getId() {
        return id;
    }

    private void setId(String id) {
        this.id = id;
    }

}
public class AccessibleObjectBase {

    public static void main(String... args) throws Exception{
        TestBean testBean = new TestBean();
        // private方法, 不能直接调用
        Method setId = TestBean.class.getDeclaredMethod("setId", String.class);
        System.out.println("setId:" + setId.isAccessible());
        try {
            setId.invoke(testBean, "111");
        }catch (Exception e){
            System.out.println("private不能直接调用");
        }
        setId.setAccessible(true);
        System.out.println("设置可访问:" + setId.isAccessible());

        setId.invoke(testBean, "111");
        System.out.println("设置可访问后,能够绕过private限制,进行调用,结果为:" + testBean.getId());

    }
}

输出结果以下:

setId:false
private不能直接调用
设置可访问:true
设置可访问后,能够绕过private限制,进行调用,结果为:111

2.3.1.4. Executable

Executable表示可执行元素的一种封装,能够获取方法签名相关信息。

所涉及方法以下:

方法 含义
getName 获取名称
getModifiers 获取修饰符
getTypeParameters 获取类型参数(泛型)
getParameterTypes 获取参数列表
getParameterCount 获取参数数量
getGenericParameterTypes 获取参数类型
getExceptionTypes 获取异常列表
getGenericExceptionTypes 获取异常列表

锁涉及的子类主要有:

  1. Constructor 构造函数
  2. Method 方法

样例代码以下:

public class TestBean {
    private String id;

    public <T, R>TestBean(String id) throws IllegalArgumentException, NotImplementedException {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    private void setId(String id) {
        this.id = id;
    }

}

public class ExecutableTest {
    public static void main(String... args) throws Exception{
        for (Constructor constructor : TestBean.class.getConstructors()){
            System.out.println("getName: " + constructor.getName());

            System.out.println();

            System.out.println("getModifiers: " + Modifier.toString(constructor.getModifiers()));

            System.out.println();

            System.out.println("getTypeParameters:");
            for (TypeVariable<Constructor> t : constructor.getTypeParameters()){
                System.out.println("type var:" + t.getName());
            }

            System.out.println();
            System.out.println("getParameterCount:" + constructor.getParameterCount());

            System.out.println();
            System.out.println("getParameterTypes:");
            for (Class cls : constructor.getParameterTypes()){
                System.out.println(cls.getName());
            }

            System.out.println();
            System.out.println("getExceptionTypes:");
            for (Class cls : constructor.getExceptionTypes()){
                System.out.println(cls.getName());
            }
        }
    }
}

输出结果为:

getName: com.example.reflectdemo.reflectbase.TestBean

getModifiers: public

getTypeParameters:
type var:T
type var:R

getParameterCount:1

getParameterTypes:
java.lang.String

getExceptionTypes:
java.lang.IllegalArgumentException
sun.reflect.generics.reflectiveObjects.NotImplementedException

2.3.1.5. 方法命名规则

整个反射机制存在着通用的命名规则,了解这些规则,能够大大减小理解方法的阻力。

getXXXgetDeclaredXXX, 二者主要区别在于获取元素的可见性不一样,通常状况下getXXX返回public类型的元素,而getDeclaredXXX获取全部的元素,其中包括private、protected、public和package。

2.3.2. 类型信息

Class自身信息包括类名、包名、父类以及实现的接口等。

Class类实现AnnotatedElement接口,以提供对注解的支持。除此之外,涉及方法以下:

方法 含义
getName 获取类名
getCanonicalName 获得目标类的全名(包名+类名)
getSimpleName 等同于getCanonicalName
getTypeParameters 获取类型参数(泛型)
getSuperclass 获取父类
getPackage 获取包信息
getInterfaces 获取实现接口
getModifiers 获取修饰符
isAnonymousClass 是否匿名类
isLocalClass 是否局部类
isMemberClass 是否成员类
isEnum 是否枚举
isInterface 是不是接口
isArray 是不是数组
getComponentType 获取数组元素类型
isPrimitive 是不是基本类型
isAnnotation 是不是注解
getEnumConstants 获取枚举全部类型
getClasses 获取定义在该类中的public类型
getDeclaredClasses 获取定义在该类中的类型

实例以下:

class Base<T> implements Callable<T> {

    @Override
    public T call() throws Exception {
        return null;
    }
}
public final class BaseClassInfo<T, R extends Runnable> extends Base<T> implements Runnable, Serializable {

    @Override
    public void run() {

    }


    public static void main(String... args){
        Class<BaseClassInfo> cls = BaseClassInfo.class;

        System.out.println("getName:" + cls.getName());
        System.out.println();
        System.out.println("getCanonicalName:"  + cls.getCanonicalName());
        System.out.println();
        System.out.println("getSimpleName:" + cls.getSimpleName());
        System.out.println();
        System.out.println("getSuperclass:" + cls.getSuperclass());
        System.out.println();
        System.out.println("getPackage:" + cls.getPackage());

        System.out.println();
        for (Class c : cls.getInterfaces()){
            System.out.println("interface : " + c.getSimpleName());
        }

        System.out.println();
        for (TypeVariable<Class<BaseClassInfo>> typeVariable : cls.getTypeParameters()){
            System.out.println("type var : " + typeVariable.getTypeName());
        }

        System.out.println();
        System.out.println("getModifiers:" + Modifier.toString(cls.getModifiers()));
    }
}

输出结果为:

getName:com.example.reflectdemo.classdetail.BaseClassInfo

getCanonicalName:com.example.reflectdemo.classdetail.BaseClassInfo

getSimpleName:BaseClassInfo

getSuperclass:class com.example.reflectdemo.classdetail.Base

getPackage:package com.example.reflectdemo.classdetail

interface : Runnable
interface : Serializable

type var : T
type var : R

getModifiers:public final

Class类型判断,实例以下:

public class ClassTypeTest {

    public static void main(String... args){
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                printClassType(getClass());
            }
        };
        System.out.println("匿名内部类");
        runnable.run();

        class M implements Runnable{

            @Override
            public void run() {
                printClassType(getClass());
            }
        }

        System.out.println("方法内部类");
        new M().run();

        System.out.println("内部类");
        new ClassTypeTest().new T().run();

        System.out.println("静态内部类");
        new S().run();

        System.out.println("枚举");
        printClassType(EnumTest.class);


        System.out.println("接口");
        printClassType(Runnable.class);

        System.out.println("数组");
        printClassType(int[].class);

        System.out.println("int");
        printClassType(int.class);

        System.out.println("注解");
        printClassType(AnnTest.class);

    }


    class T implements Runnable{

        @Override
        public void run() {
            printClassType(getClass());
        }
    }

    static class S implements Runnable{

        @Override
        public void run() {
            printClassType(getClass());
        }
    }

    enum EnumTest{
        A, B, C
    }

    @interface AnnTest{

    }


    private static void printClassType(Class cls){
        System.out.println("Class:" + cls.getName());
        System.out.println("isAnonymousClass:" + cls.isAnonymousClass());
        System.out.println("isLocalClass:" + cls.isLocalClass());
        System.out.println("isMemberClass:" + cls.isMemberClass());
        System.out.println("isEnum:" + cls.isEnum());
        System.out.println("isInterface:" + cls.isInterface());
        System.out.println("isArray:" + cls.isArray());
        System.out.println("isPrimitive:" + cls.isPrimitive());
        System.out.println("isAnnotation:" + cls.isAnnotation());

        if (cls.isEnum()){
            System.out.println("getEnumConstants:");
            for (Object o : cls.getEnumConstants()){
                System.out.println(o);
            }
        }

        if (cls.isArray()){
            System.out.println("getComponentType:" + cls.getComponentType());
        }
        System.out.println();
    }
}

输出结果以下:

匿名内部类
Class:com.example.reflectdemo.classdetail.ClassTypeTest$1
isAnonymousClass:true
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

方法内部类
Class:com.example.reflectdemo.classdetail.ClassTypeTest$1M
isAnonymousClass:false
isLocalClass:true
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

内部类
Class:com.example.reflectdemo.classdetail.ClassTypeTest$T
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

静态内部类
Class:com.example.reflectdemo.classdetail.ClassTypeTest$S
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

枚举
Class:com.example.reflectdemo.classdetail.ClassTypeTest$EnumTest
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:true
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false
getEnumConstants:
A
B
C

接口
Class:java.lang.Runnable
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:true
isArray:false
isPrimitive:false
isAnnotation:false

数组
Class:[I
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:true
isPrimitive:false
isAnnotation:false
getComponentType:int

int
Class:int
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:true
isAnnotation:false

注解
Class:com.example.reflectdemo.classdetail.ClassTypeTest$AnnTest
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:true
isArray:false
isPrimitive:false
isAnnotation:true

内部类型样例以下:

public class InnerClassTest {

    public static void main(String... args){
        System.out.println("getClasses");
        for (Class cls : InnerClassTest.class.getClasses()){
            System.out.println(cls.getName());
        }
    }

    public interface I{

    }

    public class A implements I{

    }

    public class B implements I{

    }
}

输出结果以下:

getClasses
com.example.reflectdemo.classdetail.InnerClassTest$B
com.example.reflectdemo.classdetail.InnerClassTest$A
com.example.reflectdemo.classdetail.InnerClassTest$I

2.3.3. 对象实例化

对象实例化,主要经过Constructor实例完成,首先经过相关方法获取Constructor对象,而后进行实例化操做。

所涉及的方法以下:

方法 含义
newInstance 使用默认构造函数实例化对象
getConstructors 获取public构造函数
getConstructor(Class<?>... parameterTypes) 获取特定public构造函数
getDeclaredConstructors 获取全部的构造函数
getDeclaredConstructor 获取特定构造函数

实例化涉及的核心类为Constructor,Constructor继承自Executable,拥有AnnotatedElement、AccessibleObject、Executable等相关功能,其核心方法以下:

方法 含义
newInstance 调用构造函数,实例化对象

样例以下:

public class TestBean {
    private final Integer id;
    private final String name;

    public <T, R>TestBean(Integer id, String name) throws IllegalArgumentException, NotImplementedException {
        this.id = id;
        this.name = name;
    }

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

public class ConstructorTest {
    public static void main(String... args) throws Exception{
        for (Constructor constructor : TestBean.class.getConstructors()){
            TestBean bean = (TestBean) constructor.newInstance(1, "Test");
            System.out.println("newInstance:" + bean);
        }
    }
}

输出结果为:

newInstance:TestBean{id=1, name='Test'}

2.3.4. 属性信息

对象属性是类型中最主要的信息之一,主要经过Field表示,首先经过相关方法获取Field实例,而后进行属性值操做。

所涉及的方法以下:

方法 含义
getFields 获取public字段
getField(String name) 获取特定public字段
getDeclaredFields 获取全部的的属性
getDeclaredField 获取特定字段

Field继承自AccessibleObject实现Member接口,拥有AccessibleObject、AnnotatedElement、Member相关功能,其核心方法以下:

方法 含义
isEnumConstant 是否枚举常量
getType 获取类型
get 获取属性值
getBoolean 获取boolean值
getByte 获取byte值
getChar 获取chat值
getShort 获取short值
getInt 获取int值
getLong 获取long值
getFloat 获取float值
getDouble 获取double值
set 设置属性值
setBoolean 设置boolean值
setByte 设置byte值
setChar 设置char值
setShort 设置short值
setInt 设置int值
setLong 设置long值
setFloat 设置float值
setDouble 设置double值

实例以下:

public enum EnumTest {
    A
}

public class FieldBean {
    private EnumTest aEnum;
    private String aString;
    private boolean aBoolean;
    private byte aByte;
    private char aChar;
    private short aShort;
    private int anInt;
    private long aLong;
    private float aFloat;
    private double aDouble;

}


public class FieldTest {
    public static void main(String... args) throws NoSuchFieldException, IllegalAccessException {
        FieldBean fieldBean = new FieldBean();
        Field aEnum = getByName("aEnum");
        Field aString = getByName("aString");
        Field aBoolean = getByName("aBoolean");
        Field aByte = getByName("aByte");
        Field aChar = getByName("aChar");
        Field aShort = getByName("aShort");
        Field anInt = getByName("anInt");
        Field aLong = getByName("aLong");
        Field aFloat = getByName("aFloat");
        Field aDouble = getByName("aDouble");

        aEnum.set(fieldBean, EnumTest.A);
        System.out.println("isEnumConstant: " + aEnum.isEnumConstant());
        System.out.println("set and get enum : " + aEnum.get(fieldBean));

        aString.set(fieldBean, "Test");
        System.out.println("set and get String : " + aString.get(fieldBean));

        aBoolean.setBoolean(fieldBean, true);
        System.out.println("set and get Boolean : " + aBoolean.getBoolean(fieldBean));

        aByte.setByte(fieldBean, (byte) 1);
        System.out.println("set and get Byte : " + aByte.getByte(fieldBean));

        aChar.setChar(fieldBean, 'a');
        System.out.println("set and get Char : " + aChar.getChar(fieldBean));

        aShort.setShort(fieldBean, (short) 1);
        System.out.println("set and get Short : " + aShort.getShort(fieldBean));

        anInt.setInt(fieldBean, 1);
        System.out.println("set and get Int : " + anInt.getInt(fieldBean));

        aLong.setLong(fieldBean, 1L);
        System.out.println("set and get Long : " + aLong.getLong(fieldBean));

        aFloat.setFloat(fieldBean, 1f);
        System.out.println("set and get Float : " + aLong.getFloat(fieldBean));

        aDouble.setDouble(fieldBean, 1.1);
        System.out.println("set and get Double : " + aLong.getDouble(fieldBean));

    }

    private static Field getByName(String name) throws NoSuchFieldException {
        Field field = FieldBean.class.getDeclaredField(name);
        field.setAccessible(true);
        return field;
    }
}

2.3.5. 方法信息

类型中的方法经过Method表示,首先经过相关方法获取Method实现,而后经过反射执行方法。

所涉及的方法以下:

方法 含义
getMethods 获取public方法
getMethod(String name, Class<?>... parameterTypes) 获取特定public方法
getDeclaredMethods 获取全部方法
getDeclaredMethod 获取特定方法

Method继承自Executable,拥有AnnotatedElement、AccessibleObject、Executable等相关功能,其核心方法以下:

方法 含义
getReturnType 获取方法返回类型
invoke 调用方法
isBridge 是否为桥接方法。桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。咱们能够经过Method.isBridge()方法来判断一个方法是不是桥接方法。
isDefault 是否为默认方法

实例以下:

public interface SayHi {
    String get();

    default void hi(){
        System.out.println("Hi " + get());
    }
}
public class MethodBean implements Function<String, String>, SayHi {
    private final String name;

    public MethodBean(String name) {
        this.name = name;
    }

    @Override
    public String get() {
        return "Hi " + name;
    }

    @Override
    public String apply(String s) {
        return s + name;
    }
}
public class MethodTest {
    public static void main(String... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method strMethod = MethodBean.class.getDeclaredMethod("apply", String.class);
        Method objMethod = MethodBean.class.getDeclaredMethod("apply", Object.class);
        Method hiMethod = SayHi.class.getDeclaredMethod("hi");

        MethodBean methodBean = new MethodBean("张三");

        System.out.println("Return Type:");
        System.out.println("getMethod(String):" + strMethod.getReturnType());
        System.out.println("getMethod(Object):" + objMethod.getReturnType());
        System.out.println("hi():" + hiMethod.getReturnType());

        System.out.println();
        System.out.println("isBridge:");
        System.out.println("getMethod(String):" + strMethod.isBridge());
        System.out.println("getMethod(Object):" + objMethod.isBridge());
        System.out.println("hi():" + hiMethod.isBridge());


        System.out.println();
        System.out.println("isDefault:");
        System.out.println("getMethod(String):" + strMethod.isDefault());
        System.out.println("getMethod(Object):" + objMethod.isDefault());
        System.out.println("hi():" + hiMethod.isDefault());


        System.out.println();
        System.out.println("invoke:");
        System.out.println("invoke(String):" + strMethod.invoke(methodBean, "Test"));
        System.out.println("invoke(Object):" + objMethod.invoke(methodBean, "Test"));
        System.out.println("hi():" + hiMethod.invoke(methodBean));


    }
}

输出结果:

Return Type:
getMethod(String):class java.lang.String
getMethod(Object):class java.lang.Object
hi():void

isBridge:
getMethod(String):false
getMethod(Object):true
hi():false

isDefault:
getMethod(String):false
getMethod(Object):false
hi():true

invoke:
invoke(String):Test张三
invoke(Object):Test张三
Hi Hi 张三
hi():null

2.3.6. 其余

除上述核心方法外,Class对象提供了一些使用方法。

所涉及方法以下:

方法 含义
isInstance 判断某对象是不是该类的实例
isAssignableFrom 断定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是不是其超类或超接口。若是是则返回 true;不然返回 false。
getClassLoader 获取加载当前类的ClassLoader
getResourceAsStream 根据该ClassLoader加载资源
getResource 根据该ClassLoader加载资源
public class Task implements Runnable{
    @Override
    public void run() {

    }
}
public class OtherTest {
    public static void main(String...args){
        Task task = new Task();
        System.out.println("Runnable isInstance Task:" + Runnable.class.isInstance(task));
        System.out.println("Task isInstance Task:" + Task.class.isInstance(task));

        System.out.println("Task isAssignableFrom Task:" + Task.class.isAssignableFrom(Task.class));

        System.out.println("Runnable isAssignableFrom Task :" + Runnable.class.isAssignableFrom(Task.class));
    }
}

输出结果:

Runnable isInstance Task:true
Task isInstance Task:true
Task isAssignableFrom Task:true
Runnable isAssignableFrom Task :true

3. 动态代理

代理是基本的设计模式之一,它是咱们为了提供额外的或不一样的操做,而插入的用来代替“实际”对象的对象。这些操做一般与“实际”对象通讯,所以代理一般充当中间人的角色。

例如,咱们已有一个Handler接口,和一个实现类HandlerImpl,现须要对其进行性能统计,使用代理模式,代码以下:

/**
 * handler接口
 */
public interface Handler {
    /**
     * 数据处理
     * @param data
     */
    void handle(String data);
}

/**
 * Handler 实现
 */
public class HandlerImpl implements Handler{
    @Override
    public void handle(String data) {
        try {
            TimeUnit.MILLISECONDS.sleep(100);
            System.out.println(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * Handler代理<br />
 * 实现Handler接口,记录耗时状况,并将请求发送给目标对象
 */
public class HandlerProxy implements Handler{
    private final Handler handler;

    public HandlerProxy(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void handle(String data) {
        long start = System.currentTimeMillis();
        this.handler.handle(data);
        long end = System.currentTimeMillis();
        System.out.println("cost " + (end - start) + " ms");
    }
}

public static void main(String... args){
    Handler handler = new HandlerImpl();
    Handler proxy = new HandlerProxy(handler);
    proxy.handle("Test");
}

采用代理模式,比较优雅的解决了该问题,但若是Handler接口存在多个方法,而且须要对全部方法进行性能监控,那HandlerProxy的复杂性将会提升。
Java动态代理比代理更进一步,由于它能够动态的建立代理并动态的处理对所代理方法的调用。在动态代理上所作的全部调用都会被重定向到单一的调用处理器上。

3.1. InvocationHandler

InvocationHandler 是由动态代理处理器实现的接口,对代理对象的方法调用,会路由到该处理器上进行统一处理。

其只有一个核心方法:

/**
* proxy : 代理对象
* method : 调用方法
* args : 调用方法参数
**/
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

3.2. Proxy

Proxy 用于生成代理对象。

其核心方法为:

/**
* 获取代理类<br />
* loader : 类加载器
* interfaces: 类实现的接口
*
*/
Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces);
/*
* 生成代理对象<br />
* loader : 类加载器
* interfaces : 类实现的接口
* h : 动态代理回调
*/
Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h);

/*
* 判断是否为代理类<br />
* 
* cl : 待判断类
*/
public static boolean isProxyClass(Class<?> cl);

/*
* 获取代理对象的InvocationHandler <br />
*
* proxy : 代理对象
*/
InvocationHandler getInvocationHandler(Object proxy);

3.3. demo

对于以前的性能监控,使用Java动态代理怎么实现?
/**
 * 定义代理方法回调处理器
 */
public class CostInvocationHandler implements InvocationHandler {
    // 目标对象
    private final Object target;

    public CostInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("call method " + method + " ,args " + args);
        long start = System.currentTimeMillis();
        try {
            // 将请求转发给目标对象
            return method.invoke(this.target, args);
        }finally {
            long end = System.currentTimeMillis();
            System.out.println("cost " + (end - start) + "ms");
        }


    }
}
public static void main(String... args){
    Handler handler = new HandlerImpl();

    CostInvocationHandler invocationHandler = new CostInvocationHandler(handler);

    Class cls = Proxy.getProxyClass(DHandlerMain.class.getClassLoader(), Handler.class);

    Handler proxy = (Handler) Proxy.newProxyInstance(DHandlerMain.class.getClassLoader(),
            new Class[]{Handler.class},
            invocationHandler);

    System.out.println("invoke method");
    proxy.handle("Test");
    System.out.println("isProxyClass: " + Proxy.isProxyClass(cls));
    System.out.println("getInvocationHandler: " + (invocationHandler == Proxy.getInvocationHandler(proxy)));
}

4. 基于SPI的Plugin

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有很多框架用它来作服务的扩展发现,它是一种动态替换发现的机制。

具体用法是在JAR包的"META-INF/services/"目录下创建一个文件,文件名是接口的全限定名,文件的内容能够有多行,每行都是该接口对应的具体实现类的全限定名。而后使用 ServiceLoader.load(Interface.class) 对插件进行加载。

假定,现有个场景,须要对消息进行处理,但消息处理器的实现须要放开,及能够动态的对处理器进行加载,当有新消息到达时,依次调用处理器对消息进行处理,让咱们结合SPI和反射构造一个简单的Plugin系统。

首先咱们须要一个插件接口和若干个实现类:

/**
 * 插件接口
 */
public interface Handler {
    void handle(String msg);
}
/**
 * 实现1
 */
public class Handler1 implements Handler{
    @Override
    public void handle(String msg) {
        System.out.println("Handler1:" + msg);
    }
}
/**
 * 实现2
 */
public class Handler2 implements Handler{
    @Override
    public void handle(String msg) {
        System.out.println("Handler2:" + msg);
    }
}

而后,咱们添加SPI配置,及在META-INF/services/com.example.reflectdemo.plugin.Handler添加配置信息:

com.example.reflectdemo.plugin.Handler1
com.example.reflectdemo.plugin.Handler2

其次,咱们实现DispatcherInvocationHandler类继承自InvocationHandler接口,将方法调用分发给目标对象。

/**
 * 分发处理器<br />
 * 将请求挨个转发给目标对象
 */
public class DispatcherInvocationHandler implements InvocationHandler {
    // 目标对象集合
    private final List<Object> targets;

    public DispatcherInvocationHandler(List<Object> targets) {
        this.targets = targets;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        for (Object target : targets){
            // 将请求转发给目标对象
            method.invoke(target, args);
        }
        return null;
    }
}

实现主流程,经过SPI加装插件,将插件做为转发对象实例化DispatcherInvocationHandler,在经过Proxy构建动态代理对象,最后调用handle方法进行业务处理。

public static void main(String... args){
        // 使用SPI加载插件
        ServiceLoader<Handler> serviceLoader = ServiceLoader.load(Handler.class);
        List<Object> handlers = new ArrayList<>();
        Iterator<Handler> handlerIterator = serviceLoader.iterator();
        while (handlerIterator.hasNext()){
            Handler handler = handlerIterator.next();
            handlers.add(handler);
        }
        // 将加载的插件组装成InvocationHandler,以进行分发处理
        DispatcherInvocationHandler invocationHandler = new DispatcherInvocationHandler(handlers);
        // 生成代理对象
        Handler proxy = (Handler) Proxy.newProxyInstance(HandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler);
        // 调用handle方法
        proxy.handle("Test");
    }

运行结果以下:

Handler1:Test
Handler2:Test

5. 总结

Java类型系统、反射、动态代理,做为Java的高级应用,大量用于各大框架中。对其的掌握有助于加深对框架的理解。
相关文章
相关标签/搜索