代理模式(Proxy Pattern):通过创建代理对象来控制对另一个对象的访问,通常用于实现横切关注点(cross-cutting concerns),例如日志记录、系统安全性检查等。常见的代理模式就是动态代理和静态代理。
静态代理:在编译时就已经确定代理关系,也就是在编译时创建代理对象。在静态代理中,代理类和被代理类通常是通过接口或继承关系来建立联系,并且代理类需要实现与被代理类相同的接口,以便能够对外提供相同的服务。
静态代理的简单实现步骤如下:
实例:用户登录代理认证
// 接口
interface UserService {
//目标方法
void login(String username, String password);
}
// 被代理类
class UserServiceImpl implements UserService {
//具体实现
public void login(String username, String password) {
System.out.println("用户:" + username + " 登录成功.");
}
}
// 静态代理类
class AuthProxy implements UserService {
//引用
private UserService userService;
//初始化
public AuthProxy(UserService userService) {
this.userService = userService;
}
//增强方法
public void login(String username, String password) {
//在调用目标方法前增强逻辑
if (authenticate(username, password)) {
userService.login(username, password);//原始方法
} else {
System.out.println("身份认证失败: " + username);
}
}
private boolean authenticate(String username, String password) {
// 简单实现身份验证逻辑
return username.equals("alice") && password.equals("123");
}
}
// 简单测试
public class Main{
public static void main(String[] args) {
UserService userService = new AuthProxy(new UserServiceImpl());
userService.login("alice", "123"); // 身份验证成功
userService.login("john", "123"); // 身份验证失败
}
}
静态代理的优点:简单易懂、编码方便,可以在代理类中灵活添加额外的功能。
静态代理的缺点:代理类和被代理类之间的关系在编译时就确定了,如果有多个类需要代理,就需要编写多个代理类,导致代码冗余。
动态代理:在运行时动态生成代理类,运行时才创建代理对象。
相比于静态代理,动态代理更加灵活,可以在运行时决定代理类和被代理类的关系,无需提前编写代理类。Java中实现动态代理的关键类是Proxy和InvocationHandler。Proxy类提供了创建动态代理类的方法,而InvocationHandler接口则需要自定义一个实现类,用于处理代理类的方法调用。
动态代理的简单实现步骤如下:
实例:(和上面静态代理一样)
// 接口
interface UserService {
//目标方法
void login(String username, String password);
}
// 被代理类
class UserServiceImpl implements UserService {
//具体实现
public void login(String username, String password) {
System.out.println("用户:" + username + " 登录成功.");
}
}
// 实现类,也就是工具类
class AuthenticationHandler implements InvocationHandler {
private Object target;
public AuthenticationHandler (Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (authenticate(args[0].toString(), args[1].toString())) {
return method.invoke(target, args);
} else {
System.out.println("身份认证失败: " + args[0]);
return null;
}
}
private boolean authenticate(String username, String password) {
return username.equals("alice") && password.equals("123");
}
}
// 简单测试
public class Main{
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
//创建代理对象,参数有类加载器、被代理对象、代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new AuthenticationHandler (userService)
);
proxy.login("alice", "123"); // 身份验证成功
proxy.login("john", "123"); // 身份验证失败
}
}
动态代理优点:在运行时决定代理类和被代理类的关系,无需提前编写代理类,减少了代码冗余。
动态代理缺点:相比于静态代理,它在运行时会带来一定的性能开销。
详细内容链接:Java动态代理
实现方式:静态代理在编译时就确定代理关系,而动态代理是在运行时动态生成代理类。
灵活性:静态代理的关系在编译时确定,无法动态修改,而动态代理可以在运行时决定代理关系。
代码复用:动态代理相比静态代理具有更好的代码复用性。在静态代理中,每个被代理类都需要编写一个对应的代理类,导致代码冗余。而在动态代理中,可以通过一个通用的InvocationHandler实现类来处理多个被代理类的方法调用,减少了代码的冗余。
性能开销:动态代理在运行时通过反射机制动态生成代理类,相比静态代理会带来一定的性能开销。每次方法调用都需要通过反射调用InvocationHandler中的invoke方法,在方法调用频繁的情况下可能会影响性能。而静态代理在编译时就确定了代理关系,直接调用被代理类的方法,性能更高。
应用场景: