动态代理 + 反射

本文介绍了Java中动态代理的概念,如何使用`Proxy.newProxyInstance`创建无侵入式的代理对象,以及通过`InvocationHandler`控制代理行为。涉及反射API如`Class.forName`、`getDeclaredConstructor`和`getDeclaredMethod`的使用实例。
摘要由CSDN通过智能技术生成

动态代理

特点:
无侵入式的给代码增加额外的功能。
在这里插入图片描述
interface Star

package proxy;
/**
 * 在接口中 定义 想要 被代理 的方法
 */
public interface Star {
    //唱歌
    public abstract String sing(String name);

    //跳舞
    public abstract void song();
}

class BigStar

package proxy;
/**
 * JavaBean 要实现 代理的 接口
 */
public class BigStar implements Star{
    private String name;

    public BigStar(String name) {
        this.name = name;
    }
    
    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    //跳舞
    @Override
    public void song(){
        System.out.println(this.name + "正在跳舞");
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

class ProxyUtil

package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 创建一个 代理
 */
public class ProxyUtil {
    /**
     * 方法作用:给一个明星对象, 创建一个代理
     * @param bigStar 被代理的明星对象 JavaBean
     * @return 给 JavaBean 创建的代理
     */
    public static Star createProxy(BigStar bigStar){
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                         * Object proxy  参数1:代理的对象 (一般用不到)
                         * Method method 参数2:要运行的方法
                         * Object[] args 参数3:调用方法时,传递的实参
                         */
                        if ("sing".equals(method.getName())) {
                            System.out.println("准备话筒");
                        } else if ("song".equals(method.getName())) {
                            System.out.println("准备场地");
                        }

                        //代码形式:调用需要 被代理的对象(JavaBean) 的方法
                        return method.invoke(bigStar, args);
                    }
                }
        );
        return star;
    }
}

class Test

package proxy;
public class Test {
    public static void main(String[] args) {
        /**
         *  1. 获取代理的对象
         *  2. 调用代理的(唱歌/跳舞)方法
         */

        //获取代理的对象
        BigStar bigStar = new BigStar("G.E.M");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //调用代理的(唱歌)方法
        String singName = proxy.sing("倒数");
        System.out.println(singName);

        //调用代理的(跳舞)方法
        proxy.song();
    }
}

运行结果

准备话筒
G.E.M正在唱倒数
谢谢
准备场地
G.E.M正在跳舞

提问:

  • Java提供了什么API帮我们创建代理?
  • newProxyInstance方法在创建代理时,需要接收几个参数,每个参数的含义是是什么?
  • 通过invokehandlerinvoke方法指定代理干的事,这个invoke会被谁调用?要接收那几个参数?

反射

反射允许对成员变量,成员方法和构造方法的信息进行编程访问

  • 获取class对象的三种方式:
package reflection.demo1;
public class reflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
         * 获取class对象的三种方式:
         * 1. Class.forName("全类名"),即相对路径
         * 2. 类名.class
         * 3. 对象.getClass()
         */
        //1. 最为常用
        Class clazz1 = Class.forName("reflection.demo1.Student");
        //2. 更多的是当作参数进行传递
        Class clazz2 = Student.class;
        //3. 前提条件,需要有对象
        Student student = new Student("张三", 18);
        Class clazz3 = student.getClass();

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}
  • 利用反射获取构造方法
    在这里插入图片描述
    JavaBean中的某个私有构造方法:
public class Student {
    private String name;
    private int age;
    public Student(){
    }
    public Student(String name) {
        this.name = name;
    }
    protected Student(int age){
        this.age = age;
    }
    private Student(String name, int age){
        this.name = name;
        this.age = age;
    }
}

reflectDemo2测试:

package reflection.demo2;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class reflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1. 获取 class 字节码文件对象
        Class clazz = Class.forName("reflection.demo2.Student");
        //2. 获取构造方法,我们以Constructor类中用于创建对象的方法 讲解
        Constructor con1 = clazz.getDeclaredConstructor(String.class, int.class);
//        System.out.println(con1);
//        int modifiers = con1.getModifiers();
//        System.out.println("con1对应的构造方法的修饰属性是:" + modifiers); // 2

        //暴力反射:临时取消权限校验
        con1.setAccessible(true);
        //使用Constructor类用于创建对象
        Student stu = (Student) con1.newInstance("张三", 18);
        System.out.println(stu);
    }
}
  • 利用反射获取成员变量
    在这里插入图片描述
    JavaBean中的成员变量:
