
👨💻程序员三明治:个人主页 🔥 个人专栏: 《设计模式精解》 《重学数据结构》
🤞先做到 再看见!
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。
代理模式中的角色:
代理模式的类图:

简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,扩展目标对象的功能。
代理模式有静态代理和动态代理两种实现方式
静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。
1.定义发送短信的接口
public interface SmsService {
String send(String message);
}2.实现发送短信的接口
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}3.创建代理类并同样实现发送短信的接口
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}3.创建代理类并同样实现发送短信的接口
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method send()");
smsService.send(message);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method send()");
return null;
}
}4.实际使用
public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("java");
}
}运行上述代码之后,控制台打印出:
before method send()
send message:java
after method send()动态代理更加灵活。我们不需要对每个目标类都单独创建一个代理类(JDK动态代理机制),并且也不需要代理类实现接口,我们可以直接代理目标类( CGLIB 动态代理机制)。从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
JDK动态代理只能代理接口,底层是采用实现接口的方式实现的
例子:一个接口和一个实现类。
package com.powernode.mall.service;
/**
* 订单接口
**/
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单详情
*/
void detail();
/**
* 修改订单
*/
void modify();
}package com.powernode.mall.service.impl;
import com.powernode.mall.service.OrderService;
public class OrderServiceImpl implements OrderService {
@Override
public void generate() {
try {
Thread.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已生成");
}
@Override
public void detail() {
try {
Thread.sleep(2541);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单信息如下:******");
}
@Override
public void modify() {
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已修改");
}
}package com.powernode.mall;
import com.powernode.mall.service.OrderService;
import com.powernode.mall.service.impl.OrderServiceImpl;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 第一步:创建目标对象
OrderService target = new OrderServiceImpl();
// 第二步:创建代理对象
OrderService orderServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象);
// 第三步:调用代理对象的代理方法
orderServiceProxy.detail();
orderServiceProxy.modify();
orderServiceProxy.generate();
}
}以上第二步创建代理对象是需要大家理解的: OrderService orderServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象);
这行代码做了两件事:
Proxy类全名:java.lang.reflect.Proxy。这是JDK提供的一个类(所以称为JDK动态代理)。主要是通过这个类在内存中生成代理类的字节码。
其中newProxyInstance()方法有三个参数:
所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:
package com.powernode.mall.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimerInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}有了目标对象我们就可以在invoke()方法中调用目标方法了。代码如下:
package com.powernode.mall.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimerInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
// 通过构造方法来传目标对象
public TimerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 目标执行之前增强。
long begin = System.currentTimeMillis();
// 调用目标对象的目标方法
Object retValue = method.invoke(target, args);
// 目标执行之后增强。
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
// 一定要记得返回哦。
return retValue;
}
}package com.powernode.mall;
import com.powernode.mall.service.OrderService;
import com.powernode.mall.service.TimerInvocationHandler;
import com.powernode.mall.service.impl.OrderServiceImpl;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建代理对象
OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
// 调用代理对象的代理方法
orderServiceProxy.detail();
orderServiceProxy.modify();
orderServiceProxy.generate();
}
}大家可能会比较好奇:那个InvocationHandler接口中的invoke()方法没看见在哪里调用呀?
注意:当你调用代理对象的代理方法的时候,注册在InvocationHandler接口中的invoke()方法会被调用。也就是上面代码第24 25 26行,这三行代码中任意一行代码执行,注册在InvocationHandler接口中的invoke()方法都会被调用。
执行结果:

学到这里可能会感觉有点懵,折腾半天,到最后这不是还得写一个接口的实现类吗?没省劲儿呀?
你要这样想就错了!!!
我们可以看到,不管你有多少个Service接口,多少个业务类,这个TimerInvocationHandler接口是不是只需要写一次就行了,代码是不是得到复用了!!!!
而且最重要的是,以后程序员只需要关注核心业务的编写了,像这种统计时间的代码根本不需要关注。因为这种统计时间的代码只需要在调用处理器中编写一次即可。
到这里,JDK动态代理的原理就结束了。
CGLIB动态代理既可以代理接口,也可以代理类,底层是使用继承实现,所以既然是继承,那被代理的目标类就不能用final修饰。
我们准备一个没有实现接口的类(目标类),如下:
package com.powernode.mall.service;
public class UserService {
public void login(){
System.out.println("用户正在登录系统....");
}
public void logout(){
System.out.println("用户正在退出系统....");
}
}使用CGLIB在内存中为UserService类生成代理类,并创建对象:
package com.powernode.mall;
import com.powernode.mall.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建字节码增强器
Enhancer enhancer = new Enhancer();
// 告诉cglib要继承哪个类
enhancer.setSuperclass(UserService.class);
// 设置回调接口
enhancer.setCallback(方法拦截器对象);
// 生成源码,编译class,加载到JVM,并创建代理对象
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login();
userServiceProxy.logout();
}
}和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是net.sf.cglib.proxy.MethodInterceptor
编写MethodInterceptor接口实现类:
回调已经写完了,可以修改客户端程序了:
package com.powernode.mall;
import com.powernode.mall.service.TimerMethodInterceptor;
import com.powernode.mall.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建字节码增强器
Enhancer enhancer = new Enhancer();
// 告诉cglib要继承哪个类
enhancer.setSuperclass(UserService.class);
// 设置回调接口
enhancer.setCallback(new TimerMethodInterceptor());
// 生成源码,编译class,加载到JVM,并创建代理对象
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login();
userServiceProxy.logout();
}
}执行结果:

至此,CGLIB代理就写完了。
你理解了吗?
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=h70g0sv71wz