代理模式介绍(静态代理、jdk动态代理、cglib代理)
一、静态代理
(一)定义
1、定义
为其他对象提供一种代理以控制对这个对象的访问;
2、涉及到的角色
(1)抽象主题角色:真实主题和代理主题的共同接口,便于在使用真实主题的地方都可以使用代理主题;
(2)代理主题角色:代理类,负责控制对真实主题的引用,在需要的时候创建和删除真实主题,并且在真实主题处理完毕后做预处理和善后处理的工作;
(3)真实主题角色:被代理角色,业务逻辑的具体执行者;
(二)类图
(三)代码实现
1、抽象主题角色(业务接口)
package com.xiaobai.design_pattern.proxy;
/**
* @author wangtw
* @date 2023/12/1 23:03
* @description 代理模式-抽象主题角色
*/
public interface IGamePlayer {
/**
* 打怪
*/
void killBoss();
/**
* 升级
*/
void upGrade();
}
2、真实主题角色(具体业务功能)
package com.xiaobai.design_pattern.proxy;
/**
* @author wangtw
* @date 2023/12/1 23:04
* @description 代理模式-真实主题角色
*/
public class GamePlayer implements IGamePlayer{
/**
* 打怪角色
*/
private String name;
public GamePlayer(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* 打怪
*/
@Override
public void killBoss() {
System.out.println(this.name + "在打野怪");
}
/**
* 升级
*/
@Override
public void upGrade() {
System.out.println(this.name + "升了1级");
}
}
3、代理主题角色(代理类)
package com.xiaobai.design_pattern.proxy;
import java.lang.reflect.Field;
import java.util.Date;
/**
* @author wangtw
* @date 2023/12/1 23:10
* @description
*/
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
private void start() {
System.out.println("开始时间为" + new Date());
}
private void end() {
System.out.println("结束时间为" + new Date());
}
/**
* 获取角色名称
* @param operateStep
*/
private void operatePerson(String operateStep) {
Class<? extends IGamePlayer> aClass = gamePlayer.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.getName().equals("name")) {
declaredField.setAccessible(true);
try {
Object name = declaredField.get(gamePlayer);
System.out.println(name + operateStep + "操作");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
@Override
public void killBoss() {
operatePerson("开始");
start();
this.gamePlayer.killBoss();
}
@Override
public void upGrade() {
this.gamePlayer.upGrade();
operatePerson("结束");
end();
}
}
4、测试类
package com.xiaobai.design_pattern.proxy;
import org.junit.jupiter.api.Test;
/**
* @author wangtw
* @date 2023/12/1 23:21
* @description 代理模式测试类
*/
public class ClientDemo {
@Test
public void test() {
IGamePlayer player = new GamePlayer("芈月");
IGamePlayer proxy = new GamePlayerProxy(player);
proxy.killBoss();
proxy.upGrade();
}
}
输出:
芈月开始操作
开始时间为Sat Dec 02 20:14:09 CST 2023
芈月在打野怪
芈月升了1级
芈月结束操作
结束时间为Sat Dec 02 20:14:09 CST 2023
(四)总结
1、代理模式的优点:
(1)职责清晰:真实角色负责处理实际的业务逻辑,不用关心非本职的事务,通过代理完成附加的事务;
(2)高扩展性:不同的需求可能会有不同的真实角色,只要实现了接口,代理类就可以完全在不做任何修改的情况下代理各种真实主题角色;
2、静态代理模式的缺点:
(1)若抽象主题角色增加功能,会影响代理类;
(2)不同的功能需求可能会有不同的代理类;
二、jdk动态代理
(一)使用动态代理的场景
在程序执行期间,使用jdk的反射机制创建代理类对象,动态指定要代理的对象;
不依赖于业务接口;
静态代理目标很多时,可以使用动态代理,避免静态代理的缺点;
(二)代码实现
1、创建代理类
代理类需要实现InvocationHandler接口,并重写invoke方法,生成代理类对象后,调用目标类实现方法时会调用生成代理对象中的invoke方法
package com.xiaobai.design_pattern.proxy;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author wangtw
* @date 2023/12/2 21:36
* @description jdk动态代理处理器
*/
public class JdkGamePlayerProxy implements InvocationHandler {
private Object targetObject;
public JdkGamePlayerProxy(Object object) {
this.targetObject = object;
}
private void start() {
System.out.println("开始时间为" + new Date());
}
private void end() {
System.out.println("结束时间为" + new Date());
}
/**
* 获取角色名称
* @param operateStep
*/
private void operatePerson(String operateStep) {
Class<?> aClass = this.targetObject.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.getName().equals("name")) {
declaredField.setAccessible(true);
try {
Object name = declaredField.get(this.targetObject);
System.out.println(name + operateStep + "操作");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("============开始执行" + method.getName() + "方法==============");
operatePerson("开始");
start();
Object invoke = method.invoke(this.targetObject, args);
operatePerson("结束");
end();
System.out.println("============结束执行" + method.getName() + "方法==============");
return invoke;
}
}
2、测试方法
使用java.lang.reflect.Proxy#newProxyInstance生成代理对象
@Test
public void jdkProxyTest() {
//生成代理类文件 在根目录的同级目录,com下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
IGamePlayer player = new GamePlayer("澜");
Class<? extends IGamePlayer> playerClass = player.getClass();
//代理类 实现需要实现InvocationHandler接口,重写invoke方法 传入业务实现类对象
JdkGamePlayerProxy jdkGamePlayerProxy = new JdkGamePlayerProxy(player);
// 创建代理类对象
/**
*
loader – the class loader to define the proxy class
interfaces – the list of interfaces for the proxy class to implement
h – the invocation handler to dispatch method invocations to
*/
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(playerClass.getClassLoader(),
playerClass.getInterfaces(), jdkGamePlayerProxy);
System.out.println(proxy.getClass());
proxy.killBoss();
proxy.upGrade();
}
输出:
class com.sun.proxy.$Proxy9
开始执行killBoss方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜在打野怪
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜升了1级
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行upGrade方法==
jdk动态代理依赖于java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
三、cglib动态代理
(一)说明
1、说明
cglib动态代理需要依赖第三方,以下代码使用spring生态的cglib包(org.springframework.cglib)实现cglib代理;
2、基础类介绍:
org.springframework.cglib.proxy.MethodInterceptor:方法拦截器类;
org.springframework.cglib.proxy.Enhancer:增强类;
org.springframework.cglib.proxy.MethodProxy:方法代理类
(二)代码实现
拦截器需要实现org.springframework.cglib.proxy.MethodInterceptor接口,重写intercept方法,使用org.springframework.cglib.proxy.MethodProxy#invoke执行目标方法
1、拦截器实现
package com.xiaobai.design_pattern.proxy;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author wangtw
* @date 2023/12/3 20:17
* @description
*/
public class CglibGamePlayerProxy implements MethodInterceptor {
private Object targetObject;
public CglibGamePlayerProxy(Object targetObject) {
this.targetObject = targetObject;
}
private void start() {
System.out.println("开始时间为" + new Date());
}
private void end() {
System.out.println("结束时间为" + new Date());
}
/**
* 获取角色名称
* @param operateStep
*/
private void operatePerson(String operateStep) {
Class<?> aClass = this.targetObject.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.getName().equals("name")) {
declaredField.setAccessible(true);
try {
Object name = declaredField.get(this.targetObject);
System.out.println(name + operateStep + "操作");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("============开始执行" + method.getName() + "方法==============");
operatePerson("开始");
start();
Object result = methodProxy.invoke(this.targetObject, objects);
operatePerson("结束");
end();
System.out.println("============结束执行" + method.getName() + "方法==============");
return result;
}
}
2、测试方法
使用org.springframework.cglib.proxy.Enhancer对目标类进行增强,并设置回调拦截器
@Test
public void cglibProxyTest() {
IGamePlayer gamePlayer = new GamePlayer("亚瑟");
// 对目标方法进行增强
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(IGamePlayer.class); // 设置要代理的父类
enhancer.setCallback(new CglibGamePlayerProxy(gamePlayer)); // 设置回调的拦截器
IGamePlayer proxy = (IGamePlayer) enhancer.create(); // 创建代理对象
proxy.killBoss();
proxy.upGrade();
}
输出:
开始执行killBoss方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟在打野怪
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟升了1级
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行upGrade方法==
参考:
韩敬海 设计模式(Java版)
Java-JDK动态代理
JDK动态代理
动态代理之 cglib 实现