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

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

动态代理

静态代理的代理类是由程序员手写的,而动态代理则不同,动态代理的代理类字节码则是在程序运行时由Java反射机制动态生成的,这就会方便而且灵活很多,因为Java反射机制可以生成任意类型的动态代理类嘛,java.lang.reflect 包中的 Proxy类和InvocationHandler 接口又提供了生成动态代理类的能力,接下来我们就来看实例:

接口和接口的实现类还是和上面一样,这里为了演示动态代理我们多添加一个接口flag1以及它的实现类getflag1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface flag {
void getflag();
}
public class getflag implements flag {
@Override
public void getflag() {
System.out.println("Give you flag: flag{wllm_yyds}");
}
}
public interface flag1 {
void getflag();
}
public class getflag1 implements flag1{
@Override
public void getflag() {
System.out.println("This is your flag:flag{Welc0me_to_WLLMCTF}");
}
}

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

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

public class DynamicProxy implements InvocationHandler{
private Object object;
public DynamicProxy(Object object)
{
this.object=object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是你的flag");
method.invoke(object,args);
System.out.println("请拿好");
return null;
}
}

其实这逻辑是很简单的,就是用反射去执行一个方法,然后在执行方法的前后我们可以加入一些代码,这个应该不难理解,接下来我们就来看看主类是怎么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.lang.reflect.*;
public class test11 {
public static void main(String[] args) {
flag flag = new getflag();
flag1 flag1 = new getflag1();
InvocationHandler handler1 = new DynamicProxy(flag);
flag a = (flag) Proxy.newProxyInstance(getflag.class.getClassLoader(), new Class[]{flag.class}, handler1);
a.getflag();
InvocationHandler handler2 = new DynamicProxy(flag1);
flag1 b = (flag1) Proxy.newProxyInstance(getflag1.class.getClassLoader(), new Class[]{flag1.class}, handler2);
b.getflag();
}
}

image.png

可以看到,成功了,这真的就很神奇,同一个动态代理的类,居然同时为两种不同的接口以及不同的实现类提供了代理,接下来我们就来讲讲它是怎么实现的,慢慢分析主类的代码,我觉得最重要的就是在动态创建代理类的时候会调用invoke方法,从而实现代理

首先我们需要定义一个InvocationHandler的实例,将我们需要代理的接口放进去

接下来就是通过Proxy的静态方法newProxyInstance动态创建代理,而这个方法需要三个参数:

第一个参数就是使用的类加载器ClassLoader,我们用默认的就行,通常就是接口实现类的ClassLoader

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

第三个参数是一个实现了InvocationHandler接口的对象,里面包含了具体代理的逻辑

最后再将返回的Object强制转换回接口就好了

参考文章:

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

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

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

请我喝杯咖啡吧~

支付宝
微信