反射

反射是在jvm运行的过程中,动态的获取类的属性和方法

常见的反射场景:

  1. 编辑器在编辑的时候,动态代码提醒、
  2. 创建数据库连接时用到的 Class.forName("com.mysql.jdbc.Driver);

jvm加载类的过程

  1. java文件会经过编译器编译成 .class 文件
  2. JVM 加载 .class 文件,会在内存中生成代表某个对象的Class 类,就如上面写的Class.forName("com.mysql.jdbc.Driver);  他的返回值就是一个Class 对象。一个类只会有一个Class对象。
  3. 加载结束,会进行类的一些初始化操作。

Class 类

  • 所以要生成对象,首先要有Class类

Class 类的构造函数是私有的,所以我们没有办法直接创建一个Class,不能直接new 一个Class 那就只能通过其他方式获取到一个Class。

  1. 前面说过,Class.forName 会返回一个Class。
  2. Object 类中有getClass() 方法
    public final native Class<?> getClass();
  3. 直接通过静态变量获取
Class a = int.class;
Class<Test> testClass = Test.class;

有了Class类,我们就可以对对象进行一些列的操作。

实现反射

//1.获取student的Class类Class<?> stuClazz = Class.forName("reflect.Student");
// 2.对student初始化,new 一个student出来。
Student student = (Student)stuClazz.newInstance();
//3.获取方法名Method setName = stuClazz.getMethod("setName", String.class);
//4.给student设置name 为 lant
setName.invoke(student,"lant");
//5.输出  Student{name='lant', age=0, addr='null'}
System.out.println(student.toString());// 对于私有方法,不能使用getMethod直接调用会抛出NoSuchMethodException 异常
Method setAddr = stuClazz.getMethod("setAddr", String.class);
// 需要使用getDeclaredMethod方法进行调用。 这时又会抛出 IllegalAccessException,Student with modifiers "private" 异常
Method setAddr = stuClazz.getDeclaredMethod("setAddr", String.class);
//需要再代码里面设置权限为true,就可以对私有方法进行访问,
Student{name='lant', age=0, addr='Nanjing'}
setAddr.setAccessible(true);
setAddr.invoke(student,"Nanjing");
System.out.println(student.toString());

以上分别实现了公共方法和私有方法的调用,对于静态方法,可以省略  2  instance的步骤,

反射过程无法执行某些虚拟机优化,所有性能开销会大, 并且通过反射会使类的内部曝光,访问到类的私有属性, 也能给一个 int 类型的数组塞入一个字符串, 会使代码存在一定的安全性。

代理

代理,就是将自己要做的事情,交给别人去做。

代理分为静态代理和动态代理。

静态代理

public interface IStudentService {    public String getUserName();
} public class StudentImpl implements IStudentService{   @Overridepublic String getUserName() {return "StudentImpl";}
} public class StudentProxy implements IStudentService{private IStudentService service;public StudentProxy(IStudentService service) {this.service = service;}@Overridepublic String getUserName() {return "StudentProxy";}
}
public static void main(String[] args) throws  Exception { IStudentService service = new StudentImpl();StudentProxy studentProxy = new StudentProxy(service);System.out.println(studentProxy.getUserName());  //StudentProxy
}

如上,在StudentProxy实现了代理,但是因为他是要实现service接口的,所以他必须要重新里面所有的方法,这种方式不易维护代码,但是可以保护目标对象,可以在代理类中进行功能拓展。

jdk动态代理。

动态代理需要实现 InvocationHandler 接口,并重写里面的invoke方法。通过 handler 实现对所有的方法进行统一管理。在invoke方法里面,可以对原有的方法进行拓展。

public class StudentJdkHandler implements InvocationHandler {private Object obj;public StudentJdkHandler(Object object) {this.obj = object;}/**** @param object   代理的真是对象,* @param method   需要调用的方法* @param args     调用方法的入参* @return* @throws Throwable*/@Overridepublic Object invoke(Object object, Method method, Object[] args) throws Throwable {return method.invoke(obj, args);}
} public class Test {public static void main(String[] args) {IStudentService service = new StudentImpl();StudentJdkHandler studentJdkHandler = new StudentJdkHandler(service);/*** newProxyInstance  三个参数,*ClassLoader loader      指定由哪个Classloader对象来机型代理对象*Class<?>[] interfaces,  一个interface对象的数组,将这组接口提供之后,就可以代理实现这些接口*InvocationHandler h    表示调用方法的时候,需要关联到哪个handler上面去**/IStudentService proxyInstance =
(IStudentService)Proxy.newProxyInstance(studentJdkHandler.getClass().getClassLoader(),service.getClass().getInterfaces(),studentJdkHandler);System.out.println(proxyInstance.getUserName());}
}

jdk动态代理是通过反射完成的。jdk动态代理是通过实现接口来实现的,也就是说动态代理的对象必须实现一个或者多个接口。只是我们不需要重写所有的接口了。

cglib动态代理

jdk动态代理是通过实现接口实现的,cglib是通过继承来实现代理的。

public class People {    private String name;public String say(){return "people";}
}
public class CgProxy implements MethodInterceptor {    private Object target;public CgProxy(Object target) {this.target = target;}// 通过cglib 里面的工具类 创建代理对象public Object getInstance(Object target) {Enhancer enhancer= new Enhancer();// 设置父类为需要代理的类enhancer.setSuperclass(target.getClass());// 设置回调,  在CgProxy执行这行代码的时候,将this设置为回调对象,  在这测试代码里就是Student, 在后续  执行的过程中会回调 CgProxy的intercept 方法enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object invoke = method.invoke(target, objects);return invoke;}
}public class Test {    public static void main(String[] args) {People people = new People();CgProxy proxy = new CgProxy(people);// 可以理解为 父类 A = new 子类()People peopleInstance = (People) proxy.getInstance(people);System.out.println(peopleInstance.say());}
}

因为cglib是通过继承实现的,所有如果类是无法被继承的,那就没有办法使用cglib,cglib的性能更高一点。

Mybatis中的Mapper实现

  • 使用mapper层的时候,我们并不需要实现mapper接口,而是通过绑定 xml 文件实现数据库操作。所以,mapper的实现不是基于cglib实现的。
  • 静态代理需要实现接口中的所有方法,显然也不会采用静态代理的方式进行实现。
  • 看下是不是jdk动态代理方式

调试代码,找到getMapper方法,一层层执行进来

进来之后,可以看到 getMapper的执行方式

可以看到这里调用了newInstance方法,

进来之后发现,MapperProxy 又执行了instance方法,

查看MapperProxy 后,发现他实现了InvocationHandler接口,并重新了invoke方法。

在invoke方法里面,调用了execute方法

进来之后发现,这里面就是对应了 xml 里面的标签,在这里对sql进行了操作。

可以看到这里是调用了SqlSession的方法,继续向下,发现是DefaultSqlSession  实现了SqlSession,通过这里实现了对数据的操作。

但是还有一个问题,有这么多mapper,到底执行哪个mapper?

再回来看刚才getMapper的方法,这个knownMappers 是一个HashMap, 那就会有初始化的地方,

往下看,这里有一个addMapper的方法,就是从这里初始化的。

从这个addMapper方法一步步向上找,

在  package org.apache.ibatis.builder.xml; 里面,找到了对addMapper的操作,这里调用了Class.forName,参数就是namespace, 也就是我们在 xml 里面写的那个namespace。

在这里进行了初始化之后,将所有的mapper放到knownMappers,key就是 mapper的class,

所以在最初执行getmapper的时候,传入的实际上是一个Class,然后再从knownMappers get出来。获取到对应的mapper,然后再执行invoke

参考内容:

https://zhuanlan.zhihu.com/p/133619880

https://segmentfault.com/a/1190000011291179

https://www.cnblogs.com/hanganglin/p/4485999.html

反射和代理以及Mapper中的应用相关推荐

  1. Java程序员从笨鸟到菜鸟之(八)反射和代理机制

    反射和代理机制是JDK5.0提供的Java新特性,反射的出现打破了java一些常规的规则,如,私有变量不可访问.但反射和代理在学习过程中也是一个比较难理解的知识点.本人曾经学过一段时间的反射和代理,但 ...

  2. java 反射 动态代理

    在上一篇文章中介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大 ...

  3. Android 反射、代理调用系统隐藏API方法与接口类连接Wi-Fi

    本文转载自:http://www.xwdoor.net/android-reflection-proxy-call-system-hidden-api-method-and-interface-cla ...

  4. 反射和代理的具体应用

    原文发布于 github.com/ta7sudan/no-, 如需转载请保留原作者 @ta7sudan. ES6 为我们提供了许多新的 API, 其中个人觉得最有用的(之一)便是代理了. 代理和反射都 ...

  5. java反射学习(2):反射与代理模式

    一 基本代理设计模式 代理模式核心思路,一个接口有两个子类,一个子类完成业务需求,另一个完成辅助功能 假设实现一个功能,张三吃饭 代码如下: 接口 PersonDao.java package com ...

  6. 【java】深入分析Java反射-动态代理 proxy

    1.概述 转载:深入分析Java反射(四)-动态代理 [Java]Java 反射机制浅析 [Java]java代理 静态代理 动态代理 proxy [java]静态代理 proxy 2.动态代理的简介 ...

  7. Java基础(三)——反射、代理

    文章目录 反射.代理 1 反射 2 静态代理 3 动态代理 反射.代理 1 反射 反射机制可以用于动态操作Java代码,能够知道任意一个类的属性和方法,能够调用任意一个对象的属性和方法 优点:运行时根 ...

  8. java反射-动态代理

    转自:https://www.cnblogs.com/throwable/p/12272269.html 动态代理的简介# Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单 ...

  9. 反射+动态代理+枚举

    一.反射 1.类的加载的概述 A:类的加载概述当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化.加载 就是指将.class文件读入内存,并为 ...

最新文章

  1. Redis大集群扩容性能优化实践
  2. 在阿里云里申请免费Https证书SSL
  3. 学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK
  4. centos php支持yaf,CentOS环境下给PHP7.0安装yaf扩展
  5. HDL的综合和c语言的编译区别,C语言与verilog 的区别及相互转化
  6. 使用python读取iphone文件_如何在连接的iPhone上用Python从windows7访问照片?
  7. 【registry】registry An exception was thrown while processing request with message
  8. mysql sql语句集合
  9. js混淆还原工具_混淆过的js代码如何还原?
  10. 华为S9303三层交换机一次配置经历和心得
  11. 六神不安,生死命悬一线
  12. 关于Teigha的使用记录
  13. 阿里云服务器能抗ddos攻击吗
  14. Flutter实战之go_router路由组件入门指南
  15. aspose实现word,excel在线预览
  16. 1379690-01-3,3-Azido-D-alanine HCl结构式分享
  17. PHP7有哪些新特性
  18. 将Outlook中的邮件保存到本地磁盘,释放邮箱空间
  19. Java程序员进阶必知的分布式系统专业术语分析
  20. linux shell 获取环境变量,bash shell中的环境变量

热门文章

  1. Mysql存储过程-新增数据时多条件判定是否已经存在某值
  2. C++定时器实现定时任务
  3. 广州市数字经济行业市场“十四五”发展趋势及经营模式分析报告2022-2027年
  4. mysql数据库怎么冷备份恢复_MySQL数据库的备份与恢复
  5. 区块链发展面临三大挑战
  6. (附源码)springboot校园兼职系统 毕业设计 463450
  7. 2007版Excel创建的数据透视表并不能在2003版中使用
  8. 包含第k元素LIS(dp)
  9. python唐诗分析综合_Python爬虫抓取唐诗宋词
  10. 单词统计(哎呦我去!)