动态代理和静态代理
代理模式(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接口则需要自定义一个实现类,用于处理代理类的方法调用。
动态代理的简单实现步骤如下:
- 定义一个接口,声明代理类和被代理类需要实现的方法。
- 创建一个InvocationHandler的实现类,实现invoke方法,在该方法中定义对被代理类方法的处理逻辑。
- 使用Proxy类的静态方法newProxyInstance创建动态代理类的实例,传入ClassLoader、接口列表和InvocationHandler实例。
- 调用动态代理类的方法,实际上会调用InvocationHandler中的invoke方法来处理方法调用。
实例:(和上面静态代理一样)
// 接口
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方法,在方法调用频繁的情况下可能会影响性能。而静态代理在编译时就确定了代理关系,直接调用被代理类的方法,性能更高。
应用场景:
- 静态代理适用于代理类数量较少且固定的情况,可以在编译时确定代理关系,并在代理类中添加额外的功能。例如,日志记录、权限校验等横切关注点可以通过静态代理来实现。
- 动态代理适用于代理类数量较多或不确定的情况,可以在运行时动态生成代理类,并通过InvocationHandler实现类来处理不同的代理逻辑。例如,AOP(面向切面编程)中的方法拦截、事务管理等功能就是通过动态代理来实现。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!