【Java基础】java中的反射机制与动态代理

一、java中的反射机制

java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法。这种动态获取类的信息及动态调用类中方法的功能称为java的反射机制。

获取一个类的Class对象是应用反射机制的前提,获取Class对象的方式有如下三种:

  1. instance.getClass(),这个是Object类里面的方法

  2. Type.Class属性,任何的数据类型,基本数据类型或者抽象数据类型,都可以通过这种方式获取类

  3. Class.forName("类的全名"),Class类提供了这样一个方法,让我们通过类名来获取到对象类

1. 通过反射机制操作某个类的属性

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

2. 通过反射机制调用某个类的方法

package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 调用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 -> 张三
    }
    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);
    }
}

二、动态代理

代理的定义:将对真实对象的方法访问转换为对代理对象中同名方法的访问,为真实对象提供一种代理以控制对这个真实对象的访问。

动态代理和静态代理的区别:静态代理必须要为每个真实类定义一个代理类,而动态代理中无需定义代理类,由java反射包中的Proxy类根据传入的真实对象动态的创建代理对象或代理类。

在java的动态代理机制中,有一个类和接口至关重要,Proxy类用于动态创建代理类或代理对象,调度处理器接口InvocationHandler;每个动态代理对象必须与实现了InvocationHandler接口的实例关联,代理对象的方法调用最终都转换成此关联实例通过invoke 方法对真实对象的方法调用。

下面是实现动态代理的经典案例:

package net.xsoftlab.baike;

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

//定义真实类和代理类共同的接口
interface Subject {
    public void rent();

    public void hello(String str);
}

//定义真实类(被代理类)
class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("I want to rent my house");
    }

    @Override
    public void hello(String str) {
        System.out.println("hello: " + str);
    }
}

//定义继承了InvocationHandler接口的调度处理器
public class MyInvocationHandler implements InvocationHandler {
    //要代理的真实对象
    private Object realObj;

    // 构造方法给要代理的真实对象赋值
    public MyInvocationHandler(Object realObj) {
        this.realObj = realObj;
    }

    /**
     * 执行动态代理对象的所有方法都会被替换为执行如下的invoke方法
     * 其中:
     * autoProxy:动态代理对象
     * method:代表正在执行的方法
     * args:由动态代理对象调用方法时传入的实参
     */
    @Override
    public Object invoke(Object autoProxy, Method method, Object[] args) throws Throwable {
        //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent invoke");
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        System.out.println("Method:" + method);
        method.invoke(realObj, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent invoke");
        return null;
    }

}

class Client {
    public static void main(String[] args) {
        //要代理的真实对象
        Subject realObj = new RealSubject();
        //要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler MyHandler = new MyInvocationHandler(realObj);
        /*通过Proxy类的newProxyInstance方法动态创建代理对象
         *public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
         * 它的三个参数
         * 第一个参数 指定动态代理类的类加载器
         * 第二个参数 指定动态代理类需实现的接口集合
         * 第三个参数 指定与该动态代理对象所关联的调度处理器实例
         */
        Subject autoProxy = (Subject) Proxy.newProxyInstance(MyHandler.getClass().getClassLoader(), realObj.getClass().getInterfaces(), MyHandler);
        System.out.println(autoProxy.getClass().getName());
        autoProxy.rent();
        autoProxy.hello("world");
    }
}
View Code

2.1 动态代理和AOP

上面介绍了动态代理,接下来说下它的应用。在开发中如果需要对同一个功能模块进行复用,可以通过复制功能模块的代码来实现,但是在开发中一般情况下都会将这个功能包装为一个方法然后在需要使用的地方直接调用就可以了。

通过将公共模块组装成方法来调用虽然提高了复用减少了代码的冗余,但却提高了这三个代码段与方法的耦合。最理想的效果是:代码块一,代码块二,代码块三既可以执行蓝色区域的代码部分,又无需以硬编码的方式来直接调用蓝色区域的方法。这时就可以通过动态代理来实现这个功能。

接下来使用Proxy和InvocationHandler实现当程序调用info()或run()方法时,系统可以“自动”将method1()和method2()两个方法插入info()和run()方法中执行。
首先提供一个Dog接口,在该接口中提供两个方法:

猜你喜欢

转载自www.cnblogs.com/leiblog/p/10485543.html
今日推荐