Java代理模式

The trees, like the longings of the earth, stand atiptoe to peep at the heaven.

群树如表示大地的愿望似的,踮起脚来向天空窥望。

静态代理

假设现在有一个人(person接口),他的名字叫A(class A implement person),小A 40岁了必须要找对象,但是呢自己比较害羞不好意思开口,这样就需要一个媒婆来帮他找对象(MeiPo 代理类),至此就是静态代理模式,下面我们用代理来实现下:

Class Person

1
2
3
public interface Person {
void findLove();
}

Class A

1
2
3
4
5
6
7
8
public class XiaoA implements Person{
@Override
public void findLove() {
//个人条件
System.out.println("高富帅");
System.out.println("有房有车的");
}
}

Class Meipo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Meipo2 implements Person {

private XiaoA xiaoA;

public Meipo2(XiaoA xa) {
// TODO Auto-generated constructor stub
this.xiaoA = xa;
}

@Override
public void findLove() {
// TODO Auto-generated method stub
System.out.println("我是媒婆,要帮小A找对象,小A的条件为:");
xiaoA.findLove();
System.out.println("寻找中...");
System.out.println("寻找到仙女一枚!");
}
}

Main

1
2
3
4
5
6
public class TestFindLove {
public static void main(String[] args) {
Meipo2 meipo2 = new Meipo2(new XiaoA());
meipo2.findLove();
}
}

静态代理模式总结

  • 真实对象和代理对象都要实现同一接口
  • 代理对象要代理真实角色

好处:

  1. 代理对象可以做很多真实对象做不了的事情 、
  2. 真实对象可以专注做自己的事情

缺点:

一个真实角色会产生一个代理角色

动态代理

实现代理对象的步骤

  1. 创建接口,定义目标类要完成的功能
  2. 创建目标类,实现接口
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
    1. 调用目标方法
    2. 增强功能
  4. 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型
  • 动态代理角色和静态代理角色一样
  • 动态代理的代理类时动态生成的,不是我们直接写好的
  • 分为两类
    • 基于接口的动态的代理:基于JDK的动态代理
    • 基于类的动态代理:cglib
    • java字节码实现:javasist

基于JDK的动态代理

JDK提供了java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类,这两个类相互配合

  1. Proxy:代理
  2. InvocationHandler:调用处理程序

上面的静态代理是每来一个人就创建一个媒婆代理他,这样的话工作过于繁琐,这时我们为何我开一个媒婆公司呢?将找对象的人交给媒婆公司,让媒婆公司创建媒婆代理他传宗接代的任务!

Class B

目标接口的实现类,也就是接下来的目标类

1
2
3
4
5
6
7
public class XiaoB implements Person{
@Override
public void findLove() {
System.out.println("矮穷矬");
System.out.println("屌丝一枚");
}
}

findLove目标类中目标方法

媒婆 改为 媒婆公司:Class MeipoCompany

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MeipoCompany implements InvocationHandler {
private Person target; //被代理对象的引用作为一个成员变量保存下来了
//传入对象
public void setTarget(Object target) {
this.target = target;
}

//获取被代理人的个人资料
public Object getInstance(Person target) throws Exception{
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理对象的class是:"+clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}


@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("我是媒婆公司:" + "得给你找个异性才行");
System.out.println("说下你的条件:");
System.out.println("------------");
//反射调用方法
method.invoke(target, args);
System.out.println("------------");
System.out.println("开始进行海选...");
System.out.println("找到啦");
return null;
}
}

在invoke()方法中,我们没有创建对象,所以,我们使用method.invoke

两个参数:

  1. 对象,这个对象是动态的,不是固定的,所以我们需要传入进来,可以通过调用该类的构造方法setTarget()进行传入
  2. 参数,调用方法的参数,即args

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestFindLove {
public static void main(String[] args) {
try {
Person handler = (Person)new MeipoCompany().getInstance(new XiaoA());
System.out.println("代理类:"+A.getClass());
handler.findLove();
System.out.println("************************");
//1. 创建目标对象
Person B = new XiaoB();
//2. 创建 InvocationHandler 对象
InvocationHandler handler1 = new MeipoCompany(B);
//3.创建代理对象
Person proxy = (Person)Proxy.newProxyInstance(B.getClassLoader(), B.getInterfaces(), handler1)
System.out.println("代理类:"+B.getClass());
//4.通过代理执行方法
B.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
//1. 创建目标对象
Person B = new XiaoB();
//2. 创建 InvocationHandler 对象
InvocationHandler handler1 = new MeipoCompany(B);
//3.创建代理对象
Person proxy = hander.getInstance(handler);
Person proxy = (Person)Proxy.newProxyInstance(B.getClassLoader(), B.getInterfaces(), handler1)
System.out.println("代理类:"+B.getClass());
//4.通过代理执行方法
proxy.findLove();

这段代码中不需要MeipoCompany中有getInstance方法,直接在main类中实现,等价于A的动态代理

这里调用方法时,就会自动跳转到invoke方法,将方法名与参数传递给invoke中的method.invoke方法

InvocationHandler

实现InvocationHandler,重写invoke方法

处理代理实例,并且返回结果

1
2
3
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {	
return null;
}

invoke表示代理对象要执行的功能代码,代理类要完成的功能就写在invoke中。

  1. 调用目标方法,执行目标方法的功能

  2. 功能增强,在目标方法调用时,增加功能

  3. 参数

    1. Object proxy

      jdk创建的代理对象,无需赋值

    2. Method method

      目标类中的方法,jdk提供,无需赋值

    3. Object[] args

      目标类中方法的参数,jdk提供,无需赋值


Method类:表示方法的类,确切的说就目标类中的方法

作用:通过Method可以执行某个目标类的方法,method.invoke()

method.invoke(目标对象,方法的参数)

Proxy类

核心对象,创建代理对象

之前的创建对象都是new类的构造方法,现在我们是使用Proxy类的对象,代替new的使用.

静态方法newProxyInstance

1
2
3
4
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
}

作用:创建代理对象,等同于静态代理中的new

参数

  1. ClassLoader loader

    类加载器,负责向内存中加载对象,使用反射获取对象的ClassLoader

    是目标对象的类加载器

  2. Class<?>[] interfaces

    是接口,目标对象所实现的接口,也是反射获取的

  3. InvocationHandler h

    是我们自己写的,代理类要完成的功能

返回值:就是目标对象的代理对象

查看评论