前言
动态代理模式,也是Java中常见的一种设计模式,其中动态代理模式,也是用到了反射,所以我们反射和动态代理一起学一学
一、了解反射
反射看名字很高大上哈,其实搞明白了也很简单,反射之中包括了一个反字,所以了解反射我们先从正开始,正常情况下呢,我们调用一个类来做方法的时候,是从main方法里声明一个,然后再实例化这个类,然后再调用里面的方法
public class RefleDemo {
public static void main(String[] args) {
//实例化对象的标准用法,也就是所谓的正
Servant servant=new Servant();
servant.service("Hello");
}
}
反射则是一开始并不知道我要初始化对象它是什么类,自然也就无法使用new 关键字来创建对象了,这时候,我们使用JDK提供的反射API进行反射调用,这时候小伙伴们可能就有疑问,什么情况下,我们不知道我们要初始化的类是什么呢?
各种框架,比如说我们Android中使用了动态代理的网络通讯的框架Retrofit
Class servantRefle=Class.forName("rfle.Servant");
Servant servantClass= (Servant) servantRefle.newInstance();
servantClass.service("hello");
反射就是在运行的时候才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法
反射是Java被视为动态语言的关键,根据反射我们可以根据类信息,操作类中的所有方法和属性
二、继续了解反射(哈哈哈)
1. 每一个类对应的class放在哪里?
大家都知道,Java语音是一个面向对象的语言,在面向对象的世界里,万事万物皆对象,那我们写的class类是不是对象呢,是的,他们都是 java.lang.Class 的对象
我们写的每一个类,都是这个Class它的一个对象,我们在写类的时候,并没有显式的写这个对象,那么它放在哪里了呢,我们都知道,我们运行的时候,我们写的类会编译成一个类,生成一个class文件,而编译器就把Class的这个对象存放在class文件的末尾,里面保存了类的元数据信息,这些元数据信息都包括什么呢?
``
2. 这个class里面都保存了什么
保存了类的所有信息,比如它是类还是接口 集成 和实现了那些类和接口,有什么属性 ,有什么方法
我们在new一个对象的时候,可以new 很多对象,但是这个类生成的class对象只能有一个(在不同的类加载器,可能有多个,这里涉及到虚拟机的知识了,emmm…)
我们在实例化Servant这个的类对象的时候,虚拟机会去检查,在虚拟机里面,这个类有没有被加载过,如果没有,虚拟机会先加载Servant对应的这个class对象,加载完之后,才会轮到Servant实例化本身的对象
3. 如何使用?
我们怎么获取这个Class 对象呢?
我们有好几种方法
- 通过类名获取
Class servantClass1=Servant.class;
- 通过实例来获取
Servant servant=new Servant();
servant.service("Hello");
Class servantClass2=servant.getClass();
- 通过这个类的全限定名称 (包名+这个类的类名)Class.forName来获取
Class servantRefle=Class.forName("rfle.Servant");
前面两种获取Class 的方式,都得需要确切的知道Servant是存在的有的,对于各种框架来说,它们常用的是第三种方式来获取Class 对象
获取了这个Class 对象,我们就可以做很多很多事情,能做什么呢
比如获取 Servant的实例,
Class servantRefle=Class.forName("rfle.Servant");
Servant servantClass= (Servant) servantRefle.newInstance();
servantClass.service("hello");
但是单单获取实例远远不够滴,
3.1 获取类加载器
package rfle;
public class TestClassLoader {
public static void testClassLoader() throws ClassNotFoundException {
//1、获取一个系统的类加载器(可以获取,当前这个类就是它加载的)
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2丶获取系统类加载器的父类加载器(扩展加载器,可以获取)
classLoader=classLoader.getParent();
System.out.println(classLoader);
//3丶获取扩展类加载器(引导类加载器,不可获取)
classLoader=classLoader.getParent();
System.out.println(classLoader);
//4、测试当前类是那个类加载器加载的(系统加载器)
classLoader=Class.forName("rfle.RelectionTest").getClassLoader();
System.out.println(classLoader);
//5、测试JDK提供的Object类 由那个类加载器加载的(引导类)
classLoader=Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
}
}
package rfle;
public class RelectionTest {
public static void main(String[] args) throws ClassNotFoundException {
TestClassLoader.testClassLoader();
}
}
运行结果
3.2 获取构造器对象
这是一个Person类
public class Person {
String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("this is setName");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
System.out.println("this is setAge");
}
}
/***
* 包含一个带参的构造方法和不带参的构造方法
* @param name
* @param age
*/
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
//私有方法
private void privateMethod(){
System.out.println("这是一个私有方法");
}
}
public static void testConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
String className="rfle.Person";
Class<Person> clazz= (Class<Person>) Class.forName(className);
System.out.println("获取全部Constructor对象-----");
Constructor<Person>[] constructors= (Constructor<Person>[]) clazz.getConstructors();
for (Constructor<Person> constructor:constructors) {
System.out.println(constructor);
}
System.out.println("获取某一个Constructor对象 需要参数列表----");
Constructor<Person> constructor= clazz.getConstructor(String.class,int.class);
System.out.println(constructor);
System.out.println("调用Constructor的newInstance方法创建对象----");
Person person1= constructor.newInstance("赵Sir",18);
System.out.println(person1.getName());
}
运行结果
这里需要说一下因为我们的构造方法的参数类型是int型的,所以我们再获取构造器的时候传入的参数一定是int.class 而不能是Integer.class,不然会报没有找到方法异常
3.3 获取方法并执行相对应的方法
public class TestMethod {
/**
* 方法相关
*/
public void testMethod() throws Exception{
String className="rfle.Person";
Class clazz=Class.forName(className);
System.out.println("获取clazz对应类中的所有方法," +
"不能获取private方法,且获取从父类继承来的所有方法");
Method[] methods= clazz.getMethods();
for (Method method:methods) {
System.out.println(method.getName()+"()");
}
System.out.println("__________________________________");
System.out.println("获取所有方法,包括私有方法" +
"所有声明的方法,且获取当前类方法");
methods= clazz.getDeclaredMethods();
for (Method method:methods) {
System.out.println(method.getName()+"()");
}
System.out.println("__________________________________");
System.out.println("获取指定方法,和获取构造器的差不多,需要方法名称 和参数列表 无参则不写");
Method method=clazz.getDeclaredMethod("setName",String.class);
System.out.println(method);
method=clazz.getDeclaredMethod("setAge",int.class);
System.out.println(method);
System.out.println("__________________________________");
System.out.println("执行我们获取的方法");
Object object=clazz.newInstance();
//第一个参数 这个方法所在类的实例,可变参数 参数列表
method.invoke(object,18);
System.out.println("__________________________________");
System.out.println("执行私有方法—");
method=clazz.getDeclaredMethod("privateMethod");
//在执行私有方法之前 一定要 执行这句代码。把Accessible设成true
method.setAccessible(true);
method.invoke(object);
}
}
运行结果
执行私有方法的时候一定要注意,一定要在执行之前,将Accessible设成true,不仅是方法,访问私有域的时候也得设置一下
3.4 通过反射访问成员变量
public class TestFiled {
/***
* 域相关
*/
public void testFiled() throws Exception{
String className="rfle.Person";
Class clazz=Class.forName(className);
System.out.println("获取共有和私有的所有字段,但不能获取父类字段");
Field[] fields= clazz.getDeclaredFields();
for (Field field:fields) {
System.out.println(field.getName());
}
System.out.println("__________________________________");
System.out.println("获取指定字段");
Field field=clazz.getDeclaredField("name");
System.out.println(field.getName());
System.out.println("__________________________________");
System.out.println("获取指定字段的值");
Person person=new Person("铭儿",18);
//第一个参数 这个方法所在类的实例
Object object= field.get(person);
System.out.println(field.getName()+"="+object);
System.out.println("__________________________________");
System.out.println("设置指定对象的值");
field.set(person,"小铭同学");
System.out.println(field.getName()+"="+person.getName());
//访问私有字段
field=clazz.getDeclaredField("age");
field.setAccessible(true);
field.get(person);
field.set(person,20);
System.out.println(field.getName()+"="+person.getAge());
}
}
运行结果
反射很强大,它很灵活,可以突破权限修饰符直接进行访问,所以使用的时候一定要小心
因为反射是在运行的时候,临时创建的类,在调用的效率上肯定比我们new的时候要慢
三、动态代理模式
1.静态代理模式
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
目的:
- 通过引入代理对象方式,来间接的访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性
- 通过代理对象对原有的业务增强
通俗点讲所谓的代理对象就相当于,我们日常生活中的中介
用品公司,就是目标对象,mark代购就是代理对象,张三 就是访问着,代理不单单提供工厂的售卖服务,还提供售前咨询,售后服务等等,但是用代码怎么实现呢
目标对象和 代理对象都要实现一个共同的接口服务
代理对象 包含目标对象
我们用代码实现一下这种模式
/***
* 代理对象 和工厂共同的业务接口
*/
public interface ManToolsFactory {
void saleManTools(String size);
}
/***
* 目标对象 工厂
*/
public class AaFactory implements ManToolsFactory{
@Override
public void saleManTools(String size) {
System.out.println("按需求定制了一个size为"+size+"的女model");
}
}
/***
* 代理对象 mark
*/
public class Mark implements ManToolsFactory {
public Mark(ManToolsFactory manToolsFactory) {
this.manToolsFactory = manToolsFactory;
}
public ManToolsFactory manToolsFactory;
@Override
public void saleManTools(String size) {
doSthBefore();
manToolsFactory.saleManTools(size);
doSthAfter();
}
/***
* 后置处理器
*/
private void doSthAfter(){
System.out.println("精美包装,快递一条龙服务");
}
/***
* 前置处理器
*/
private void doSthBefore(){
System.out.println("根据需求进行市场调研和产品分析");
}
}
/***
*张三
*/
public class Client {
public static void main(String[] args) {
//静态代理模式
ManToolsFactory factory=new AaFactory();
Mark mark=new Mark(factory);
mark.saleManTools("D");
}
}
这样的话,张三和工厂没有任何联系,而是通过mark这个代理买来了要给size是D的女model
这就是静态代理模式,因为真实对象,和代理对象是一对一存在的
静态代理模式有什么缺点呢
违反开闭原则:扩展能力差,可维护性差
2. 动态代理模式
业务扩展了,多了一个张三的老婆 ,张三和张三的老婆要求肯定是不一样的,都是好朋友 不都得帮忙
他们的需求不一样,人也不一样,怎么办呢,如果用静态代理维护,要么Mark再添加一个业务,女人用品的服务
要么把他推荐给更懂女人的Alvin
public class Alvin implements WomanToolsFactory {
public WomanToolsFactory womanToolsFactory;
@Override
public void saleWomenTools(float length) {
womanToolsFactory.saleWomenTools(length);
}
/***
* 后置处理器
*/
private void doSthAfter(){
System.out.println("精美包装,快递一条龙服务");
}
/***
* 前置处理器
*/
private void doSthBefore(){
System.out.println("根据需求进行市场调研和产品分析");
}
}
按同样的流程走一遍,如果业务全包给mark,每次增加一个业务是不是都得更改mark,这就违反了开闭原则(对修改关闭,对新增开放)
这个海外代购公司,就是动态的生成代理,张三来了就安排mark去接待,他老婆来了就派Avlin去接待
/***
* Mark代购公司
*/
public class MarkCompany implements InvocationHandler {
/***
* 持有真实对象
*/
private Object factory;
public Object getFactory() {
return factory;
}
public void setFactory(Object factory) {
this.factory = factory;
}
/***
* 通过Proxy获取动态代理对象
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),this);
}
/***
* 通过动态代理对象方法进行增强
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSthBefore();
Object result =method.invoke(factory,args);
doSthAfter();
return result;
}
/***
* 后置处理器
*/
private void doSthAfter(){
System.out.println("精美包装,快递一条龙服务");
}
/***
* 前置处理器
*/
private void doSthBefore(){
System.out.println("根据需求进行市场调研和产品分析");
}
}
public class Client {
public static void main(String[] args) {
//静态代理模式
ManToolsFactory factory=new AaFactory();
Mark mark=new Mark(factory);
mark.saleManTools("D");
//动态代理模式
ManToolsFactory aaToolsFactory=new AaFactory();
MarkCompany markCompany=new MarkCompany();
markCompany.setFactory(aaToolsFactory);
//张三来了
ManToolsFactory emploee1= (ManToolsFactory) markCompany.getProxyInstance();
emploee1.saleManTools("E");
//张三老婆来了
WomanToolsFactory bbToolsFactory=new BbFactory();
markCompany.setFactory(bbToolsFactory);
WomanToolsFactory emploee2= (WomanToolsFactory) markCompany.getProxyInstance();
emploee2.saleWomenTools(1.8f);
//我们再编码过程中没有声明任何代理类就完成了两次 海外代购
}
}
这样就可以动态的生成代理类了,无需再去声明Mark和Alvin了,同样的完成了海外代购服务
3.Proxy是怎么帮助我们生成的代理类的呢?
我们debug的时候看一下啊 emploee的信息
在后边的类信息里,emplyee1 是、$Proxy0 这个就是jdk为我们生成的代理对象,$Proxy0 是怎么生成的呢,了解这一个问题之前 先了解一下类的生命周期
看我们在生成动态代理的时候,是没有走编写java源文件这一步的,jdk是怎么实现的呢
在Proxy的newProxyInstance方法里
看我画圈圈的地方Class Class什么,前面我们说了是每个类的对应的class对象,这个肯定是有用的,然后接着往下看
然后拿到了这个类的构造方法,返回了 cl对应类的一个实例,然后我们继续去看这个cl是怎么生成的去看getProxyClass0这个方法
看到这个代码,发现了一个规矩好像,在实现接口的时候,接口数不能超过65535,字节码里有这个存储接口数量的正好占四个字节,刚好不能超过这个数
然后我们再看return 看变量名proxyClassCache,说明jdk在生成动态代理的时候是做了缓存的,既然是有缓存,第一次生成的的时候肯定不存在啊,那我们就接着去看get的实现,在get方法里我们发现真正创建代理类的方法是这个方法
接着往下走去看apply,我们会发现 apply是一个泛型接口,
我们goto impl正好发现了一个实现类
跟着这个思路走,去看这个实现类里这个方法,我们好像又发现了点什么
它吧我们的代理类生成了字节码然后存在了byte里了,我们再看看proxyname
哎呀,正好和我们debug出来的一样,后边加了个 long 进行cas自增的一个num.,为了验证是不是递增的我再debug一下把emplee2也看一下
ProxyGenerator.generateProxyClass()这个方法就是 传进去的class信息,然后根据class信息,去实现 这个接口的实现类的字节码,然后通过defineClass0这个方法返回一个class
但是我们点进去 发现是一个native方法,那我们想个办法 看一看这个生成的代理类到底是啥,我们把生成的byte数组写到一个文件里,然后再用反编译工具 反编译一下看看 到底是个什么东西
public class ProxyUtils {
public static void generateClassFile(Class clazz,String proxyName){
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, new Class[]{
clazz});
String paths=clazz.getResource(".").getPath();
FileOutputStream out=null;
try {
out=new FileOutputStream(paths+proxyName+".class");
out.write(proxyClassFile);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
调用
//动态代理模式
ManToolsFactory aaToolsFactory=new AaFactory();
MarkCompany markCompany=new MarkCompany();
markCompany.setFactory(aaToolsFactory);
//张三来了
ManToolsFactory emploee1= (ManToolsFactory) markCompany.getProxyInstance();
emploee1.saleManTools("E");
//张三老婆来了
WomanToolsFactory bbToolsFactory=new BbFactory();
markCompany.setFactory(bbToolsFactory);
WomanToolsFactory emploee2= (WomanToolsFactory) markCompany.getProxyInstance();
emploee2.saleWomenTools(1.8f);
ProxyUtils.generateClassFile(aaToolsFactory.getClass(),emploee1.getClass().getSimpleName());
ProxyUtils.generateClassFile(bbToolsFactory.getClass(),emploee2.getClass().getSimpleName());
反编译之后的结果
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.AaFactory;
public final class $Proxy0 extends Proxy implements AaFactory {
private static Method m1;
private static Method m8;
private static Method m3;
private static Method m2;
private static Method m6;
private static Method m5;
private static Method m7;
private static Method m9;
private static Method m0;
private static Method m4;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{
var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void notify() throws {
try {
super.h.invoke(this, m8, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void saleManTools(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{
var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void wait(long var1) throws InterruptedException {
try {
super.h.invoke(this, m6, new Object[]{
var1});
} catch (RuntimeException | InterruptedException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final void wait(long var1, int var3) throws InterruptedException {
try {
super.h.invoke(this, m5, new Object[]{
var1, var3});
} catch (RuntimeException | InterruptedException | Error var5) {
throw var5;
} catch (Throwable var6) {
throw new UndeclaredThrowableException(var6);
}
}
public final Class getClass() throws {
try {
return (Class)super.h.invoke(this, m7, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void notifyAll() throws {
try {
super.h.invoke(this, m9, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void wait() throws InterruptedException {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | InterruptedException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m8 = Class.forName("proxy.AaFactory").getMethod("notify");
m3 = Class.forName("proxy.AaFactory").getMethod("saleManTools", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m6 = Class.forName("proxy.AaFactory").getMethod("wait", Long.TYPE);
m5 = Class.forName("proxy.AaFactory").getMethod("wait", Long.TYPE, Integer.TYPE);
m7 = Class.forName("proxy.AaFactory").getMethod("getClass");
m9 = Class.forName("proxy.AaFactory").getMethod("notifyAll");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m4 = Class.forName("proxy.AaFactory").getMethod("wait");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
所有的代理类都继承了 Proxy,看反编译结果 是不是实现了我们的业务接口AaFactory,是不是满足了我们前面所说的不管是代理对象还是真实对象,都得共同实现一个业务接口,接着往下看
我们看到了一个熟悉的方法saleManTools(),里面执行了一个
h.invoke(this, m3, new Object[]{var1});
h是谁呢
这个文件里没有,肯定是再父类里面,我们去找找
我去,这个h不就是我们在写动态代理传进去的InvocationHandler么,它的invoke方法不就是
this,就是代理对象了,m3是啥?我们继续找m3
看到这里可能,有同学会懵逼,我第一遍也懵来着,总结一下
1、MarkCompany 实现了 InvocationHandler
2丶调用MarkCompany getProxyInstance的时候将业务接口的Class信息传给Proxy.newProxyInstance()
3、newProxyInstance利用反射生成一个 $Proxy+number的一个类
4,newProxyInstance,生成一个代理类的实例 将InvocationHandler 也就是MarkCompany传进去
5、调用这个代理类的实例的 saleManTools方法 ,也就调用了InvocationHandler 也就是MarkCompany 的invoke方法,完成了代理的对象方法的增强
总结
上边就是反射和动态代理的相关知识,从源码入手分析了一下Jdk动态代理对象是怎么生成的,写的有什么不对的请大佬们多多赐教!希望大佬们一键三连!
转载:https://blog.csdn.net/amszlk/article/details/108917554