Java|反射机制与动态代理

1.类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

  1. 加载

    就是指将class文件读入内存,并为之创建一个Class对象。
    任何类被使用时系统都会建立一个Class对象。

  2. 连接
    a) 验证 : 是否有正确的内部结构,并和其他类协调一致
    b) 准备 : 负责为类的静态成员分配内存,并设置默认初始化值
    c) 解析: 把类中的符号引用转换为直接引用

  3. 初始化

2.类加载器

  1. 类加载器的概述
    类加载器负责将.class文件加载到内在中,并为之生成对应的Class对象。
  2. 类加载器的分类与作用
    a) Bootstrap ClassLoader 根类加载器,也被称为引导类加载器。
    负责Java核心类的加载,比如System,String等。
    在JDK中JRE的lib目录下rt.jar文件中。
    b) Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载。
    在JDK中JRE的lib目录下ext目录
    c) Sysetm ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

3.反射

3.1 概述

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

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

简单来说,就是通过一个类的字节码文件对象(Class)反着去剖析这个类的构成

注意:一个类只有一个字节码文件对象

3.2 获取Class文件对象的三种方式

  1. 通过Object类中的getClass方法
  2. 类的静态属性(.class)
  3. 通过Class类中的一个forName静态方法 推荐使用
    方式3使用了全类名(带有包名的类),有效避免了类名相同带了的误用。
        Student student = new Student();
        //1.通过Object类中的getClass方法
        Class<? extends Student> aClass = student.getClass();

        //2.通过类的静态属性(class属性)
        Class<Student> aClass2 = Student.class;
        

        //3.通过Class类中的一个forName静态方法(推荐使用)
        Class<?> aClass3 = Class.forName("GetClassDemo.Student");
        //先敲双引号,然后敲类名,按住Ctrl+alt+空格进行提示

        System.out.println(aClass==aClass2);
        //注意:一个类只有一个字节码文件对象,所以输出true

3.3 通过反射获取构造方法并创建类的对象

一个类中一般有以下三部分构成,这三部分都可以通过反射获得。

  1. 构造方法 Constructor
  2. 成员变量 Field
  3. 成员方法 Method

有一个Student类如下,下面介绍通过反射获取Student类的构造方法。

public class Student {

    public Student() {
        System.out.println("空参构造执行 了");
    }

    public Student(String name) {
        System.out.println("一个参数的构造执行了");
    }

    public Student(String name, int age) {
        System.out.println("两个个参数的构造执行了"+name+"=="+age);
    }

    private Student(String name, char ch) {
        System.out.println("私有构造执行了");
    }

}
  1. 获取该类所有公共的构造方法对象
    Constructor<?>[] getConstructors()
    返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

  2. 获取该类所有的构造方法对象(包括私有的)
    Constructor<?>[] getDeclaredConstructors()
    返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。

  3. 获取单个的构造方法对象
    Constructor getConstructor(Class<?>… parameterTypes)
    返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

  4. 获取单个的私有构造方法对象
    Constructor getDeclaredConstructor(Class<?>… parameterTypes)
    返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法

public class MyTest {
    public static void main(String[] args) throws Exception {
        //构造方法		Constructor
        Class<?> aClass = Class.forName("GetConstructorDemo.Student");

        //1.获取该类所有公共的构造方法对象
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("**********************");
/*输出
public GetConstructorDemo.Student(java.lang.String,int)
public GetConstructorDemo.Student(java.lang.String)
public GetConstructorDemo.Student()
*/

        //2.获取该类所有的构造方法对象(包括私有的)
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("**********************");
/*
private GetConstructorDemo.Student(java.lang.String,char)
public GetConstructorDemo.Student(java.lang.String,int)
public GetConstructorDemo.Student(java.lang.String)
public GetConstructorDemo.Student()
*/
        //3.获取单个的构造方法对象
        Constructor<?> constructor = aClass.getConstructor();
        Constructor<?> constructor1 = aClass.getConstructor(String.class);
        Constructor<?> constructor2 = aClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        System.out.println(constructor1);
        System.out.println(constructor2);
        System.out.println("**********************");
/*
public GetConstructorDemo.Student()
public GetConstructorDemo.Student(java.lang.String)
public GetConstructorDemo.Student(java.lang.String,int)
*/
        //4.获取单个的私有构造方法对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, char.class);
        System.out.println(declaredConstructor);
/*
private GetConstructorDemo.Student(java.lang.String,char)
*/
    }
}

下面介绍,用反射的机制,通过构造方法创建类的对象。
1.通过空参构造创建类的对象
2.通过有参构造创建类的对象
3.通过私有构造创建类的对象

