Java篇之Java动态代理

Java动态代理

害前段时间应付该死的学校考试,差不多一个多月的时间没咋学技术吧,就很烦,不过好在结果是好的嘛,现在总算是考完了,可以回家放松放松顺便学学技术了;复习考试之前的那段时间一直在学Java安全,差不多把cc1的链子看完了,确实挺难的,有好多好多要讲到的小东西,接下来就先来总结动态代理吧,相信把好多知识点掰细了它也就不难了

Java代理模式

代理是什么?其实我们生活中也有许多代理的例子,比如说房东要将房子出售,于是到房地产中介公司找一个中介(代理),由他来帮房东完成销售房屋,签订合同、网签、贷款过户等等事宜,而买家也是和这个中介进行谈判的;也就是说,代理本身是不会真正实现具体的服务的,它做的只是一个搭桥的工作

而在Java中,我们首先是需要一个被代理的接口,以及代理类(Proxy)和真正的实现类(Real),它们都会去同时继承这个接口,但是用户只能接触到代理类;在用户调用代理类时,代理类在内部调用了实现类,也可以实现一些其它的操作,然后将结果返回给了用户;代理又分为静态代理和动态代理两种,接下来就来看看这两种,当然重点肯定是动态代理哈

image.png

静态代理

静态代理就是代理类是之前就已经写好了的,来举个例子,先来个接口flag

1
2
3
public interface flag {
void getflag();
}

然后写它的实现类getflag,并且继承这个接口:

1
2
3
4
5
6
public class getflag implements flag {
@Override
public void getflag() {
System.out.println("Give you flag: flag{wllm_yyds}");
}
}

然后就是代理类Proxy,它同样会继承flag接口,并且它拥有实现类的对象,代为执行里面的方法,就是因为这种间接性,可以附加多种用途,比如说在调用的前后都可以加入一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Proxy implements flag{
flag flag;
public Proxy(flag flag)
{
this.flag=flag;
}

@Override
public void getflag() {
System.out.println("这是你的flag");
flag.getflag();
System.out.println("请拿好");
}
}

最后我们就来创建主类了,在主类中我们先创建一个实现类的对象,然后交给代理类,让代理类来执行里面的方法:

1
2
3
4
5
6
7
public class test11 {
public static void main(String[] args) {
flag flag = new getflag();
Proxy p = new Proxy(flag);
p.getflag();
}
}

image.png

这就是所谓的静态代理,我们可以看到,通过代理,我们可以拓展一些新的功能,而且防止了用户直接操作目标对象,提升了安全性;但是缺点也很明显,由于代理类是之前就写好了的,很固定,所以说降低了灵活性;而且代理类和实现类还需要共同实现一个接口或者说共同继承某个类,当类很多时这就很不方便,而且一旦改变了接口的内容,代理类和实现类都需要发生变化

动态代理

静态代理的实现类是由程序员手写的,而动态代理则不同,动态代理最神奇的地方就是不需要程序员来手写实现类了,它可以在运行期动态创建某个接口的实例,而且一个动态代理类可以为多个不同的接口实现代理;这种没有实现类但是却在运行的时候动态创建接口对象的方式,我们称为动态代码;而JDK提供的动态创建接口对象的方式,我们就称为动态代理:

接口和还是和上面一样,这里为了演示动态代理我们多添加一个接口flag1,一个有参数一个没参数吧,方便看:

1
2
3
4
5
6
public interface flag {
void getflag(String flag);
}
public interface flag1 {
void getflag1();
}

动态代理类肯定需要实现InvocationHandler接口的,然后里面只用重写一个invoke方法就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class realproxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.toString().contains("java.lang.String")){
System.out.println("方法名为: "+method);
System.out.println("参数为: "+args[0]);
}
else{
System.out.println("方法名为: "+method);
System.out.println("没有传入参数");
}
return null;
}
}

其实这动态代理类的逻辑是很简单的,这个类中的invoke方法里面包含着你代理的具体逻辑;它会获取到你想要执行的方法名,以及传入的参数,相当于你想让它执行的任何操作都可以在代码中体现;下面我们在主函数中利用Proxy.newProxyInstance动态的创建实现类对象:

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class test {
public static void main(String[] args) {
InvocationHandler handler = new realproxy();//代理类对象,里面包含着具体的代理逻辑
flag flag = (flag) Proxy.newProxyInstance(test.class.getClassLoader(), new Class[]{flag.class},handler);//动态创建flag的实现类
flag1 flag1 = (flag1) Proxy.newProxyInstance(test.class.getClassLoader(),new Class[]{flag1.class},handler);//动态创建flag1的实现类
flag.getlag("flag");//调用实现类中实现的接口,实际就会调用到代理类对象的invoke方法中
flag1.getflag1();//同上
}
}

image.png

可以看到,成功了,这真的就很神奇,同一个代理类对象,居然同时为两种不同的接口提供相同代理,接下来我们就来讲讲它是怎么实现的,慢慢分析主类的代码,其实核心就是我们在调用实现类对象中实现的任意接口的时候,都会先调用到代理类的invoke方法,然后依照invoke方法中的代码执行,从而实现代理

首先我们需要定义一个InvocationHandler的实例,这里面包含着代理的具体逻辑,是最重要的

接下来就是通过Proxy的静态方法newProxyInstance动态创建接口的实现类对象,而这个方法需要三个参数:

第一个参数就是使用的类加载器ClassLoader,我们用默认的就行,通常就是主函数所在类的ClassLoader

第二个参数是我们需要实现的接口数组,一定要写成数组的形式

第三个参数是一个实现了InvocationHandler接口的对象,里面包含了具体代理的逻辑,调用任何方法都将进入到这个对象的invoke方法中

最后再将返回的Object强制转换回接口就好了,我们把它当成正常的实现类对象用就行了

参考文章:

https://xie.infoq.cn/article/9a9387805a496e1485dc8430f

https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2023 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信