Java设计模式之代理模式
代理模式属于结构型设计模式,代理模式是为一个类提供一个替身,以控制对这个类的访问,通过代理类访问被代理类。
代理模式的三种形式
(1):静态代理
(2):动态代理(JDK代理、接口代理)
(3):Cglib代理(动态代理,可以在内存中动态的创建对象)
实现静态代理UML类图

实现动态代理UML类图
一、 创建静态代理的步骤
(1):创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
(2):创建一个代理类PhoneProxy 并实现接口
(1)创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
/**
* @author yly
* @ClassName ChinaTelecom
* @Date 2020/2/20 19:28
* @Version 1.0
**/
public interface operator {
public void accessToTheNetwork();
}
/**
* @author yly
* @ClassName ChinaTelecom
* @Date 2020/2/20 19:30
* @Version 1.0
**/
public class ChinaTelecom implements operator {
@Override
public void accessToTheNetwork() {
System.out.println("中国电信");
}
}
(2)创建一个代理类PhoneProxy 并实现operator 接口
/**
* @author yly
* @ClassName PhoneProxy
* @Date 2020/2/20 19:31
* @Version 1.0
**/
public class PhoneProxy implements operator {
private ChinaTelecom chinaTelecom;
public PhoneProxy(ChinaTelecom chinaTelecom) {
this.chinaTelecom = chinaTelecom;
}
@Override
public void accessToTheNetwork() {
System.out.println("代理开始");
chinaTelecom.accessToTheNetwork();
System.out.println("代理完成");
}
}
(3)使用代理对象访问被代理的类
/**
* @author yly
* @ClassName demo
* @Date 2020/2/20 19:33
* @Version 1.0
**/
public class demo {
public static void main(String[] args) {
PhoneProxy phoneProxy = new PhoneProxy(new ChinaTelecom());
phoneProxy.accessToTheNetwork();
}
}
(4)运行结果
代理开始
中国电信
代理完成
静态代理优点:
- 不修改目标类功能的前提下,能通过代理对象对目标功能进行扩展。
静态代理的缺点:
- 代理对象需要与被代理对象同时实现一样的接口,所以会有很多代理类
- 接口增加方法,代理类与被代理类都需要修改代码
二、创建动态代理的步骤与静态代理一致
(1)创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
/**
* @author yly
* @ClassName ChinaTelecom
* @Date 2020/2/20 19:28
* @Version 1.0
**/
public interface operator {
public void accessToTheNetwork();
public void play(String name);
}
/**
* @author yly
* @ClassName ChinaTelecom
* @Date 2020/2/20 19:30
* @Version 1.0
**/
public class ChinaTelecom implements operator {
@Override
public void accessToTheNetwork() {
System.out.println("中国电信");
}
@Override
public void play(String name) {
System.out.println("玩游戏:"+name);
}
}
(2)创建一个代理类PhoneProxy ,此处使用JDK动态代理,不需要实现接口
/**
* @author yly
* @ClassName PhoneProxy
* @Date 2020/2/20 19:31
* @Version 1.0
**/
public class PhoneProxy {
private Object object;
public PhoneProxy(Object object) {
this.object = object;
}
/**
* @return
* @CallerSensitive
* public static Object newProxyInstance(ClassLoader loader, //指定当前目标对象使用的类加载器,获取加载器方法
* Class<?>[] interfaces,//目标对象实现的接口类型,使用泛型方法确认类型
* InvocationHandler h) //事情处理,执行目标对象方法时,触发事情处理器方法,将当前执行的目标对象方法作为参数传入
* throws IllegalArgumentException
* {}
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理开始");
Object returnVal = method.invoke(object, args);
System.out.println("JDK动态代理提交");
return returnVal;
}
});
}
}
(3)使用代理对象访问被代理的类
/**
* @author yly
* @ClassName demo
* @Date 2020/2/21 0:49
* @Version 1.0
**/
public class demo {
public static void main(String[] args) {
//创建目标对象
ChinaTelecom chinaTelecom = new ChinaTelecom();
//创建代理对象
operator proxyInstance = (operator)new PhoneProxy(chinaTelecom).getProxyInstance();
//proxyInstance=class com.sun.proxy.$Proxy0内存中生成的代理对象
System.out.println("proxyInstance="+proxyInstance.getClass());
proxyInstance.play("王者荣耀");
}
}
(4)运行结果
proxyInstance=class com.sun.proxy.$Proxy0
JDK动态代理开始
玩游戏:王者荣耀
JDK动态代理提交
三、Cglib代理
Cglib可以使用目标对象子类来进行代理,不需要实现接口,可以弥补静态代理和JDK动态代理的不足,因为静态代理和JDK动态代理都需要目标对象实现一个接口,如果目标对象只是一个单独的对象,没有实现接口,就可以使用Cglib代理。Cglib被广泛的用于aop框架中,例如Spring AOP。
Cglib代理模式注意事项
(1):需要引入cglib的jar文件
(2):在内存中动态创建子类,代理的类不能为final,否则报错。
(3):目标对象方法如果是final或者static时,不会执行目标对象额外的业务方法
Cglib实现步骤
(1):创建目标对象不需要实现接口
(2):创建代理类
(1)创建目标对象不需要实现接口
/**
* @author yly
* @ClassName ChinaTelecom
* @Date 2020/2/21 10:56
* @Version 1.0
**/
public class ChinaTelecom {
public final String play(String name) {
System.out.println("动态代理cglib,不实现接口");
return "玩游戏" + name;
}
}
(2)创建代理类,实现MethodInterceptor接口,重写 intercept()方法
/**
* @author yly
* @ClassName PhoneProxy
* @Date 2020/2/21 10:58
* @Version 1.0
**/
public class PhoneProxy implements MethodInterceptor {
private Object object;
public PhoneProxy(Object object) {
this.object = object;
}
public Object getProxyInstance() {
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(object.getClass());
//设置回调函数
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 重写intercept方法,会调用目标对象的方法
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理开始");
Object invoke = method.invoke(object, objects);
System.out.println("Cglib代理结束");
return invoke;
}
}
(3)使用代理对象访问被代理的类
/**
* @author yly
* @ClassName demo
* @Date 2020/2/21 11:06
* @Version 1.0
**/
public class demo {
public static void main(String[] args) {
ChinaTelecom chinaTelecom = new ChinaTelecom();
ChinaTelecom proxyInstance =(ChinaTelecom) new PhoneProxy(chinaTelecom).getProxyInstance();
String play = proxyInstance.play("王者荣耀");
System.out.println(play);
}
}
(4)运行结果
Cglib代理开始
动态代理cglib,不实现接口
Cglib代理结束
玩游戏王者荣耀
将目标方法play()用final修饰时,执行结果为
动态代理cglib,不实现接口
玩游戏王者荣耀
代理模式的使用场景
- 防火墙代理(内网通过代理穿透防火墙,实现对公网的访问)
- 远程代理(通过远程代理,可以将远程对象当本地对象调用)
- 缓存代理(请求资源文件时,先到缓存拿数据,如果找到资源则返回,如果找不到资源,再从服务器拿资源)
使用静态代理实现缓存代理
(1)创建File接口
/**
* @author yly
* @ClassName Image
* @Date 2020/2/10 19:02
* @Version 1.0
**/
public interface File {
public File resource();
}
(2)创建File的实现类ReadFile
/**
* @author yly
* @ClassName RealImage
* @Date 2020/2/10 19:03
* @Version 1.0
**/
public class ReadFile implements File {
private File file;
private String fileName;
public ReadFile(String fileName) {
this.fileName = fileName;
}
@Override
public File resource() {
System.out.println("缓存文件: " + fileName);
return file;
}
public File getResource(String fileName){
System.out.println("资源文件: " + fileName);
return file = new ReadFile(fileName);
}
}
(3)创建代理类ProxyFile
/**
* @author yly
* @ClassName ProxyImage
* @Date 2020/2/10 19:05
* @Version 1.0
**/
public class ProxyFile implements File {
private ReadFile readFile;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public File resource() {
if(readFile == null){
readFile = new ReadFile(fileName);
return readFile.getResource(fileName);
}
return readFile.resource();
}
}
(4)使用代理对象访问被代理的类
/**
* @author yly
* @ClassName ProxyPatternDemo
* @Date 2020/2/10 19:06
* @Version 1.0
**/
public class ProxyPatternDemo {
public static void main(String[] args) {
File file = new ProxyFile("file.jpg");
// 文件将从磁盘加载
File resource = file.resource();
System.out.println(resource);
System.out.println("");
// 文件从缓存加载
File resource1 = file.resource();
System.out.println(resource1);
}
}
(5)运行结果
资源文件: file.jpg
com.java.designmode.impl.ReadFile@16d3586
缓存文件: file.jpg
com.java.designmode.impl.ReadFile@16d3586