首先我们要先了解什么是代理模式:
代理模式是一种设计模式,它可以为其他对象提供一种代理,以控制对该对象的访问。在现实生活中,代理模式可以用很多例子来说明。下面是一些例子:
- 餐厅预订:当你想去一家热门的餐厅用餐时,你可以通过电话或者网络平台预订座位。但是有时候这些餐厅可能会很忙,难以预订。这时,你可以联系一家代理公司,让他们为你预订座位。代理公司会负责与餐厅联系,为你预订座位,并在需要时帮助你修改或取消预订。
- 网络购物:当你在购物网站上买东西时,你需要提供个人信息和支付信息。但是有时候你可能不想向这些网站透露太多个人信息。这时,你可以使用一些代理服务,如虚拟信用卡或匿名浏览器插件,来隐藏你的真实身份和信息。
- 快递服务:当你需要向远方寄送包裹时,你可以联系快递公司。但是有时候你可能无法直接联系到快递公司,或者需要在特定时间和地点交付包裹。这时,你可以使用一些代理服务,如物流代理公司或快递代收点,来帮助你处理包裹,并在需要时代替你与快递公司联系。
JDK 代理
假设有这样一个场景,我们有一个接口 UserService
,其中包含一个方法 save
,用于保存用户信息。我们想要在调用这个方法之前,先打印一行日志,表示用户信息正在被保存。我们可以使用 JDK 动态代理来实现这个功能,具体代码如下:
// 被代理对象
public interface UserService {
void save(String username);
}
public class UserServiceImpl implements UserService{
@Override
public void save(String username) {
System.out.println("Saving user " + username);
}
}
// 代理对象
public class UserServiceProxy implements InvocationHandler {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在保存用户信息....");
Object invoke = method.invoke(userService, args);
System.out.println("成功保存用户信息....");
return invoke;
}
}
// 测试
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserService proxyInstance = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserService.class}, new UserServiceProxy(userService));
proxyInstance.save("marry");
}
}
CGLib代理
现在,假设我们有一个类 UserDao
,其中包含一个方法 save
,用于保存用户信息。我们想要在调用这个方法之前,先打印一行日志,表示用户信息正在被保存。我们可以使用 CGLib 动态代理来实现这个功能
先引入相关依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
// 被代理对象
public class UserDAO {
public void save(String username) {
System.out.println("Saving user " + username);
}
}
// 代理类
public class UserDAOIntercept implements MethodInterceptor {
// 目标对象,也就是被代理的对象
private Object target;
// 通过构造方法进行注入
public UserDAOIntercept(Object target) {
this.target = target;
}
// 创建代理对象的方法,返回一个代理对象
public Object getIntercept() {
// 使用Enhancer类来创建代理对象
Enhancer enhancer = new Enhancer();
// 设置父类为目标对象的类
enhancer.setSuperclass(target.getClass());
// 设置回调为当前代理类的实例
enhancer.setCallback(this);
// 创建并返回代理对象
return enhancer.create();
}
// 实现 MethodInterceptor 接口的 intercept 方法,拦截代理对象的方法调用
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("正在保存用户信息....");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("成功保存用户信息....");
return result;
}
}
// 测试
public class Client {
public static void main(String[] args) {
// 创建目标对象
UserDAO userDAO = new UserDAO();
// 创建代理类,并传入目标对象
UserDAOIntercept intercept = new UserDAOIntercept(userDAO);
// 获取代理对象
UserDAO proxy = (UserDAO) intercept.getIntercept();
// 调用代理对象的方法
proxy.save("jack");
}
}
总结
这两种代理方法的相同点是:
- 都可以在运行时动态地创建代理对象,而不需要手动编写代理类的代码。
- 都可以在不修改目标对象的源码的情况下,对目标对象的方法进行增强或拦截。
这两种代理方法的不同点是:
- JDK 代理需要目标对象实现一个或多个接口,而 CGLib 代理不需要目标对象实现任何接口。
- JDK 代理是通过实现目标对象的接口来创建代理对象,而 CGLib 代理是通过继承目标对象来创建子类作为代理对象。
- JDK 代理不能对 final 修饰的类或方法进行代理,而 CGLib 代理可以,但是不能对final修饰的方法进行拦截。
- JDK 代理的性能比 CGLib 代理的性能稍高,但是 CGLib 代理的功能比 JDK 代理的功能更强大。
- 由于 CGLib 代理是基于类的代理技术,因此它可以代理非公共的类和方法,而 JDK 动态代理则只能代理公共的类和方法。
- JDK 动态代理在创建代理对象时,需要传入被代理对象实例和一个实现了 InvocationHandler 接口的回调函数实例,该回调函数将在代理对象方法被调用时执行。而 CGLib 代理则是通过 Enhancer类的 setSuperclass() 方法来指定被代理类,并通过 setCallback() 方法来指定回调函数实例。
- 由于 JDK 动态代理是基于接口的代理技术,因此它可以为多个接口创建代理对象,而 CGLib 代理只能为一个类创建代理对象