动态代理
咱们来聊聊动态代理,大家最开始听到这个词的时候肯定很懵,我也是这样的。同时大家脑海中肯定也会想到是不是会有静态代理呢?答案是肯定的。想要对动态代理了解透彻,首先需要先了解什么是代理。
1.代理
听到代理这个词,相信大家在心中知道了这个的词的作用与含义。在日常生活中大家肯定见过或者说知道这么些个场景。例如:大家去租房或者说买房,是不是有房屋中介会联系到我们。我们向中介诉说我们的需求,而我们只需要等待,在等待期间中介会根据我们的需求去找到匹配房源,然后向我们推荐。
在上面的场景中,房屋中介代替我们去做了寻找房源的事情,我们可以称房屋中介为代理。
类似的场景还有:商品代购、经纪人等等。
在讲解静态代理和动态之前,咱们先来了解下代理模式。
2.代理模式
代理模式是一种设计模式,它为其他对象提供一个代理以控制对这个对象的访问。在代理模式中,代理对象(Proxy)代表了目标对象(Real Subject),并提供了与目标对象相同的接口。客户端可以通过代理对象间接调用目标对象的方法,而代理对象可以在调用前后增加额外的操作,比如权限检查、日志记录、缓存处理、延迟加载等。
相信有读者看完这段话还是不能理解,没关系。下面咱们用代码实现代理看看,读者后面再来看看这段话相信就有自己的理解了。
3. 静态代理
在先讲解动态代理之前,咱们先讲解什么是静态代理。
定义接口
点击查看代码
package com.demo.proxy;
/**
* 接口
*/
public interface Star {
String sing(String name);
String dance(String dance);
}
定义被代理类
点击查看代码
package com.demo.proxy;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 被代理类
*/
@Data
@AllArgsConstructor
public class BigStar implements Star{
private String name;
@Override
public String sing(String song) {
System.out.println(this.name+"正在唱歌:"+song);
return "谢谢你们喜欢我的歌";
}
@Override
public String dance(String dances){
System.out.println(this.name+"正在跳舞:"+dances);
return "谢谢你们喜欢我的舞";
}
}
定义代理类
点击查看代码
package com.demo.proxy;
/**
* 代理类
*/
public class StaticProxy implements Star{
private BigStar bigStar;
public StaticProxy() {
}
public StaticProxy(BigStar bigStar) {
this.bigStar = bigStar;
}
@Override
public String sing(String name) {
/*代理对象增强的部分*/
System.out.println(System.currentTimeMillis());
/*被代理对象调用方法*/
String s = bigStar.sing(name);
/*代理对象增强的部分*/
System.out.println(System.currentTimeMillis());
return s;
}
@Override
public String dance(String dance) {
return null;
}
}
我们来回顾下静态代理的实现:
(1)代理类是自己手工实现的,自己创建一个Java类,表示代理类。
(2)同时你所要代理的目标是确定的。
总结来说静态代理:目标类和代理类实现了相同的接口,在代理类中依赖了目标类,代理类的方法中调用了目标类的方法,并做了一些增强性的工作。
同时也可以发现静态代理的缺点:
(1)当目标类增加了,代理类可能也需要成倍的增加,代理类数量过多。
(2)当你的接口中功能增加了,或者修改了,会影响众多的实现类,厂家类,代理都需要修改,影响比较多。
4.动态代理
动态代理指的是:在程序的执行过程中,使用jdk的反射机制,创建代理对象,并动态的指定代理的目标类。
动态代理的实现方式常用有两种:
- 使用JDK代理。
- 通过CDLIB代理。
在本文着重讲解JDK动态代理,jdk动态代理中最重要的两个分别是Proxy类和InvocationHandler接口。(要求目标对象必须实现接口)
Method
当你有一个 Method 对象,代表了某个类的一个具体方法时,你可以通过调用 invoke() 方法来执行该方法。invoke() 方法接受两个参数:
object:这是要执行方法的对象实例。对于静态方法,这个参数可以是 null。
args:这是一个可变参数列表,包含了传递给目标方法的实际参数值。如果方法没有参数,可以传入一个空数组(new Object[0])。
Proxy类的newProxyInstance方法
点击查看代码
/*参数1:用于指定类加载器 去加载生成的代理类 写法固定:一般用当前类的类加载器
参数2:用于指定生成的代理长成什么样子,也就是有哪些方法
参数3:用于指定生成的代理对象 做什么事情InvocationHandler 是个接口*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
InvocationHandler接口的invoke方法
点击查看代码
// Object proxy:jdk创建的代理对象,无需赋值
// Method method:目标类中的方法
// Object[] args:目标类中方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
动态代理步骤
创建接口,定义目标类要完成的功能
点击查看代码
package com.demo.proxy;
/**
* 接口
*/
public interface Star {
String sing(String name);
String dance(String dance);
}
创建目标类,实现该接口
点击查看代码
package com.demo.proxy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 被代理类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BigStar implements Star{
private String name;
@Override
public String sing(String song) {
System.out.println(this.name+"正在唱歌:"+song);
return "谢谢你们喜欢我的歌";
}
@Override
public String dance(String dances){
System.out.println(this.name+"正在跳舞:"+dances);
return "谢谢你们喜欢我的舞";
}
}
创建 InvocationHandler 接口的实现类,在invoke()方法中完成代理类的功能
点击查看代码
package com.demo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
//动态代理,目标对象是活动的,不是固定的,需要传入进来
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override //回调方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*代理对象做的事 会在这里写代码*/
if(method.getName().equals("sing")){
System.out.println("收钱,20w");
//return method.invoke(star,args);
}else if(method.getName().equals("dance")){
System.out.println("收钱,10w");
//return method.invoke(star,args);
}
return method.invoke(target,args);
}
}
测试
动态代理执行流程
望各位大佬指正补充。