前言
在程序中,对象A与对象B不能直接交互时、某一功能需要增强时,目标对象需要被保护时,都需要通过动态代理来实现,在代理模式中:对于客户端程序来说,使用代理对象就好像在使用目标对象一样没有什么差别。
代理模式是23种设计模式之一,属于结构型设计模式。
代理模式的作用:为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个客户不想或不能直接地引用一个对象,此时可以通过一个“代理”(第三者)实现间接引用。代理对象在客户端和目标对象间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。
代理模式中的角色:
1、代理类(代理主题);
2、目标类(真实主题);
3、目标类和目标类得到公共接口(抽象主题);
客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
代理模式实现的两种形式:
1、静态代理;
2、动态代理;
一、静态代理
1、类与类之间的常见的6种关系:
泛化关系: 继承 is a的关系
关联关系:has a
2、模拟简单的静态代理:
OrderService接口
package org.powernode;
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单
*/
void detail();
/**
* 修改订单
*/
void modify();
}
OrderService接口实现类
package org.powernode;
public class OrderServiceImpl implements OrderService{
@Override
public void generate() {
try {
Thread.sleep(1558);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("正在申请的订单……");
}
@Override
public void detail() {
try {
Thread.sleep(155);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("查看订单详情……");
}
@Override
public void modify() {
try {
Thread.sleep(1255);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("修改订单");
}
}
OrderService代理类对象
package org.powernode;
代理对象
public class OrderServiceProxy implements OrderService{
//目标对象
private OrderService orderService;
// 通过构造方法将目标对象传递给代理对象
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public void generate() {
long start = System.currentTimeMillis();
//执行目标对象的目标方法
orderService.generate();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - start)+"毫秒");
}
@Override
public void detail() {
long start = System.currentTimeMillis();
//执行目标对象的目标方法
orderService.detail();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - start)+"毫秒");
}
@Override
public void modify() {
long start = System.currentTimeMillis();
//执行目标对象的目标方法
orderService.modify();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - start)+"毫秒");
}
}
客户端程序
package org.powernode;
public class Main {
public static void main(String[] args) {
//创建目标对象
OrderServiceImpl orderService = new OrderServiceImpl();
//创建代理对象
OrderServiceProxy orderServiceProxy = new OrderServiceProxy(orderService);
// 调用代理对象的代理方法
orderServiceProxy.generate();
orderServiceProxy.modify();
orderServiceProxy.detail();
}
}
测试结果
这种方式的优点:符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。所以这种方案是被推荐的。
代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。OrderServiceImpl是目标类。OrderServiceProxy是代理类。
如果系统中业务接口很多,一个接口对应一个代理类,显然也是不合理的,会导致类爆炸。怎么解决这个问题?动态代理可以解决。因为在动态代理中可以在内存中动态的为我们生成代理类的字节码。代理类不需要我们写了。类爆炸解决了,而且代码只需要写一次,代码也会得到复用。
二、动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是解决代理类的数量,提高代码复用性。
在内存当中动态生成类的技术常见的包括:
1、JDK动态代理技术:只能代理接口。
2、CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。
三、JDK动态代理
静态代理的时候,除了以上一个接口和一个实现类之外,还需要写一个代理类UserServiceProxy!在动态代理中UserServiceProxy代理类是可以动态生成的。这个代理类不需要写。我们直接写客户端程序即可:
调用后处理器
package org.powernode;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//调用处理器
public class TimerInvocationHandler implements InvocationHandler {
// 目标对象
private Object object;
// 通过构造方法来传目标对象
public TimerInvocationHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 目标执行之前增强。
long start = System.currentTimeMillis();
// 调用目标对象的目标方法
Object invoke = method.invoke(object, args);
// 目标执行之后增强。
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - start)+"毫秒");
return invoke;
}
}
客户端程序
package org.powernode;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
/**
* JDK动态代理
*/
// 第一步:创建目标对象
OrderServiceImpl orderService = new OrderServiceImpl();
// 第二步创建代理对象
OrderService service = (OrderService) Proxy.newProxyInstance(orderService.getClass().getClassLoader(),
orderService.getClass().getInterfaces(),
new TimerInvocationHandler(orderService));
// 调用代理对象的代理方法
service.modify();
service.detail();
service.generate();
}
}
四、CGLIB动态代理
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
使用CGLIB,需要引入它的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
目标对象
package org.powernode.CGLIB;
public class UserService {
public void login(){
System.out.println("用户正在登录系统....");
}
public void logout(){
System.out.println("用户正在退出系统....");
}
}
客户端
package org.powernode.CGLIB;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
//创建字节码增强器
Enhancer enhancer = new Enhancer();
//告诉cglib需要继承哪一个类
enhancer.setSuperclass(UserService.class);
//设置回调接口
//在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor
enhancer.setCallback(new TimerMethodInterceptor());
//生成源码,编译class,加载到Jvm,并创建对象
UserService userService = (UserService) enhancer.create();
userService.login();
userService.logout();
}
}
在MethodInterceptor的intercept()方法中调用目标以及添加增强:
package org.powernode.CGLIB;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {
/**
* MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
* 第一个参数:目标对象
* 第二个参数:目标方法
* 第三个参数:目标方法调用时的实参
* 第四个参数:代理方法
* 在MethodInterceptor的intercept()方法中调用目标以及添加增强:
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前增强
long begin = System.currentTimeMillis();
// 调用目标
Object retValue = methodProxy.invokeSuper(o, objects);
// 后增强
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
// 一定要返回
return retValue;
}
}
五、总结
使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。
从执行效率上看,Cglib动态代理效率较高。