Java反射和动态代理

文章目录

    • 1、反射
      • 1.1 反射的概述
      • 1.2 反射作用
      • 1.3 获取字节码文件对象的方式
      • 1.4 字节码文件和字节码文件对象
      • 1.5 获取构造方法
      • 1.6 获取构造方法并创建对象
      • 1.7 获取成员变量并获取值和修改值
      • 1.8 获取成员方法
      • 1.9 获取成员方法并运行
      • 1.10 反射练习
        • 1.10.1 泛型擦除
        • 1.10.2 修改字符串的内容
        • 1.10.3 利用反射保存对象中的信息
        • 1.10.4反射结合配置文件
    • 2. 动态代理
      • 2.1 动态代理好处
      • 2.2 动态代理三要素
      • 2.3 动态代理简单实现
      • 2.4 动态代理扩展
      • 2.5 动态代理练习

1、反射

1.1 反射的概述

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

专业的解释

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

通俗的理解

  • 利用反射创建的对象可以无视修饰符调用类里面的内容

  • 可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。

  • 读取到什么类,就创建什么类的对象

  • 读取到什么方法,就调用什么方法

  • 此时当需求变更的时候不需要修改代码,只要修改配置文件即可。

1.2 反射作用

反射都是从class字节码文件中获取的内容。

  • 获取class字节码文件的对象
  • 利用反射如何获取构造方法(创建对象)
  • 利用反射如何获取成员变量(赋值,获取值)
  • 利用反射如何获取成员方法(运行)

反射的好处:

  • 无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的;
  • 反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

1.3 获取字节码文件对象的方式

  • Class这个类里面的静态方法forName(“全类名”)(最常用)
  • 通过class属性获取,一般更多的是当做参数进行传递
  • 通过对象获取字节码文件对象,当我们已经有了这个类的对象时,才可以使用。

代码示例:

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. 第一种方式
        //全类名 : 包名 + 类名
        Class clazz1 = Class.forName("com.ya.reflect.demo01.Student");

        //2. 第二种方式
        Class clazz2 = Student.class;

        //3.第三种方式
        Student s = new Student();
        Class clazz3 = s.getClass();

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}

image-20231227144539976

1.4 字节码文件和字节码文件对象

java文件(源代码阶段):就是编写的java代码。

字节码文件(加载阶段):就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)

字节码文件对象(运行阶段):当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。

反射获取的是字节码文件对象,这个对象在内存中是唯一的。

1.5 获取构造方法

规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限setAccessible(true),否则无法使用
方法名说明
Constructor<?>[] getConstructors()获得所有的构造(只能public修饰)
Constructor<?>[] getDeclaredConstructors()获得所有的构造(包含private修饰)
Constructor getConstructor(Class<?>… parameterTypes)获取指定构造(只能public修饰)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)获取指定构造(包含private修饰)
Constructor类中用于创建对象的方法:
- T newInstance(Object... initargs) :根据指定的构造方法创建对象
- setAccessible(boolean flag) :设置为true,表示取消访问检查

代码示例:

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;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        //1.获取class字节码文件对象
        Class clazz = Class.forName("com.ya.reflect.demo02.Student");

        //2.获取构造方法
        // 2.1 返回所有公共构造方法对象的数组
        System.out.println("-----返回所有公共构造方法对象的数组----");
        Constructor[] con1 = clazz.getConstructors();
        for (Constructor con : con1) {
            System.out.println(con);
        }

        // 2.2 返回所有构造方法对象的数组
        System.out.println("-------返回所有构造方法对象的数组------");
        Constructor[] con2 = clazz.getDeclaredConstructors();
        for (Constructor con : con2) {
            System.out.println(con);

        }

        // 2.3 返回所有构造方法对象的数组
        System.out.println("-------返回单个公共构造方法对象------");
        Constructor con3 = clazz.getConstructor(String.class);
        System.out.println(con3);
        //2.4 获取指定的空参构造
        System.out.println("-------获取指定的空参构造------");
        Constructor con4 = clazz.getConstructor();
        System.out.println(con4);


        // 2.5 返回所有构造方法对象的数组
        System.out.println("-------返回单个构造方法对象------");
        Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con5);


        System.out.println("-----获取构造方法的权限修饰符(返回整数)--------");
        // 返回的是整数,public 1,private 2,protected 4,abstract 1024
        int modifiers = con5.getModifiers();
        System.out.println(modifiers);

        System.out.println("-----获取构造方法的参数--------");
        Parameter[] parameters = con5.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        System.out.println("--------创建对象-----------");
        //暴力反射:表示临时取消权限校验
        con5.setAccessible(true);   // 如果这个权限修饰符是私有的,就要取消权限校验
        Student stu = (Student) con5.newInstance("张三", 23);

        System.out.println(stu);
    }
}