public class MyTest {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("CreateObjectDemo.Student");

        //1.通过空参构造创建类的对象
        Constructor<?> constructor = aClass.getConstructor();
        Student o = (Student) constructor.newInstance();
        System.out.println("**********************");
        //2.通过有参构造创建类的对象
        Constructor<?> constructor2 = aClass.getConstructor(String.class, int.class);
        Student s1 = (Student) constructor2.newInstance("tom", 23);
        System.out.println("**********************");
        //3.通过私有构造创建类的对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, char.class);
        declaredConstructor.setAccessible(true);//取消权限检查
        Student s2 = (Student) declaredConstructor.newInstance("jack", 'd');
    }
}

3.4 通过反射获取成员变量(字段类型)并对其赋值

有一个Student类如下,下面介绍通过反射获取Student类的成员变量(字段类型)。

public class Student {

    public String name;
    public int age;
    private double money;

    public Student() {
    }
}

1.获取所有的公共字段对象
2.获取所有的字段对象,包括私有的
3.获取单个公共字段对象
4.获取单个的私有字段对象

public class MyTest1 {
    public static void main(String[] args) throws NoSuchFieldException {
        //成员变量(字段类型)		Field
        Class<Student> studentClass = Student.class;

        //1.获取所有的公共字段对象
        Field[] fields = studentClass.getFields();
        for (Field field : fields) {
            System.out.println(field.toString());
        }
/*
public java.lang.String GetFieldDemo.Student.name
public int GetFieldDemo.Student.age
*/
        System.out.println("=====================");
        //2.获取所有的字段对象,包括私有的
        Field[] declaredFields = studentClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.toString());
        }
/*
public java.lang.String GetFieldDemo.Student.name
public int GetFieldDemo.Student.age
private double GetFieldDemo.Student.money
*/
        System.out.println("+++++++++++++++++++++");
        //3.获取单个公共字段对象
        Field name = studentClass.getField("name");
        System.out.println(name);
/*
public java.lang.String GetFieldDemo.Student.name
*/
        System.out.println("+++++++++++++++++++++");
        //4.获取单个的私有字段对象
        Field money = studentClass.getDeclaredField("money");
        System.out.println(money);
/*
private double GetFieldDemo.Student.money
*/
    }
}

下面介绍,用反射的机制,对成员变量进行赋值。
1.给对象的公共变量进行赋值
2.给对象的私有变量赋值

public class MyTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class<?> aClass = Class.forName("GetFieldDemo.Student");
        //利用构造方法创建对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object o = declaredConstructor.newInstance();

        //1.给对象的公共变量进行赋值
        Field name = aClass.getField("name");
        name.set(o,"tom");

        // 获取对象的公共变量
       Object o1 = name.get(o);
       System.out.println(o1);

       //2.给对象的私有变量赋值
        Field money = aClass.getDeclaredField("money");
        money.setAccessible(true);
        money.setDouble(o,78.94);

        Double o2 = (Double) money.get(o);
        System.out.println(o2);
    }
}

3.5 通过反射获取成员方法并让成员方法执行

有一个Student类如下,下面介绍通过反射获取Student类的成员方法。

public class Student {
   private Student() {
   }

   public void show(){
       System.out.println("空参数的show方法执行了");
   }

   public void test(String name,int age) {
       System.out.println("两个参数的 test方法执行了"+name+"==="+age);
   }

   public String hehe(double num) {
       System.out.println("有返回值的方法执行了"+num);
       return "呵呵";
   }

   private String haha(){
       System.out.println("私有的方法执行了");
       return "哈哈";
   }
}

1.获取所有的公共的方法对象,包括他父类的公共方法 对象也获取到了
2.获取所有的公共的方法对象包括私有的
3.获取单个的公共方法对象
4.获取私有的方法对象

public class MyTest1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //成员方法		Method
        Class<?> aClass = Class.forName("GetMethodDemo.Student");

        //1.获取所有的公共的方法对象,包括他父类的公共方法 对象也获取到了
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("*******************");
/*
public void GetMethodDemo.Student.test(java.lang.String,int)
public void GetMethodDemo.Student.show()
public java.lang.String GetMethodDemo.Student.hehe(double)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
*/
        //2.获取所有的公共的方法对象包括私有的
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("*************************");
/*
public void GetMethodDemo.Student.test(java.lang.String,int)
private java.lang.String GetMethodDemo.Student.haha()
public void GetMethodDemo.Student.show()
public java.lang.String GetMethodDemo.Student.hehe(double)
*/
        //3.获取单个的公共方法对象
        Method test = aClass.getMethod("test", String.class, int.class);
        System.out.println(test);
        System.out.println("*************************");
