反射

反射

反射的定义

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

为什么使用反射

  1. 反射被广泛应用在那些需要在运行时检测或修改程序行为得程序中,例如Spring AOP
  2. 使用反射加载配置文件,降低耦合,例如工厂模式中读取配置文件、Spring容器依赖注入
  3. 反射可以在编译时使用本地不存在的class文件,完成代码编译,在运行时获取实际class文件例如项目各个部分并行编写代码,各部分之间只需要暴露接口即
  4. 反射使用动态编译创建对象,具有很大的灵活性,在java EE开发中需要更新某些功能时,可以在运行时动态创建,而不需要卸载以前的版本重新安装新版本

反射的缺点

  1. 反射包括了一些动态类型,JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作的效率低得多,应该避免在经常被执行的代码或对性能要求很高的程序中使用反射
  2. 反射要求程序必须在一个没有安全限制的环境中运行
  3. 反射允许执行一些在正常情况下不被允许的操作,例如访问私有属性和方法,所以使用反射有可能导致副作——代码有功能上的错误,降低可移植性。反射破坏了封装性,当平台发生改变时,代码的行为就有可能也随着变化(持保留意见)

java中的反射

反射是java被视为(但不是)动态(准动态)语言的一个关键性质,反射机制允许程序在运行时加载、探知、使用编译期完全未知的class,即java可以加载一个在运行时才得知的class,获得其完整结构。java中的Class与java.lang.reflect一起构成了反射的API基础。

反射的应用

代理模式

代理模式中分为代理类和被代理类,代理类除了调用被代理类的方法完成最主要的功能之外,还有其他一些额外的功能。
代理模式分为抽象主题、真实主题和代理主题三类,抽象主题是一个接口,真实主题是被代理类且实现了抽象主题,代理主题是代理类且也实现了抽象主题。

interface Subject { //抽象主题
    void browse();
}

class RealSubject implements Subject { //真实主题
    @Override
    public void browse() {
        System.out.println("上网操作...");
    }
}

class Proxy implements Subject { //代理类
    private Subject subject;

    public Proxy(Subject subject) { //传入被代理对象
        this.subject = subject;
    }

    @Override
    public void browse() {
        System.out.println("额外的代理操作1...");
        this.subject.browse();
        System.out.println("额外的代理操作2...");
    }
}

public class Test {
    public static void main(String[] args) {
        Subject subject = new Proxy(new RealSubject()); //使用代理类即可
        subject.browse();
    }
}

上述代码是静态代理,所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。
静态代理优点:被代理类只需要关心业务逻辑即可,而其他额外的操作由代理类去实现。思想就是Spring中的切面思想,但AOP就是动态代理的实际应用。
缺点:如果抽象主题增加一个方法,被代理类需要实现此方法,且所有的代理类也需要实现此方法。
但是可以更灵活地为每个需要被代理的方法进行灵活配置不同的代理操作。而动态代理中如果有多个需要被代理的方法,则不能配置不同的代理操作,所有被代理的方法都是同样的代理操作。(重新考虑这句话的意思)

interface Subject {
    void browse();

    void doSomething();
}

class RealSubject implements Subject {
    @Override
    public void browse() {
        System.out.println("上网操作...");
    }

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

class Proxy implements Subject {
    private Subject subject;

    public Proxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void browse() {
        System.out.println("额外的代理操作1...");
        this.subject.browse();
        System.out.println("额外的代理操作2...");
    }