image-20231227152918797

1.6 获取构造方法并创建对象

涉及到的方法:newInstance

代码示例:

public class ReflectDemo02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        
        //需求1:获取空参,并创建对象(所创建的对象的属性是默认值)
        //1.获取整体的字节码文件对象
        Class clazz = Class.forName("com.ya.reflect.demo02.Student");
        //2.获取空参的构造方法
        Constructor con = clazz.getConstructor();
        //3.利用空参构造方法创建对象
        Student stu = (Student) con.newInstance();
        System.out.println(stu);


        System.out.println("----------------------------------");

        //需求2:获取带参构造,并创建对象
        //1.获取整体的字节码文件对象
        Class clazz2 = Class.forName("com.ya.reflect.demo02.Student");
        //2.获取有参构造方法
        Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class);
        //3.临时修改构造方法的访问权限(暴力反射)
        con2.setAccessible(true);
        //4.直接创建对象
        Student stu2 = (Student) con2.newInstance("zhangsan", 23);
        System.out.println(stu2);
    }
}

image-20231227153804258

1.7 获取成员变量并获取值和修改值

获取成员变量规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

获取值和修改值:

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值

代码示例:

public class Student {
    private String name;
    private int age;
    public String gender;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //1.获取class字节码文件的对象
        Class clazz = Class.forName("com.ya.reflect.demo03.Student");

        //2.获取所有的成员变量
        System.out.println("-----获取所有的成员变量----");
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //3.获取单个的成员变量
        System.out.println("-----获取单个的成员变量----");
        Field fieldName = clazz.getDeclaredField("name");
        System.out.println(fieldName);

        System.out.println("-----获取成员变量的数据类型----");
        Class<?> type = fieldName.getType();
        System.out.println(type);

        System.out.println("-----获取成员变量记录的值----");
        Student student = new Student("zhangsan", 23, "男");
        fieldName.setAccessible(true);
        String value = (String) fieldName.get(student);
        System.out.println(value);

        System.out.println("-----修改对象里面记录的值---");
        fieldName.set(student,"lisi");
        System.out.println(student);
    }
}

image-20231227160023230

1.8 获取成员方法

规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的,包含父类中所有的公共方法)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到,不能获取父类的
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到
获取方法的修饰符
获取方法的名字
获取方法的形参
获取方法的返回值
获取方法的抛出的异常

代码示例:

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }

    private String eat(String something) throws IOException,NullPointerException,ClassCastException {
        System.out.println("在吃" + something);
        return "奥利给";
    }

    private void eat(String something,int a) {
        System.out.println("在吃" + something);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
       
        //1. 获取class字节码文件对象
        Class clazz = Class.forName("com.ya.reflect.demo04.Student");

        //2. 获取里面所有的方法对象(包含父类中所有的公共方法)
        System.out.println("-------获取里面所有的方法对象(包含父类中所有的公共方法)-----");
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        // 3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
        System.out.println("--获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)--");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        // 4.获取指定的单一方法
        System.out.println("-------获取指定的单一方法-----");
        Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(eatMethod);

        // 5.获取方法的修饰符(返回整数)
        System.out.println("-------获取方法的修饰符(返回整数)-----");
        int modifiers = eatMethod.getModifiers();
        System.out.println(modifiers);

        // 6.获取方法的名字
        System.out.println("-------获取方法的名字-----");
        String eatMethodName = eatMethod.getName();
        System.out.println(eatMethodName);

        // 7.获取方法的形参
        System.out.println("-------获取方法的形参-----");
        Parameter[] parameters = eatMethod.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        //8.获取方法的抛出的异常
        System.out.println("-------获取方法的抛出的异常-----");
        Class[] exceptionTypes = eatMethod.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }
    }
}

image-20231227163212848