/*
public void GetMethodDemo.Student.test(java.lang.String,int)
*/
        //4.获取私有的方法
        Method haha = aClass.getDeclaredMethod("haha");
        System.out.println(haha);
/*
private java.lang.String GetMethodDemo.Student.haha()
*/
    }
}

下面介绍,用反射的机制,让成员方法执行。
1.利用反射机制执行无参方法
2.利用反射机制执行有参方法
3.利用反射机制执行私有方法

public class MyTest2 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        //
        Class<?> aClass = Class.forName("GetMethodDemo.Student");

        //用反射机制,使用空参构造,创建对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Student student = (Student) declaredConstructor.newInstance();

        //1.利用反射机制执行无参方法
        Method show = aClass.getMethod("show");
        //返回的是show方法的返回值,如果该方法没有返回值,那么返回null
        Object invoke = show.invoke(student);
        System.out.println(invoke);
/*
空参数的show方法执行了
null
*/
        System.out.println("*********************");
        //2.利用反射机制执行有参方法
        Method hehe = aClass.getMethod("hehe", double.class);
        Object invoke1 = hehe.invoke(student, 3.4);
        System.out.println(invoke1);
/*
有返回值的方法执行了3.4
呵呵
*/
        System.out.println("*********************");
        //3.利用反射机制执行私有方法
        Method haha = aClass.getDeclaredMethod("haha");
        haha.setAccessible(true);
        Object invoke2 = haha.invoke(student);
        System.out.println(invoke2);
/*
私有的方法执行了
哈哈
*/
    }
}

3.6 示例:通过反射运行配置文件

配置文件(peizhi.properties):

classname=PropertiesDemo.Cat
methname=sleep

通过更改配置文件,可以让不同类中的不同方法运行

public class Cat {
    public void eat(){
        System.out.println("猫吃鱼");
    }

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

public class Dog {
    public void eat() {
        System.out.println("狗吃骨头");
    }

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


public class MyTest {
    public static void main(String[] args) throws Exception{
        //通过反射运行配置文件内容
		//通过更改配置文件,可以让不同类中的不同方法运行
		
        //读取配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("peizhi.properties"));

        Class<?> classname = Class.forName(properties.getProperty("classname"));
        Object o = classname.getDeclaredConstructor().newInstance();
    }
}

输出

猫白天睡觉

3.7 示例:通过反射越过泛型检查

示例:我给你ArrayList的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
针对泛型只在编译器有效,运行期就擦除了这一特点,利用反射越过泛型检查。

public class domo {
    public static void main(String[] args) throws Exception {
        //我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

        ArrayList<Integer> list = new ArrayList<>();
        list.add(100);

        Class<? extends ArrayList> aClass = list.getClass();
        Method add = aClass.getDeclaredMethod("add", Object.class);
        add.invoke(list, "abc");
        System.out.println(list);
    }

输出:[100, abc]

3.8 示例:通过反射写一个通用的设置某个对象的某个属性为指定的值

public class MyTest {
    public static void main(String[] args) throws Exception {
        Student student = new Student();
        MyUtils.setProperty(student,"name","李四");
        Object name = MyUtils.getProperty(student, "name");
        System.out.println(name);
    }
}



public class MyUtils {                                                                                                                       
    private MyUtils() {                                                                                                                      
    }                                                                                                                                                                                                                                                                                                                                                                                                               
    //写注释的快捷键:/**+Enter                                                                                                                      
    /**                                                                                                                                      
     *                                                                                                                                       
     * @param obj    对象                                                                                                                      
     * @param propertyName    成员变量名                                                                                                        
     * @param value    值                                                                                                                     
     */                                                                                                                                      
    public static void setProperty(Object obj, String propertyName, Object value) throws Exception {                                         
     //此方法可将obj对象中名为propertyName的属性的值设置为value。                                                                                               
        Class<?> aClass = obj.getClass();                                                                                                    
        Field declaredField = aClass.getDeclaredField(propertyName);                                                                         
        declaredField.setAccessible(true);                                                                                                   
        declaredField.set(obj,value);                                                                                                        
    }                                                                                                                                        
                                                                                                                                             
