Fork me on GitHub

反射与代理

  所谓反射,可以理解为在运行时获取对象类型信息的操作。传统的编程方法要求程序员在编译阶段就决定使用的类型,但是在反射机制的帮助下,编程人员可以在运行时动态地获取这些信息。

1 反射概述

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机领域关于应用反射性的研究。它首次被程序语言的设计领域所采用。

  在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述和监测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

1.1 Java中的反射机制

反射是Java程序开发语言的特征之一。它可以做的不仅仅是简单地列举类、字段以及方法,通过反射,还能够在需要时完成创建实例、调用方法 以及访问字段的工作。

归纳起来,Java反射机制主要提供了以下功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的的成员变量和方法
  • 在运行时调用任意一个对象的方法。通过反射甚至可以调用到private方法
  • 生成动态代理

1.2 Java反射API

Java反射所需要的类并不多,主要有java.lang.Class类和java.lang.reflect包下的Field、Constructor、Method、Array类,下面对这些类做一个简单的说明。

  • Class类:Class类的实例表示正在运行的Java应用程序中的类和接口
  • Field类:提供有关类和接口的属性的信息,以及对它的动态访问权限
  • Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限
  • Method类:提供关于类或接口上单独某个方法的信息
  • Array类:提供了动态创建数组和访问数组的静态方法。该类中的所有方法都是静态方法。

1.3 Class类

  Java程序在运行时,系统会对所有的对象进行所谓的运行时类型标识,用来保存这些类型信息的类就是Class类。Class类封装一个对象和接口运行时的状态。

  JVM为每种类型管理着一个独一无二的Class对象。也就是说,每个类都有一个Class对象。Java程序运行过程 中,当需要创建某个类的实例时,JVM首先检查所要加载的类对应的Class对象是否已经存在。如果还不存在,JVM就会根据类名查找对应的字节码文件并加载,接着创建对应的Class对象,最后才创建出这个类的实例。

  ps:Java基本数据类型(八种)和关键字void也对应一个Class对象,每个数组属性也被映射为Class对象,所有具有相同类型和维数的数组都共享该Clsaa对象。

  有三种方式获取Class对象。

  1. 调用Object类的getClass()方法获取。
  2. 利用Class类的forName(String name)静态方法获取,name必须是类或接口的全限定名
  3. 使用类型名.class获取

 1.4 创建对象

1,使用无参构造器创建对象

直接使用Class对象的newInstance()创建对象。

2,使用带参构造方法创建对象

分三步:获取指定类对应的Class对象;通过Class对象获取满足指定参数类型要求的Constructor对象;调用指定Constructor对象的newInstance方法,传入相应参数,创建对象。