image-20231227163234833

1.9 获取成员方法并运行

Object invoke(Object obj, Object… args) :运行方法

  • 参数一:用obj对象调用该方法
  • 参数二:调用方法的传递的参数(如果没有就不写)
  • 返回值:方法的返回值(如果没有就不写)

代码示例:

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1. 获取class字节码文件对象
        Class clazz = Class.forName("com.ya.reflect.demo04.Student");

        //2. 获取指定的单一方法
        Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
      
        // 3. 方法运行
        System.out.println("-------方法运行-----");
        Student student = new Student();
        eatMethod.setAccessible(true);
        String result = (String) eatMethod.invoke(student, "汉堡包");
        System.out.println(result);
    }
}

image-20231227163822950

1.10 反射练习

1.10.1 泛型擦除

集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。

代码示例:

public class Test1 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        //1.创建集合对象
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
//        list.add("aaa");

        //2.利用反射运行add方法去添加字符串
        //因为反射使用的是class字节码文件

        //获取class对象
        Class clazz = list.getClass();

        //获取add方法对象
        Method method = clazz.getMethod("add", Object.class);

        //运行方法
        method.invoke(list,"aaa");

        //打印集合
        System.out.println(list);
    }
}

image-20231227165333141

image-20231227165351201

1.10.2 修改字符串的内容

在这个练习中,需要掌握的是字符串不能修改的真正原因。

字符串,在底层是一个byte类型的字节数组,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private

final修饰value表示value记录的地址值不能修改。

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码示例:

public class Test2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s = "abc";
        String ss = "abc";
        // private final byte[] value= {97,98,99};

        /**
         * 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值,
         * 如果我们利用反射获取了value的地址值,也是可以修改的,
         * final修饰的value,真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,
         * 但是这种操作非常危险,JDK高版本已经屏蔽了这种操作,低版本还是可以的
         */

        //1.获取class对象
        Class clazz = s.getClass();

        //2.获取value成员变量(private)
        Field field = clazz.getDeclaredField("value");

        //临时修改权限
        field.setAccessible(true);

        //3.获取value记录的地址值
        byte[] bytes = (byte[]) field.get(s);
        bytes[0] = 100; // 对应的字符编码为 'd'

        System.out.println(s);  //输出dbc
        System.out.println(ss); //输出dbc
    }
}

image-20231227170402073

image-20231227170444908

1.10.3 利用反射保存对象中的信息

利用反射保存对象中的信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。

需求:创建对象,并将对象信息保存到txt文件中。

Student

public class Student {
    private String name;
    private int age;
    private char gender;
    private double height;
    private String hobby;

    public Student() {
    }

    public Student(String name, int age, char gender, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
    }
}

Teacher

public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        Student s = new Student("张三",23,'女',167.5,"睡觉");
        Teacher t = new Teacher("李四",10000);
        //saveObject(s);
        saveObject(t);
    }

    //把对象里面所有的成员变量名和值保存到本地文件中
    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.获取字节码文件的对象
        Class clazz = obj.getClass();
        //2. 创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("my-reflect-dynamicproxy/src/a.txt"));
        //3. 获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
            //写出数据
            bw.write(name + "=" + value);
            bw.newLine();
        }
        bw.close();
    }
}

image-20231227172337515

image-20231227172433181

1.10.4反射结合配置文件

需求:利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

  • 通过Properties加载配置文件
  • 得到类名和方法名
  • 通过类名反射得到Class对象
  • 通过Class对象创建一个对象
  • 通过Class对象得到方法
  • 调用方法

代码示例:

prop.properties

classname=com.ya.reflect.demo06.Teacher
method=teach

