
本文共 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
参考:
发表评论
最新留言
关于作者