package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestReflection {

    public static void main(String[] args) {
        //使用有参构造器创建对象
        Human person = new Human();
        Class<? extends Human>  c1 = person.getClass();
        try {
            Constructor<? extends Human> cons = c1.getDeclaredConstructor(int.class);
            System.out.println(cons.newInstance(11).toString());
        } catch (SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException e1) {
            e1.printStackTrace();
        } 
        
        //无参构造器创建对象
        Human anotherPerson = new Woman();
        Class<? extends Human> c2 = anotherPerson.getClass();
        System.out.println(c2.getName());
        try {
            //    得到实例
            System.out.println(c2.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}
class Human{
    private int height;
    Human(){
        
    }
     Human(int height){
        this.height = height;
    }
    
    public int getHeight(){
        return this.height;
    }
    @Override 
    public String toString(){
        return "human";
    }
}
class Woman extends Human{

    @Override
    public String toString() {
        return "Woman";
    }    
}

1.5 调用方法(Method对象的invoke方法,直接举例)

package reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestReflection {

    public static void main(String[] args){
                try {
            //获取Class对象
            Class<?>  c1 = Class.forName("reflection.Human");
            //获取实例对象
            Human h = (Human) c1.newInstance();
            //得到方法
            Method method = c1.getDeclaredMethod("setHeight", int.class);
            //获取访问私有方法的权限
            method.setAccessible(true);
            //调用方法
            method.invoke(h, 12);
            //测试方法是否调用成功
            System.out.println(h.getHeight());
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e1) {
            e1.printStackTrace();
        } 
    }

}
class Human{
    private int height;

    private void setHeight(int height){
        this.height = height;
    }
    public int getHeight(){
        return this.height;
    }
    @Override 
    public String toString(){
        return "human";
    }
}

1.6 访问变量

  使用反射可以取得类的成员变量的对象代表,成员变量的对象代表是java.lang.reflect.Field类的实例,可以使用它的getXXX方法来获取指定对象上的值,也可以调用它的setXXX方法来动态修改指定对象的值。

对象类:

package reflection;

public class Picture {
    private String name;
    private String path;
    Picture(String name,String path){
        this.name = name;
        this.path = path;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPath() {
        return path;
    }
    public void setPath(String path) {
        this.path = path;
    }
}

修改字段值:

package reflection;
import java.lang.reflect.Field;

public class Reflection {
    public static void main(String[] args){
        
        Picture p = new Picture("mine","d://picture");
        //获得Class
        Class<? extends Picture> pClass = p.getClass();
        try{
            //获取变量
            Field name = pClass.getDeclaredField("name");
            System.out.println(p.getName());
            //设置变量值
            name.setAccessible(true);
            name.set(p, "change");
            System.out.println(p.getName());
        }catch( Exception e){
        }
    }
}

1.7 利用反射操作数组

写一个数组长度扩充的静态方法:

 

package test;
import java.lang.reflect.Array;

public class Test{
    
    public static void main(String[] tags){
        int[] array = {1,2};
        for(Object o : (int[])arrayGrow(array)){
            System.out.println(o);
        }
    }
    //数组扩充,适合于所有数组
    public static Object arrayGrow(Object array){
        
        Class<? extends Object> c = array.getClass();
        if(!c.isArray())
            return null;
        //the Class representing the component type of this class if this class is an array
        //返回数组的组件类型的类,例如int[]返回int。
        Class<?> className = c.getComponentType();
        //原数组长度
        int length = Array.getLength(array);
        //新数组长度
        int newLength = 2 * length;
        //获得数组的实例
        Object returnArray = Array.newInstance(className,newLength);
        //复制
        System.arraycopy(array, 0, returnArray, 0, length);
        return returnArray;
    }
}

 

结果:

需要注意的是:要适合于所有的数组,形参类型和返回的类型都必须是Object,因为Object是一切类型的父类。而Object[]并不是int[]的父类。

2 代理

代理模式:为其他对象提供一种代理以控制对这个对象的访问。为什么需要代理?比如,有客户需要找一个厂家做衣服,但是客户找不到合适的厂家,于是通过一个中介公司,由总结公司帮他找厂家做这些衣服。

2.1 静态代理

  1. Subject抽象主体角色,可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
  2. RealSubject具体主题角色,也叫作被委托角色、被代理角色,是业务逻辑的具体执行者。
  3. Proxy代理主体角色,也叫作委托类、代理类,负责对真实角色的应用,把所有抽象主体类定义的方法限制委托给真实主体角色实现,并且在真实主题角色完毕前后做预处理和善后处理工作。

抽象主体:

package dynamicProxy.subject;

public interface ISubject {
    void doSomething();
}

真正主体:

package dynamicProxy.subject;

public class RealSubject implements ISubject {

    @Override
    public void doSomething() {
        System.out.println("do something");
    }
}

代理类:

package dynamicProxy.proxy;

import dynamicProxy.subject.ISubject;

public class StaticProxy implements ISubject {
    
    private ISubject subject;
    
    public StaticProxy(ISubject subject){
        this.subject = subject;
    }
    
    @Override
    public void doSomething() {
        subject.doSomething();
    }
}

测试类:

public class Test {

    public static void main(String[] args) {
        
        StaticProxy p = new StaticProxy(new RealSubject());
        p.doSomething();
    }

}

结果:

这样实现了对真实主体的代理,使用的是静态代理模式,它的特征是代理类和目标对象的类都是在编译期间就已经确定下来的,不利于程序的扩展。

2.2 虚拟代理

  虚拟代理听着很复杂,实际上很简单,上面的静态代理的代理类,在创建代理类时调用其构造函数,参数传入主体对象,这时确定代理类代理的某个具体主体。

虚拟代理如下:

package dynamicProxy.proxy;

import dynamicProxy.subject.ISubject;
import dynamicProxy.subject.RealSubject;

public class StaticProxy implements ISubject {
    
    private ISubject subject;
    
    @Override
    public void doSomething() {
        if(subject == null)
            subject = new RealSubject();
        subject.doSomething();
    }
}

它在需要时才初始化主体对象,可以避免被代理对象过多时引起的初始化缓慢问题,意思就是,现在只实现了ISubject,还有实现ISubject1、ISubject2、ISubject3 ...... 的情况。如果都作为参数在构造函数里面,会初始化缓慢。(我觉得可以写重载构造函数的方式来解决)

2.3 动态代理

动态代理的原理是,在程序运行时根据需要动态创建目标类的代理对象。

 

java中动态代理主要有JDK和CGLIB两种方式区别主要是jdk是代理接口,而cglib是代理类。

 

jdk的动态代理调用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法。

通过该方法生成字节码,动态的创建了一个代理类,interfaces参数是该动态类所继承的所有接口,而继承InvocationHandler 接口的类则是实现在调用代理接口方法前后的具体逻辑。

动态代理必须要介绍InvocationHandler接口,代理类的处理类必须实现这个接口,接口中只有一个方法:

public Object invoke(Object proxy, Method method, Object[] args)

第一个参数proxy指代理类,第二个参数method是指被代理的方法的class对象,第三个参数args为传给该方法的参数值数组。实际上,你可以在方法前后实现某些操作。

 

两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。(我借用图并修改了一下,上面的×的线不对,我是在MyInvocationHandler中加入了前后消息通知advice)

整个动态代理的目录结构

其中的subject下的代码在静态代理中已经有了,就不在贴上来了。

代理的委托处理类:

package dynamicProxy.handle;

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

import dynamicProxy.notify.Notify;

public class MyInvocationHandler implements InvocationHandler {
    //被代理的对象
    private Object target = null;
    //构造函数
    public MyInvocationHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //执行前置通知
        if(true){
            new Notify().beforeNotify();;
        }
        //执行被代理的方法
        Object object = method.invoke(this.target, args);
        //执行后置通知
        if(true){
            new Notify().afterNotify();;
        }
        return object;
    }
}

 通知接口:

package dynamicProxy.notify;

public interface INotify {
    void beforeNotify();
    void afterNotify();
}

通知的实现类:

package dynamicProxy.notify;

public class Notify implements INotify {

    @Override
    public void beforeNotify() {
        System.out.println("before doSomething");
    }

    @Override
    public void afterNotify() {
        System.out.println("after doSomething");
    }
}

动态代理类:(实际上,前置通知和后置通知也可以写在此类中,它只在创建代理对象时运行,而写在MyInvocationHandler 里,可以在调用每个方法时调用)

package dynamicProxy.proxy;

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

import dynamicProxy.handle.MyInvocationHandler;

public class DynamicProxy<T>{
    //传入一个主题对象,返回代理对象
    public static<T> T newProxyInstance(Object object){
        ClassLoader classLoader = object.getClass().getClassLoader();
        Class<?>[] cArray = object.getClass().getInterfaces();
        InvocationHandler h = new MyInvocationHandler(object);
        return (T) Proxy.newProxyInstance(classLoader, cArray, h);
    }
}

测试类:

package dynamicProxy;

import dynamicProxy.proxy.DynamicProxy;
import dynamicProxy.subject.ISubject;
import dynamicProxy.subject.RealSubject;

public class Test {

    public static void main(String[] args) {
        //定义一个主题
        ISubject work = new RealSubject();
        //定义主体的代理
        ISubject proxy = DynamicProxy.newProxyInstance(work);
        //代理的行为
        proxy.doSomething();
    }
}

结果:

加一句:下面的那本设计模式之禅是本好书,很值得一读!

 参考书籍:《Java基础和案例开发详解》

      《Java核心技术第1卷-基础知识》

     《设计模式之禅》

posted @ 2018-07-21 16:24  爱跑步的星仔  阅读(186)  评论(0编辑  收藏  举报