#classname=com.ya.reflect.demo06.Student
#method=study
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

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

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}
public class ReflectDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("my-reflect-dynamicproxy/src/prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

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

        System.out.println(className);
        System.out.println(methodName);

        //3.利用反射创建对象并运行方法
        Class clazz = Class.forName(className);

        //获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        Object o = con.newInstance();
        System.out.println(o);

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

image-20231227171506023

image-20231227171526327

2. 动态代理

2.1 动态代理好处

无侵入式的给方法增强功能。调用者(BitStar)–>代理(ProxyUtil)–>对象(Star)

2.2 动态代理三要素

  • 接口:代理对象,被代理类和代理类都需要实现这个接口(本例是Star)
  • 代理类:通过Proxy类动态生成的代理类(本例是ProxyUtil)
  • 被代理类:代理类实例,它会代替被代理对象处理方法调用(本例是BigStar)

注:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

2.3 动态代理简单实现

需求:外面的人想要大明星唱一首歌

  • 获取代理的对象:代理对象 = ProxyUtil.createProxy(大明星的对象);
  • 再调用代理的唱歌方法:代理对象.唱歌的方法(“只因你太美”);

(1)接口:代理对象,定义被代理的方法

public interface Star {

    //可以把所有想要被代理的方法定义在接口当中

    //唱歌
    public abstract String sing(String name);

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

(2)被代理类:需要使用代理的实体类,要实现代理对象接口

public class BigStar implements Star{
    private String name;

    public BigStar() {
    }

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

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

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

(3)代理类:创建代理

java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法

// 创建代理
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理有哪些方法
参数三:用来指定生成的代理对象要干什么事情
public class ProxyUtil {

    /**
     *  形参:被代理的明星对象
     *  返回值:给明星创建的代理
     */
    public static Star createProxy(BigStar bigStar){
        // 创建代理
        // 这个代码没有显示实现Star接口,但通过JDK动态代理机制隐式地为生成的代理类添加了Star接口的实现,从而可以看作实现了Star接口
        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 {
                        /**
                         * 参数一:代理的对象,即star变量引用的对象
                         * 参数二:要运行的方法,sing,dance
                         * 参数三:调用sing方法时,传递的实参
                         */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

(4)代理测试

public class Test {
    public static void main(String[] args) {
        //1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}

image-20231227220017611

2.4 动态代理扩展

动态代理,还可以拦截方法。比如:在这个故事中,经纪人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        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 {
                        if("cleanWC".equals(method.getName())){
                            System.out.println("拦截,不调用大明星的方法");
                            return null;
                        }
                        //如果是其他方法,正常执行
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

2.5 动态代理练习

需求:对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强。

代码实现:

public class DynamicProxyTest {
    public static void main(String[] args) {
        //1.创建真正干活的人
        ArrayList<String> list = new ArrayList<>();

        /**
         * 2.创建代理对象
         * 参数一:类加载器。当前类名.class.getClassLoader()
         *      把当前的类,加载到内存中了,把后面的代理类,也加载到内存
         * 参数二:是一个数组,在数组里面写接口的字节码文件对象。
         *      如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截
         *      但是,一定要写ArrayList真实实现的接口
         *      假设在第二个参数中,写了MyInter接口,那么是错误的。
         *      因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截
         * 参数三:用来创建代理对象的匿名内部类
         */
        List proxyList = (List) Proxy.newProxyInstance(
                //参数一:类加载器
                DynamicProxyTest.class.getClassLoader(),
                //参数二:是一个数组,表示代理对象能代理的方法范围
                new Class[]{List.class},
                //参数三:本质就是代理对象
                new InvocationHandler() {
                    /**
                     * invoke方法参数的意义
                     * 参数一:表示代理对象,一般不用(了解)
                     * 参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截
                     * 参数三:就是下面第三步调用方法时,传递的参数。
                     *
                     * 举例1:
                     * list.add("阿玮好帅");
                     * 此时参数二就是add这个方法名
                     * 此时参数三 args[0] 就是 阿玮好帅
                     *
                     * 举例2:
                     * list.set(1, "aaa");
                     * 此时参数二就是set这个方法名
                     * 此时参数三  args[0] 就是 1  args[1]"aaa"
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //对add方法做一个增强,统计耗时时间
                        if (method.getName().equals("add")) {
                            long start = System.currentTimeMillis();
                            //调用集合的方法,真正的添加数据
                            method.invoke(list, args);
                            long end = System.currentTimeMillis();
                            System.out.println("耗时时间:" + (end - start));
                            //需要进行返回,返回值要跟真正增强或者拦截的方法保持一致
                            return true;
                        } else if (method.getName().equals("remove") && args[0] instanceof Integer) {
                            System.out.println("拦截了按照索引删除的方法");
                            return null;
                        } else if (method.getName().equals("remove")) {
                            System.out.println("拦截了按照对象删除的方法");
                            return false;
                        } else {
                            //如果当前调用的是其他方法,我们既不增强,也不拦截
                            method.invoke(list, args);
                            return null;
                        }
                    }
                }
        );

        /**
         * 3.调用方法
         * 如果调用者是list,就好比绕过了第二步的代码,直接添加元素
         * 如果调用者是代理对象,此时代理才能帮我们增强或者拦截
         * 每次调用方法的时候,都不会直接操作集合
         * 而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截
         */
        proxyList.add("aaa");
        proxyList.add("bbb");
        proxyList.add("ccc");
        proxyList.add("ddd");

        proxyList.remove(0);
        proxyList.remove("aaa");

        //打印集合
        System.out.println(list);
    }
}

image-20231227221146481

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/299841.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue+nodejs微信小程序基于uniapp的学生宿舍打卡失物招领管理系统

基于微信的宿舍管理系统的设计基于现有的手机&#xff0c;可以实现等功能。方便用户对宿舍管理系统查看个人中心、失物招领管理、失物认领管理、晚归打卡管理、宿舍信息管理、宿舍更新管理、交流论坛、系统管理等功能模块的管理及详细的设计与统计分析。根据系统功能需求建立的…

网络安全法解读之思维导图

一、出台背景 二、法律基础 三、网络安全法架构 1、第一章 总则&#xff08;1-14条&#xff09; 2、第二章 网络安全支持与促进&#xff08;15-20条&#xff09; 3、 第三章 网络运行安全&#xff08;21-39条&#xff09; &#xff08;1&#xff09;第一节 一般规定 &#xf…

Linux无法启动:Timed out waiting for device dev-disk-by

由于CPU风扇问题&#xff0c;导致主机无法启动&#xff1b;鉴于机器本身比较老旧&#xff0c;因此&#xff0c;决定直接把硬盘拆下后更换到新的主机上&#xff0c;更安全可靠&#xff1b;问题却因此而起&#xff1a;把硬盘更换到新主机后&#xff0c;居然无法启动&#xff0c;开…

简易机器学习笔记(九)LeNet实例 - 在眼疾识别数据集iChallenge-PM上的应用

前言 上一节大概讲了一下LeNet的内容&#xff0c;这一章就直接来用&#xff0c;实际上用一下LeNet来进行训练和分类试试。 调用的数据集&#xff1a; https://aistudio.baidu.com/datasetdetail/19065 说明&#xff1a; 如今近视已经成为困扰人们健康的一项全球性负担&…

Windows下默认关闭数字键盘

进入注册表&#xff0c;找到值HKEY_USERS 》 .DEFAULT 》 Control Panel 》 Keyboard &#xff0c;点击 Keyboard 之后在右侧窗口中找到 InitialKeyboardIndicators&#xff0c;设置为0&#xff0c;保存&#xff0c;重启电脑 该值的意义

深信服技术认证“SCSA-S”划重点:文件包含漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…

永磁同步电机的磁场定向控制

目录 概述 通过系统仿真验证行为 探索模型架构 生成用于集成到嵌入式应用程序的控制器 C 代码 指定控制器模型的参考行为 创建 PIL 实现 准备用于 PIL 测试的控制器模型 测试生成的代码的行为和执行时间 结论 此示例说明从电机控制算法生成 C 代码并验证其编译行为和执…

分布式事务完美解决方案:消息中间件(kafka)+ 本地事物 + 消息校对

前言 分布式事务是要保证多个服务下的多个数据库操作的一致性。分布式事务常见解决方案有&#xff1a;二阶段、三阶段和TCC实现强一致性事务&#xff0c;其实还有一种广为人知的方案就是利用消息队列来实现分布式事务&#xff0c;保证数据的最终一致性&#xff0c;也就是我们常…

带大家做一个,易上手的家常香干炒腊肉

从冰箱那一块腊肉 套个食品级的袋子 然后用冷水化冰 准备两块香干 香干切成片 不要太薄 当然也别厚了 一把青蒜 青蒜切成段 干和叶子分开装 腊肉去掉下面的肉皮 然后切小块 锅中加入清水 下入少量油和盐 开小火 水起泡泡后下入香干 过水 半分钟左右 香干捞出备用 将腊…

Geoserver扩展发布MySQL视图功能

Geoserver中并不自带mysql数据发布功能&#xff0c;需要扩展外部插件。 1、示例以geoserver-2.20.5版本进行演示&#xff0c;所以MySQL插件需要到该版本对应的“Extensions”标题下查找&#xff0c;下载地址&#xff1a;GeoServer&#xff0c;详见下图 2、选择MySQL进入下载页…

【北邮国院大四上】Business Technology Strategy 企业技术战略

北邮国院电商大四在读&#xff0c;本笔记仅为PPT内容的整理与翻译&#xff0c;并不代表本课程的考纲及重点&#xff0c;仅为本人复习时方便阅读与思考之作。 写在前面 大家好&#xff0c;欢迎来到大学期间的最后一门课程&#xff0c;本门课程是中方课&#xff0c;所以很庆幸的…

小微企业在银行信贷相关产品和机器学习建模案例_论文科研_企业调研

各银行小微企业贷款业务 互联网的时代&#xff0c;大量新信息技术的涌现和网络的无处不在&#xff0c;想要抢占这片金融天地&#xff0c;必须重视小微金融业务&#xff0c;小微企业是一直具有重大潜力的客户&#xff0c;商业银行、消金公司发展小微信贷业务可以拓宽自身客户群…

C#编程-显示运算符重载

重载函数的概念也可以应用于运算符。在将C#运算符应用到用户定义的数据类型时,运算符重载为它们提供额外的能力。只可以重载预定义的C#运算符组。 运算符重载的必要性 大多数内置数据类型都有与它们相关的预定义运算符。例如:带有运算符+、-、*和/的C#数据类型int为数学运算…

JavaScript面向对象编程实战

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 面向对象编程(OOP)是JavaScript中非常重要的一个概念。掌握OOP可以帮助我们写出更加清晰、…

synchronized、volatile关键字

Java中的synchronized关键字 synchronized关键字介绍 synchronized块是Java提供的一种原子性内置锁&#xff0c;Java中的每个对象都可以把它当作一个同步锁来使用&#xff0c;这些Java内置的使用者看不到的锁被称为内部锁&#xff0c;也叫作监视器锁。 线程的执行代码在进入…

LLM Agent之RAG的反思:放弃了压缩还是智能么?

已经唠了三章的RAG&#xff0c;是时候回头反思一下&#xff0c;当前的RAG是解决幻觉的终点么&#xff1f;我给不出直接的答案&#xff0c;不过感觉当前把RAG当作传统搜索框架在大模型时代下的改良&#xff0c;这个思路的天花板高度有限~ 反思来源于对RAG下模型回答的直观感受&…

【软考中级-软件设计师】day3:程序设计语言基础知识

概述 练习题 程序设计语言的基本成分 练习题 编译程序基本原理 名词解释 词法分析 词法分析&#xff08;英语&#xff1a;lexical analysis&#xff09;是计算机科学中将字符序列转换为单词&#xff08;Token&#xff09;序列的过程。进行词法分析的程序或者函数叫作…

Duboo-入门到学废【下篇】

目录 &#x1f953;1.dubbo-admin &#x1f32d;2.序列化 &#x1f9c2;3.超时 &#x1f95a;4.重试 ❤️5.多版本 &#x1f9c7;6.负载均衡 &#x1f35f;7.集群容错 1.dubbo-admin &#x1f495;&#x1f495;&#x1f495; 1.1dubbo-admin是什么 1.duboo-admin是一…

【大数据】Flink CDC 的概览和使用

Flink CDC 的概览和使用 1.什么是 CDC2.什么是 Flink CDC3.Flink CDC 前生今世3.1 Flink CDC 1.x3.2 Flink CDC 2.x3.3 Flink CDC 3.x 4.Flink CDC 使用5.Debezium 标准 CDC Event 格式详解 1.什么是 CDC CDC&#xff08;Change Data Capture&#xff0c;数据变更抓取&#xf…

SpringCloud-高级篇(十三)

前面的主从集群可以应对Redis高并发读的问题&#xff0c;Redis主从之间可以做同步&#xff0c;为了提高主从同步时的性能&#xff0c;单节点Redis的内存不要设置太高&#xff0c;如果内存占用过多&#xff0c;做RDB的持久化&#xff0c;或者做全量同步的时候&#xff0c;导致大…
最新文章