代理设计模式

代理模式是java中很常见的一个设计模式,在spring框架中就有大量用到。代理的本意是一个人或机构代表另一个人或机构。java中的代理模式和现实生活也有很大类似。例如老板要招聘一个员工,它可以自己亲自去发布招聘信息,面试应聘人等。但是他也可以派遣公司hr去完成这些中间细节,帮他完成这些事情。老板只要关注招人这个业务本身。
代理模式分为静态代理和动态代理。先说静态代理

静态代理

静态代理的UML结构比较简单明了:
静态代理UML
第一步:写一个接口

1
2
3
4
5
6
7
8
9
package com.xx.zh.test.staproxy;

public interface Student {

//获得姓名
public String getName();
//获得年龄
public String getAge();
}

第二部:实现这个接口(这个类是被代理类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.xx.zh.test.staproxy;

public class StudentImpl implements Student {

public StudentImpl() {
// TODO Auto-generated constructor stub
}

@Override
public String getName() {
System.out.println("得到姓名");
return "zhangsan";
}

@Override
public String getAge() {
System.out.println("得到年龄");
return "20";
}

}

第三部:代理类,代理类需要有一个被代理对象的引用变量并且实现被代理对象所实现的接口。重写接口中的方法,并在方法中调用被代理类对应的方法,这样我们就在和在被代理方法执行的前后添加代理代码来实现静态代理。

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
package com.xx.zh.test.staproxy;

public class ProxyStudent implements Student {

//被代理对象类型引用变量
private Student student ;
public ProxyStudent(Student s) {
this.student = s;
}

@Override
public String getName() {
System.out.println("before--------getName");
String name = student.getName();
System.out.println("after---------getName");
return name;
}

@Override
public String getAge() {
System.out.println("before--------getAge");
String age = student.getAge();
System.out.println("after---------getAge");
return age;
}

}

运行结果:

这种代理方式需要代理对象和目标对象实现一样的接口。
优点:

  • 可以在不修改目标对象的前提下扩展目标对象的功能。

缺点:

  • 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
  • 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。

动态代理

动态代理动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
静态代理与动态代理的区别主要在:

  • 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
  • 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

特点:

  • 动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。

动态代理在JDK中有两个核心的方法:
java.lang.reflect.Proxy中的:

1
2
3
4
5
static Object    newProxyInstance(ClassLoader loader,  //指定当前目标对象使用类加载器
Class<?>[] interfaces, //目标对象实现的接口的类型
InvocationHandler h //事件处理器
)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

java.lang.reflect InvocationHandler中的:

1
2
Object    invoke(Object proxy, Method method, Object[] args) 
// 在代理实例上处理方法调用并返回结果。

实现代码如下:

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
package com.xx.zh.test.dyproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.xx.zh.test.staproxy.Student;
import com.xx.zh.test.staproxy.StudentImpl;

public class TestMain {

public static void main(String[] args) {
Student s = new StudentImpl();
Student studentProxy = (Student) Proxy.newProxyInstance(s.getClass().getClassLoader(),
s.getClass().getInterfaces(), new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before----------"+method.getName());
Object objReturn = method.invoke(s, args);
System.out.println("after-----------"+method.getName());
return objReturn;
}
});
studentProxy.getName();
studentProxy.getAge();
}
}

运行结果:

-------------本文结束感谢您的阅读-------------