    @Override
    public void doSomething() {
        System.out.println("额外的代理操作3...");
        this.subject.doSomething();
        System.out.println("额外的代理操作4...");
    }
}

public class Test {
    public static void main(String[] args) {
        Subject subject = new Proxy(new RealSubject());
        subject.browse();
        subject.doSomething();
    }
}

动态代理类的字节码文件是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的源码文件。代理类和被代理类(委托类)的关系是在程序运行时确定。
动态代理,如果需要代理抽象主题中的多个方法,那么InvocationHandler实现类中的invoke方法中的额外代理操作需要调用的是哪个方法的判断逻辑,以便为不同的方法实现不同的代理逻辑。例如下面的示例:

public Object invoke(Object proxy, Method method, Object[] args) {
    if ("method1Name".equals(method.getName())) {
        System.out.println("代理逻辑1...");
        method.invoke(obj, args);
        System.out.println("代理逻辑2...");
    } else if ("method2Name".equals(method.getName())) {
        System.out.println("代理逻辑3...");
        method.invoke(obj, args);
        System.out.println("代理逻辑4...");
    } else if () {

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

interface Subject{
    void doSomething();

    void doAnotherthing();
}

class RealSubject implements Subject{
    @Override
    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }

    @Override
    public void doAnotherthing() {
        System.out.println("RealSubject doAnotherthing");
    }
}

class InvocationHandlerImpl implements InvocationHandler {
    private Object obj;

    public InvocationHandlerImpl(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("同样的代理操作1...");
        method.invoke(obj, args);
        System.out.println("同样的代理操作2...");
        return null;
    }
}

public class TestJDKDynamicProxy {

    public static void main(String[] args) {
        Subject realsubject = new RealSubject();
        Subject subject = (Subject) Proxy.newProxyInstance(realsubject.getClass().getClassLoader(), realsubject.getClass()
            .getInterfaces(), new InvocationHandlerImpl(realsubject));
        subject.doSomething();
        subject.doAnotherthing();
    }
}
输出为:
同样的代理操作1...
RealSubject doSomething
同样的代理操作2...
同样的代理操作1...
RealSubject doAnotherthing
同样的代理操作2...

Proxy类的newProxyInstance方法会动态地帮助我们生成一个代理类,然后编译。查看其源码,可以知道是这行Class<?> cl = getProxyClass0(loader, intfs); 生成字节码,而getProxyClass0方法显示是从proxyClassCache中取得的,而proxyClassCache又是通过ProxyClassFactory生成的。ProxyClassFactory中的这行byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
表明是使用的ProxyGenerator类的generateProxyClass方法。ProxyGenerator类来自root.jar/sun/misc下,然后可进一步查看generateProxyClass方法。
下面给出一个模拟动态代理类生成的代码示例:

package test;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Created by WangSong on 2017/9/13 10:29.
 * 手动模拟动态代理类的生成
 */
public class TestSimulateProxyGeneration {
    //第一个参数是被代理类实现的接口的Class对象,用于获取该接口的方法信息和名字等
    //第二个参数是MyInvocationHandler接口的实例,里面有相关的代理逻辑
    public static Object newProxyInstance(Class<?> interfaceClass, MyInvocationHandler handler) throws Exception{
        //最简单的方式是手动生成一个.java文件,然后再编译。更直接的方式是直接生成.class文件
        StringBuilder sb = new StringBuilder();
        //参照一个静态代理类的实现方式,来实现一个动态的生成,整体就是字符串的拼接
        //package test; //这是包名,暂时无需理会
        //
        这里的Subject在动态代理下应该可以为任何接口,所以这里需要用接口的Class类对象来取得接口名字然后再进行拼接
        另外这里的StaticProxy也应该不能是固定的,需要根据生成的动态代理类变化,不过也暂时无需理会
        //class StaticProxy implements Subject { //改动1.Subject需要改为拼接的方式
        //    private Object obj; //改动2. MyInvocationHandler实例
        //    //传入一个真实主题实例。但是在动态代理中,代理逻辑和业务逻辑是在MyInvocationHandler的实现类中完成的,所以需要传入
        //    //MyInvocationHandler接口的实例,对应方法的第二个参数
        //    public StaticProxy(Object obj) { //改动3. 传入MyInvocationHandler接口的实例
        //        this.obj = obj;
        //    }
        //    //方法体中对应的是代理逻辑和真实主题业务逻辑,而这些在动态代理中需要在MyInvocationHandler接口的实现类中完成
        //    //所以需要传入MyInvocationHandler的实现类实例
        //    public void a() { //改动4. 所有的方法都通过被代理类实现的接口的Class对象来取得,方法内逻辑通过传入的MyInvocationHandler接口的实例实现
        //        System.out.println("对应a方法的代理逻辑1...");
        //        obj.a();
        //        System.out.println("对应a方法的代理逻辑2...");
        //    }
        //    public int b() {
        //        System.out.println("对应b方法的代理逻辑1...");
        //        obj.b();
        //        System.out.println("对应b方法的代理逻辑2...");
        //        return 1;
        //    }
        //    public String c() {
        //        System.out.println("对应c方法的代理逻辑1...");
        //        obj.c();
        //        System.out.println("对应c方法的代理逻辑2...");
        //        return null;
        //    }
        //根据上面的静态代理类的实现以及分析动态代理中需要改动的地方,下面可以手动写一个动态代理类了
        sb.append("package test;\n\n");
        sb.append("import java.lang.reflect.*;\n\n");
        sb.append("class $Proxy1 implements " + interfaceClass.getSimpleName() + " {\n");
        sb.append("\tprivate MyInvocationHandler handler;\n");
        sb.append("\tpublic $Proxy1(MyInvocationHandler handler) {\n");
        sb.append("\t\tthis.handler = handler;\n");
        sb.append("\t}\n");
        Method[] methods = interfaceClass.getMethods();
        //这个实例简单起见没有考虑方法有参数的情况,通过interfaceClass获取每个方法的Method对象,传递给MyInvocationHandler的invoke使用
        for(Method m: methods) {
            sb.append("\tpublic " + m.getReturnType().getSimpleName() + " " + m.getName() + "() throws Throwable{\n");
            sb.append("\t\tMethod method = " + interfaceClass.getName() + ".class.getMethod(\"" + m.getName() + "\", new Class<?>[]{});\n");
            if ("void".equals(m.getReturnType().getSimpleName())) {
                sb.append("\t\tthis.handler.invoke(this, method);\n");
            } else {
                sb.append("\t\t" + m.getReturnType().getSimpleName() + " temp = (" + m.getReturnType().getSimpleName() + ") this.handler.invoke(this, method);\n");
                sb.append("\t\treturn temp;\n");
            }
            sb.append("\t}\n");
        }
        sb.append("}\n");
        //生成$Proxy1.java文件
        String fileDir = System.getProperty("user.dir");
        String fileName = fileDir + "\\src\\test\\$Proxy1.java";
        File file = new File(fileName);
        Writer writer = new FileWriter(file);
        writer.write(sb.toString());
        writer.close();

        //编译$Proxy1.java,生成$Proxy1.class文件,使用JavaCompiler和相关的类、接口
        //获取系统编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //获取标准文件管理器
        StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);
        //获取文件列表
        Iterable<? extends JavaFileObject> iter = sjfm.getJavaFileObjects(fileName);
        //创建编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, sjfm, null, null, null, iter);
        //执行编译
        task.call();
        sjfm.close();

        //加载$Proxy1.class到jvm中去,使用URLClassLoader
        //URL前需要加file:/
        URL[] urls = new URL[]{new URL("file:\\" + System.getProperty("user.dir") + "\\src")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        //proxyClass是代理类的Class类对象
        Class<?> proxyCLass = urlClassLoader.loadClass("test.$Proxy1");

        //完成加载后,根据代理类的Class类对象获取构造器对象,利用构造器对象传入MyInvocationHandler实例创造代理类实例
        Constructor<?> constructor = proxyCLass.getDeclaredConstructor(MyInvocationHandler.class);
        Object obj = constructor.newInstance(handler);
        return obj;
    }

    public static void main(String[] args) throws Throwable{
        //测试上面模拟过程是否正确,如果出现ClassNotFoundException,再run一次就可以
        Subject realSubject = new RealSubject(); //真实主题实例
        MyInvocationHandler handler = new MyInvocationHandlerImpl(realSubject); //代理逻辑的实现类,需要传入真实主题实例
        Subject proxy = (Subject) newProxyInstance(Subject.class, handler); //代理类实例
        proxy.a();
        proxy.b();
        proxy.c();
    }

}

//抽象主题
interface Subject{
    void a() throws Throwable;
    int b() throws Throwable;
    String c() throws Throwable;
}

//真是主题
class RealSubject implements Subject {
    @Override
    public void a() throws Throwable{
        System.out.println("a(): void");
    }

    @Override
    public int b() throws Throwable{
        System.out.println("b(): int");
        return 0;
    }

    @Override
    public String c() throws Throwable{
        System.out.println("c(): String");
        return null;
    }
}

//代理类的操作在此接口的实现类中定义
interface MyInvocationHandler {
    //method参数是被代理类的Method对象,使用方式:method.invoke(Object obj, Object... args)
    //第一个参数是从中调用底层方法的对象,即被代理类的对象,第二个参数是方法参数
    Object invoke(Object proxy, Method method) throws Throwable;
}

class MyInvocationHandlerImpl implements MyInvocationHandler {
    private Object obj; //因为MyInvocationHandler的invoke方法中的参数method需要调用被代理类的
    //对象,所以该实现类需要传入被代理类的对象,用obj保存
    public MyInvocationHandlerImpl(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method) throws Throwable {
        if ("a".equals(method.getName())) { //如果是代理的a方法,则对应a方法的代理逻辑
            System.out.println("对应a方法的代理逻辑1...");
            method.invoke(this.obj, new Object[]{}); //obj是被代理对象
            System.out.println("对应a方法的代理逻辑2...");
            return null;
        } else if ("b".equals(method.getName())) {
            System.out.println("对应b方法的代理逻辑1...");
            int temp = (int) method.invoke(this.obj, new Object[]{});
            System.out.println("对应b方法的代理逻辑2...");
            return temp;
        } else {
            System.out.println("对应c方法的代理逻辑1...");
            String temp = (String) method.invoke(this.obj, new Object[]{}); //方法参数为空,传递一个空数组即可
            System.out.println("对应c方法的代理逻辑2...");
            return temp;
        }
    }
}

获取Proxy代理类的字节码。自动生成的代理类是在内存中,需要通过sun.misc.ProxyGenerator类来获取字节码,这个类在rt.jar里面,不在API文档里面,是因为sun开头的包不属于JavaSE规范,是Sun公司的内部实现。

package test;
import java.io.File;
import java.io.FileOutputStream;

import sun.misc.ProxyGenerator;

public class Test {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        FileOutputStream out = new FileOutputStream(new File("c:/users/administrator/desktop/Proxy$0.class"));//把代理类的字节码放在桌面上
        byte[] data = ProxyGenerator.generateProxyClass("Proxy$0", new Class[] {Subject.class});//第一个参数为代理类的名字
        out.write(data);
        out.close();
    }

}

interface Subject {
    public void a(String x, int y);
}

Proxy$0.class反编译后为:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import test.Subject;

public final class Proxy$0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public final boolean equals(Object paramObject)
    throws //比如这里,这是什么意思
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

    public final String toString()
    throws 
  {
    try
    {
      return ((String)this.h.invoke(this, m2, null));
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

    public final void a(String paramString, int paramInt)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) });//这行代码是关键所在
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

    public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString",
                    new Class[0]);
            m3 = Class.forName("test.Subject").getMethod("a", new Class[]{
                    Class.forName("java.lang.String"), Integer.TYPE});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                    new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(
                    localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(
                    localClassNotFoundException.getMessage());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值