代理模式是GoF23种设计模式之一。属于结构型设计模式。
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。
代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。
在程序中,对象A和对象B无法直接交互时。((现实生活中的婚介所)
在程序中,功能需要增强时。(现实生活中的房产中介)
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
代理(Proxy)模式分为三种角色:
业务场景:系统中有A、B、C三个模块,使用这些模块的前提是需要用户登录,也就是说在A模块中要编写判断登录的代码,B模块中也要编写,C模块中还要编写,这些判断登录的代码反复出现,显然代码没有得到复用,可以为A、B、C三个模块提供一个代理,在代理当中写一次登录判断即可。代理的逻辑是:请求来了之后,判断用户是否登录了,如果已经登录了,则执行对应的目标,如果没有登录则跳转到登录页面。【在程序中,目标不但受到保护,并且代码也得到了复用。】
代理模式的类图
OrderService接口是代理类和目标类的共同接口
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单详情
*/
void detail();
/**
* 修改订单
*/
void modify();
}
订单接口的实现类OrderServiceImpl即目标类
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("订单已修改");
}
}
第一种方案:直接修改Java源代码,在每个业务方法中添加统计逻辑
public class OrderServiceImpl implements OrderService {
@Override
public void generate() {
long begin = System.currentTimeMillis();
try {
Thread.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已生成");
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end - begin)+"毫秒");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
try {
Thread.sleep(2541);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单信息如下:******");
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end - begin)+"毫秒");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已修改");
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end - begin)+"毫秒");
}
}
第二种方案:编写一个子类继承 OrderServiceImpl,在子类中重写每个方法 (在子类中调用父类的方法)
public class OrderServiceImplSub extends OrderServiceImpl{
@Override
public void generate() {
long begin = System.currentTimeMillis();
super.generate();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
super.detail();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
super.modify();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
}
第三种方案:使用静态代理模式 , 编写一个代理类 OrderServiceProxy 实现 OrderService 接口(在代理对象中调用目标对象的目标方法)
public class OrderServiceProxy implements OrderService{ // 代理对象
// 目标对象
private OrderService orderService;
// 通过构造方法将目标对象传递给代理对象
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public void generate() {
long begin = System.currentTimeMillis();
// 执行目标对象的目标方法
orderService.generate();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
// 执行目标对象的目标方法
orderService.detail();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
// 执行目标对象的目标方法
orderService.modify();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
}
编写客户端程序
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建代理对象
OrderService proxy = new OrderServiceProxy(target);
// 调用代理对象的代理方法
proxy.generate();
proxy.modify();
proxy.detail();
}
}
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
在内存当中动态生成类的技术常见的包括
第一步: 提供订单接口和订单接口的实现类(目标类)
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单详情
*/
void detail();
/**
* 修改订单
*/
void modify();
}
订单接口的实现类(目标类)
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("订单已修改");
}
}
java.lang.reflect.InvocationHandler调用处理器接口的实现类,并且实现接口中的方法invoke , 在invoke方法中调用目标对象的目标方法并对其功能增强
// 专门负责计时的一个调用处理器对象。在这个调用处理器当中编写计时相关的增强代码。这个调用处理器只需要写一个就行了。
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);
//System.out.println("增强2");
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
// 注意这个invoke方法的返回值,如果我们调用的代理对象的代理方法有返回值的话,invoke方法必须将目标对象的目标方法执行结果继续返回。
return retValue;
}
}
客户端程序
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();
}
}
我们可以提供一个工具类:ProxyUtil,封装一个方法 , 只要传递一个目标对象就可以通过这个方法获取代理对象
public class ProxyUtil {
public static Object newProxyInstance(Object target){
// 底层是调用的还是JDK的动态代理。
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
}
}
提供一个工具类:ProxyUtil,封装一个方法 , 使用匿名内部类
public class ProxyUtil {
public static Object newProxyInstance(Object target){
// 底层是调用的还是JDK的动态代理。
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target){
// 目标对象
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)+"毫秒");
//如果我们调用的代理对象的代理方法有返回值的话,invoke方法必须将目标对象的目标方法执行结果继续返回。
return retValue;
}
});
}
}
客户端代码就不需要写那么繁琐
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建代理对象
OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target);
// 调用代理对象的代理方法
orderServiceProxy.detail();
orderServiceProxy.modify();
orderServiceProxy.generate();
}
}
java.lang.reflect.Proxy是 JDK 提供的一个动态代理类 , 它不是代理对象的类 , 主要是通过这个类在内存中生成代理类的字节码
**newProxyInstance方法翻译为:新建代理对象 **, 是Proxy类提供了一个创建代理对象的静态方法 , Proxy.newProxyInstance()方法的执行,做了两件事
JDK动态生成的代理对象和目标对象的唯一联系就是它们都实现了同一个接口 , 所以可以向下转型
关于newProxyInstance()方法的三个重要的参数的含义
自己动手写调用处理器接口的实现类,不会类爆炸 , 因为这种调用处理器只需要写一次就好。代码得到复用
InvocationHandler接口中invoke方法上的三个参数
InvocationHandler接口中invoke方法的调用
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)
使用CGLIB,需要引入它的依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
准备一个没有实现接口的类
public class UserService {
public void login(){
System.out.println("用户正在登录系统....");
}
public void logout(){
System.out.println("用户正在退出系统....");
}
}
和JDK动态代理原理差不多,在CGLIB中需要提供net.sf.cglib.proxy.MethodInterceptor的接口实现类
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前面增强
long begin = System.currentTimeMillis();
// 调用目标对象的目标方法
Object retValue = methodProxy.invokeSuper(target, objects);
// 后面增强
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
return retValue;
}
}
使用CGLIB在内存中为UserService类生成代理类,并创建代理对象
public class Client {
public static void main(String[] args) {
// 创建字节码增强器对象
// 这个对象是CGLIB库当中的核心对象,就是依靠它来生成代理类。
Enhancer enhancer = new Enhancer();
// 告诉CGLIB父类是谁。告诉CGLIB目标类是谁。
enhancer.setSuperclass(UserService.class);
// 设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler)
// 在CGLIB当中不是InvocationHandler接口,是方法拦截器接口:MethodInterceptor
enhancer.setCallback(new TimerMethodInterceptor());
// 创建代理对象
// 父类是UserService,子类这个代理类一定是UserService
UserService userServiceProxy = (UserService) enhancer.create();
// 建议大家能够把CGLIB动态代理生成的代理对象的名字格式有点印象。根据这个名字可以推测框架底层是否使用了CGLIB动态代理
// class UserService$$EnhancerByCGLIB$$82cb55e3 extends UserService{}
System.out.println(userServiceProxy);
// 调用代理对象的代理方法。
boolean success = userServiceProxy.login("admin", "123");
System.out.println(success ? "登录成功" : "登录失败");
userServiceProxy.logout();
}
}
对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
Enhancer是创建代理对象的类 , 类似于JDK的Proxy类
方法名 | 功能 |
---|---|
setSuperclass(Class clazz) | 设置代理类的父类即目标类 |
setCallback() | 设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler) |
create() | 创建代理对象 , 先在内存中生成目标类的子类,其实就是代理类的字节码。然后创建代理对象 |
MethodInterceptor接口中intercept()方法的参数