反射、静态代理、动态代理的概念与实现
发布日期:2021-05-10 10:11:00 浏览次数:19 分类:精选文章

本文共 6893 字,大约阅读时间需要 22 分钟。

反射

什么是反射?

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

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

优点:

增加了程序的灵活性

降低类与类之间的耦合性

缺点

消耗资源

 

获取Class的三种方式

1.object ----- gentClass;

2.任何数据类型都有一个静态的class属性

3.通过Class的静态方法:Class.forName(String className) 常用

 

用途:

通过反射获取配置文件

反编译 .class - java

框架应用

通过反射越过泛型的检查。具体怎么越问度娘吧

 

 

举个栗子🌰

一个Student对象

package com.mtgg.DTO;import lombok.Data;@Datapublic class Student {    public String myName;    public String content;    private String name;    private Integer studentId;    private int x = 100;    public int getX() {        return x;    }    public int fix(int y) {        x = x - y;        return x;    }    public Student(String name){        System.out.println("我是有参构造方法 "+name);        this.name = name;    }    public Student(){        System.out.println("我是无参构造方法");    }    public void show1(String s){        System.out.println("调用了:公有的,String参数的show1(): s = " + s);    }    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                ", studentId=" + studentId +                '}';    }}

 

package com.mtgg.controller.proxy;import com.alibaba.fastjson.JSON;import com.mtgg.DTO.Student;import java.lang.reflect.*;/** * @Auther: Li * @Date: 2020/2/20 13:35 * @Description: 反射 */public class ProxyController {    public static void main(String[] args) {        try {            Class c = Class.forName("com.mtgg.DTO.Student");            System.out.println("对象---->"+c);            //构造方法            Constructor[] constructor = c.getConstructors();//获取构造方法数组            for (Constructor con : constructor){                System.out.println("构造方法---->"+con);            }            Constructor constructor1 = c.getConstructor(null);//获取无参构造方法            Object object = constructor1.newInstance();            System.out.println("newinstance---->"+object);            Student student = (Student) object;            System.out.println("对象名字属性---->"+student.getName());            //属性            Field[] fields = c.getFields();            for (Field field : fields){                System.out.println("field---->"+field);            }            Field field = c.getField("myName");            System.out.println("获取myname属性--->"+field);            field.set(object, "MTGG");      //此处设置的为student的属性            System.out.println("myname-set---->"+student.getMyName());            //方法            Method method = c.getMethod("show1", String.class);            System.out.println("method---"+method);            Method[] ms = c.getMethods();            System.out.println("ms:0------"+ JSON.toJSONString(ms));            Object o = method.invoke(object, "山东达里奎");//此执行了反射赋值调用        } catch (Exception e) {            e.printStackTrace();        }    }}

 运行结果

对象---->class com.mtgg.DTO.Student

构造方法---->public com.mtgg.DTO.Student(java.lang.String)
构造方法---->public com.mtgg.DTO.Student()
我是无参构造方法
newinstance---->Student{name='null', studentId=null}
对象名字属性---->null
field---->public java.lang.String com.mtgg.DTO.Student.myName
field---->public java.lang.String com.mtgg.DTO.Student.content
获取myname属性--->public java.lang.String com.mtgg.DTO.Student.myName
myname-set---->MTGG
method---public void com.mtgg.DTO.Student.show1(java.lang.String)
ms:0------[{"accessible":false,"annotatedExceptionTypes":[],"annotatedParameterTypes":[{"annotations":…………
调用了:公有的,String参数的show1(): s = 山东达里奎

 

 

 

 

静态代理

静态代理:接口类的实现

相当于获取该接口的引用,然后对其进行再次操作进行增强

优点:使用者只关心业务逻辑,不需要着眼内部实现,方便后期的变更和部分共用代码的统一处理。

缺点:当代理类中出现的被代理类越来越多时,内部就会显得非常臃肿。反而不利于管理阅读。

实现

// 共同的接口public interface Proxy {    public abstract void todo();}

 

 

// 真实角色class RealityRole implements Proxy {        @Override    public void todo() {        System.out.println("真实角色的功能");    }}// 代理角色class ProxyRole implements Proxy {    // 持有代理角色的引用    private Proxy realityRole;        public ProxyRole() {            }        //传入一个真实角色    public ProxyRole(Proxy role) {        realityRole = role;    }    @Override    public void todo() {        //在真实角色功能运行之前,代理角色做准备工作        doBefore();        //执行真实角色的功能        realityRole.todo();        //代理角色的收尾工作        doAfter();    }    private void doBefore() {        System.out.println("准备工作");    }      private void doAfter() {        System.out.println("收尾工作");    }}

创建真实角色的对象和代理角色的对象,并将真实角色对象的引用传给代理角色,让代理角色去执行功能。

public class Test {    public static void main(String[] args) {        //创建真实角色对象        Proxy realityRole = new RealityRole();        //创建代理角色对象,并制定真实对象        ProxyRole proxyRole = new ProxyRole(realityRole);        //代理角色工作,本质调用的还是真实角色的功能        proxyRole.todo();    }}运行结果:  准备工作  真实角色的功能  收尾工作

 

 

动态代理

必须得有委托类实现接口 否则要用cglib动态代理(暂不介绍)

 

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用

 

Proxy : newProxyInstance用的最多:动态获取一个代理的对象

 

 

步骤:

创建需要被代理的类

s= new出的对象

获取类加载器

ClassLoader loader = Thread.currentThread().getContextClassLoader();

指明被代理类实现的接口

Class<?>[] interfaces = s.getClass().getInterfaces();

创建代理委托类 有参构造方法传递被代理类对象s

生成代理类

Proxy.newProxyInstance(loader, interfaces, 委托类);

强制转换为接口

通过代理类调用被代理类的方法

 

 

具体代码实现

一个公共接口 eat吃,say说

public interface Persion {    String say();    String eat();}

实现这个接口

public class ProxyStaticImpl implements Persion {    @Override    public String say() {        System.out.println("say");        return null;    }    @Override    public String eat() {        System.out.println("eat");        return null;    }}
package com.mtgg.controller.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * @Author: MTGG * @Date: 21:56 2019/4/21 * @Describe: 动态代理 */public class InvocationTest implements InvocationHandler{    private Object object;    public InvocationTest(Object o){        this.object = o;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("invoke  start");//        for (Object o : args){//            System.out.println(o);//        }        //通过反射调用被代理类的方法        method.invoke(object, args);        System.out.println("invoke end");        return null;    }    public static void main(String[] args) {        ProxyStaticImpl p = new ProxyStaticImpl();//        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();        Class[] c = p.getClass().getInterfaces();        InvocationTest invocationTest = new InvocationTest(p);        Persion persion = (Persion) Proxy.newProxyInstance(classLoader, c, invocationTest);        persion.eat();        persion.say();        System.out.println("end");    }}

运行结果

invoke  start

eat
invoke end
invoke  start
say
invoke end
end
 

关于invoke的原理如下图,可跟踪下源码

权限检查--内部调用MethodAccessor的invoke方法,内部ReflectionFactory

 

 

 

 

参考:

 

 

 

 

 

 

上一篇:JVM内存模型以及垃圾回收算法的基本认识
下一篇:JWT的介绍、代码实现与解决方案

发表评论

最新留言

很好
[***.229.124.182]2025年04月05日 23时30分01秒