代理模式的核心是通过一个代理对象来包装目标对象,客户端调用代理对象的方法时,代理对象可以在调用目标对象对应方法前后插入自定义逻辑,从而实现功能增强。要在目标对象的方法调用前增加前置权限校验,只需要把校验逻辑放在代理对象调用目标方法之前即可。

静态代理实现方式
静态代理需要手动创建代理类,代理类实现和目标对象相同的接口,内部持有目标对象的引用,在接口方法中先执行权限校验,再调用目标对象的方法。
定义业务接口
首先定义一个用户服务的业务接口,包含查询用户信息的方法:
// 用户服务接口
public interface UserService {
String getUserInfo(String userId);
}
实现目标对象
创建接口的实现类,也就是我们的目标对象,内部只实现核心业务逻辑:
// 目标用户服务实现类
public class UserServiceImpl implements UserService {
@Override
public String getUserInfo(String userId) {
return "用户ID:" + userId + ",用户信息:张三";
}
}
创建静态代理类
代理类同样实现UserService接口,构造方法接收目标对象作为参数,在getUserInfo方法内先执行权限校验,再调用目标对象的方法:
// 静态代理类
public class UserStaticProxy implements UserService {
// 持有目标对象的引用
private UserService targetUserService;
public UserStaticProxy(UserService targetUserService) {
this.targetUserService = targetUserService;
}
@Override
public String getUserInfo(String userId) {
// 前置权限校验逻辑
if (!checkPermission(userId)) {
throw new RuntimeException("当前用户无权限查询该用户信息");
}
// 校验通过,调用目标对象的方法
return targetUserService.getUserInfo(userId);
}
// 权限校验方法
private boolean checkPermission(String userId) {
// 这里模拟权限校验规则,实际可以对接权限系统
return "admin".equals(userId) || "1001".equals(userId);
}
}
静态代理测试
编写测试代码验证静态代理的效果:
public class StaticProxyTest {
public static void main(String[] args) {
// 创建目标对象
UserService targetService = new UserServiceImpl();
// 创建静态代理对象
UserService proxyService = new UserStaticProxy(targetService);
// 有权限的用户调用
try {
String result1 = proxyService.getUserInfo("1001");
System.out.println("调用结果:" + result1);
} catch (Exception e) {
System.out.println("调用失败:" + e.getMessage());
}
// 无权限的用户调用
try {
String result2 = proxyService.getUserInfo("1002");
System.out.println("调用结果:" + result2);
} catch (Exception e) {
System.out.println("调用失败:" + e.getMessage());
}
}
}
动态代理实现方式
静态代理需要为每个目标类手动编写代理类,当业务接口较多时会产生大量冗余代码。动态代理可以在运行时动态生成代理对象,不需要手动编写代理类,更适合复杂场景。
JDK动态代理实现
JDK动态代理要求目标对象必须实现接口,通过InvocationHandler接口实现方法调用拦截,在调用目标方法前插入权限校验逻辑:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// JDK动态代理处理器
public class PermissionInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
public PermissionInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 只拦截getUserInfo方法,其他方法直接放行
if ("getUserInfo".equals(method.getName()) && args != null && args.length == 1) {
String userId = (String) args[0];
// 前置权限校验
if (!checkPermission(userId)) {
throw new RuntimeException("当前用户无权限查询该用户信息");
}
}
// 调用目标对象的方法
return method.invoke(target, args);
}
// 权限校验方法
private boolean checkPermission(String userId) {
return "admin".equals(userId) || "1001".equals(userId);
}
// 生成代理对象的方法
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new PermissionInvocationHandler(target)
);
}
}
JDK动态代理测试
测试动态代理的效果:
public class JdkProxyTest {
public static void main(String[] args) {
// 创建目标对象
UserService targetService = new UserServiceImpl();
// 生成动态代理对象
UserService proxyService = (UserService) PermissionInvocationHandler.getProxy(targetService);
// 有权限的用户调用
try {
String result1 = proxyService.getUserInfo("admin");
System.out.println("调用结果:" + result1);
} catch (Exception e) {
System.out.println("调用失败:" + e.getMessage());
}
// 无权限的用户调用
try {
String result2 = proxyService.getUserInfo("1003");
System.out.println("调用结果:" + result2);
} catch (Exception e) {
System.out.println("调用失败:" + e.getMessage());
}
}
}
两种实现方式对比
两种方式都可以实现前置权限校验,适用场景有所不同:
| 对比项 | 静态代理 | JDK动态代理 |
|---|---|---|
| 实现复杂度 | 需要手动编写代理类,接口多时冗余代码多 | 不需要手动编写代理类,实现更灵活 |
| 目标对象要求 | 不需要目标对象实现接口 | 要求目标对象必须实现接口 |
| 适用场景 | 接口数量少、代理逻辑简单的场景 | 接口数量多、需要统一增强的场景 |
注意事项
- 权限校验逻辑需要尽量通用,避免和具体业务方法强耦合,方便后续扩展其他校验规则
- 如果目标对象没有实现接口,可以使用CGLIB动态代理,通过继承目标类生成代理对象,实现方式和JDK动态代理类似
- 代理模式只适合做方法级别的前置增强,复杂的权限控制建议结合专业的权限框架实现