    public static Object getProperty(Object obj, String propertyName) throws Exception {                                                     
        Class<?> aClass = obj.getClass();                                                                                                    
        Field declaredField = aClass.getDeclaredField(propertyName);                                                                         
        declaredField.setAccessible(true);                                                                                                   
        Object o = declaredField.get(obj);                                                                                                   
        return o;                                                                                                                            
    }                                                                                                                                        
                                                                                                                                             
}                                                                                                                                            
                                                                                                                                             
public class Student {
    private String name;
    private int age;
    private double money;
}

4 动态代理

4.1 动态代理概述

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

动态代理:在程序运行过程中产生的这个对象,程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理.

作用:在任何时刻,只要想将“额外的操作”从“实际对象”中分离出去,并且希望能够灵活的修改“额外的操作”,都可用动态代理来实现。具体来说,动态代理可以实现:

  1. 跟踪实体类方法的调用,度量这些调用的开销;
  2. 在程序运行期间,将用户接口事件与动作关联起来;
  3. 路由对远程服务器的方法调用。

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理,如果没有接口,可以用cglib。

动态代理详述:

  1. 特点:字节码随用随创建,随用随加载
  2. 作用:不修改源码的基础上对方法增强
  3. 分类:
    3.1 基于接口的动态代理 Proxy
    3.2 基于子类的动态代理 cglib
  4. 基于接口的动态代理
    4.1 涉及的类:Proxy
    4.2 提供者:JDK官方
  5. 创建代理对象的要求: 被代理类最少实现一个接口,如果没有则不能使用
  6. 重点)如何创建代理对象: 使用Proxy类中的newProxyInstance方法
    Object obj=Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
    newProxyInstance方法里的三个参数解释如下:
  • ClassLoader:类加载器,一般用接口的类加载器(Myinterface.class.getClassLoader()
  • Class[]:Class对象数组,一般用接口的对象数组(new Class[]{Myinterface.class})
  • InvocationHandler:调用处理器(new MethodsSelect(new RealObject())),它是一个实现了InvocationHandler接口的类(class MethodsSelect implements InvocationHandler),传入的参数是一个实现了接口的实体类(class RealObject implements Myinterface

    这里对InvocationHandler接口做一些详细的说明:
    InvocationHandler接口中,只有一个 Object invoke(Object proxy, Method method, Object[] args)方法需要重写,这个方法中的三个参数
    – proxy:在其上调用方法的代理实例
    – method :对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
    – args:包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

4.2 示例1:方法过滤

package com.young.Hello20200714;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


//动态代理
public class Test2 {
    public static void main(String[] args) {
        Myinterface proxy = (Myinterface) Proxy.newProxyInstance(Myinterface.class.getClassLoader(), new Class[]{Myinterface.class}, new MethodsSelect(new RealObject()));
        proxy.boring1();
        proxy.boring2();
        proxy.interesting("young");
        proxy.boring3();
    }
}
//接口
interface Myinterface{
    void boring1();
    void boring2();
    void interesting(String arg);
    void boring3();
}

//接口的实现类
class RealObject implements Myinterface{
    @Override
    public void boring1() {
        System.out.println("boring1");
    }
    @Override
    public void boring2() {
        System.out.println("boring2");
    }
    @Override
    public void interesting(String arg) {
        System.out.println("interesting:"+arg);
    }
    @Override
    public void boring3() {
        System.out.println("boring3");
    }
}

//实现InvocationHandler的类
class MethodsSelect implements InvocationHandler{
    private Object obj;
    public MethodsSelect(Object object) {
        this.obj = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("interesting")){
            System.out.println("proxy Method");
        }
        return method.invoke(obj,args);
    }
}

output:

boring1
boring2
proxy Method
interesting:young
boring3

4.2 示例2:增删改查功能拓展

对用户的增删改查的功能进行增加,例如增加校验功能和记录日志的功能,用代理的机制来完成。

public interface MyInterface {
    //接口
    void insert();
    void delete();
    void update();
    void query();
}

public class RealObject implements MyInterface{
    //实现类
    @Override
    public void insert() {
        System.out.println("增加了一个用户");
    }
    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }
    @Override
    public void update() {
        System.out.println("修改用户");
    }
    @Override
    public void query() {
        System.out.println("查询用户");
    }
}

public class ProxyDemo {
    public static MyInterface getProxy(MyInterface myInterface){
        MyInterface obj = (MyInterface) Proxy.newProxyInstance(myInterface.getClass().getClassLoader(), myInterface.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invokevalue = null;
                if (method.getName().equals("update")) {
                    System.out.println("权限校验");
                    invokevalue = method.invoke(myInterface);
                    System.out.println("记录日志");

                }else {
                    invokevalue =method.invoke(myInterface);
                }
                return invokevalue ;
            }
        });
			return obj;
    }
}

public class MyTest1 {
    public static void main(String[] args) {
        //测试类
        MyInterface myInterface = new RealObject();
        MyInterface proxy = ProxyDemo.getProxy(myInterface);
        proxy.delete();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值