public class Student {
    private String name;
    private int age;
    public String gender;
}
package reflection.demo3;
import java.lang.reflect.Field;
public class reflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //获取 class 字节码文件对象
        Class clazz = Class.forName("reflection.demo3.Student");
        //获取 成员变量(选择单个私有成员变量讲解)
        Field name = clazz.getDeclaredField("name");
        System.out.println(name); // private java.lang.String reflection.demo3.Student.name

        //获取成员变量的 数据类型
        Class<?> type = name.getType();
        System.out.println(type); // class java.lang.String

        //获取成员变量记录的 值
        Student student = new Student("张三", 18, "男");
        //因为 name 属性是私有的,所以需要取消临时校验
        name.setAccessible(true);
        Object nameValue = name.get(student);
        System.out.println(nameValue); // 张三

        //修改对象里面记录的 值
        name.set(student, "李四");
        System.out.println(student); // Student{name='李四', age=18, gender='男'}
    }
}
  • 利用反射获取成员方法:
    在这里插入图片描述
    JavaBean中的成员方法:
public class Student {
    public void sleep(){
        System.out.println("睡觉");
    }
    private void eat(String something) {
        System.out.println("在吃" + something);
    }
}

reflectDemo4中的测试:

package reflection.demo4;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //获取 class 字节码 文件对象
        Class clazz = Class.forName("reflection.demo4.Student");
        //获取 成员方法
        //1. 获取所有的公共方法对象 (包含父类中的..)
//        Method[] methods = clazz.getMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
        //2. 获取所有的成员方法对象(不能获取父类的..)
//        Method[] declaredMethods = clazz.getDeclaredMethods();
//        for (Method declaredMethod : declaredMethods) {
//            System.out.println(declaredMethod);
//        }

        //方法运行(获取私有的单个成员方法作为讲解)
        Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
        Student student = new Student();
        eatMethod.setAccessible(true);
        /**
         * 参数一:表示方法的调用者(即,JavaBean)
         * 参数二:表示在调用方法的时候传递的实际参数(即,JavaBean中,方法需要的实际参数)
         */
        eatMethod.invoke(student, "汉堡包");
    }
}
  • 练习
    在这里插入图片描述
  1. 对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去(此时需要用到运行阶段的方式获取反射)。
    假设有两个JavaBean对象:TeacherStudent
    reflectDemo5中的测试:
package reflection.demo5;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
public class reflectDemo5 {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        Student student = new Student("张三", 23, "男", "睡觉");
        Teacher teacher = new Teacher("李四", "1000");

        saveObject(teacher);
    }
    //把对象里面所有的 成员变量名 和 值 保存到本地文件中
    private static void saveObject(Object object) throws IllegalAccessException, IOException {
        //1. 获取字节码文件的对象
        Class clazz = object.getClass();

        //2. 创建IO流  (一定要注意路径名:项目的相对路径)
        BufferedWriter bw = new BufferedWriter(new FileWriter("proxy\\src\\main\\java\\reflection\\demo5\\a.txt"));

        //3. 获取所有成员变量
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            //获取成员变量的名字
            String fieldName = declaredField.getName();
            //获取成员变量的值
            Object value = declaredField.get(object);
            bw.write(fieldName + "=" + value);
            bw.newLine();
        }
        bw.close();
    }
}
  1. 反射可以跟配置文件结合的方式,动态的创建对象,并调用方法。(优点:在测试时,不需要改测试代码,直接修改配置文件的信息,即可动态测试不同的对象所对应的方法)
    prop.properties
classname=reflection.demo6.Teacher
method=teach

两个JavaBean对象:Student 和 Teacher

public class Student {
    public Student() {
    }
    public void study() {
        System.out.println("学生正在学习");
    }
}

public class Teacher {
    public Teacher() {
    }
    public void teach() {
        System.out.println("老师正在教书");
    }
}

reflectDemo6中的测试:

package reflection.demo6;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
 * 测试:使用配置文件
 */
public class reflectDemo6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1. 读取配置文件中的信息,Properties类读取配置文件时需要 输入流
        Properties pro = new Properties();
        FileInputStream fis = new FileInputStream("proxy\\prop.properties");
        pro.load(fis);
        fis.close();
//        System.out.println(pro); // {classname=reflection.demo6.Student, method=study}

        //2. 获取全类名和方法名
        String className = (String) pro.get("classname");
        String methodName = (String) pro.get("method");

        //3. 利用 反射 创建对象 并 运行对象
        Class clazz = Class.forName(className);
        // 获取构造方法(用来实例化对象)
        Constructor con = clazz.getDeclaredConstructor();
        Object object = con.newInstance();

        // 获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.invoke(object);
    }
}

总结

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值