介绍
代理模式通俗来讲就是为被代理的对象提供一种功能增强的方法,比如说你想为你的业务代码增加日志的功能,那么就可以通过创建代理对象来处理日志的功能,而被代理的对象就只需要关注自己业务代码的实现。代理模式将不属于业务逻辑的代码交给了代理对象来实现,使用代理模式的好处就是在不破坏被代理对象业务代码的前提下,对被代理对象的方法实现功能增强。
代理模式的实现分为静态代理和动态代理:
- 静态代理:一个代理类只能替一个代理主题(接口)代理工作
- 动态代理:一个代理工作处理器,可以替多个代理主题代理工作,只有代理工作内容一样就可以。
其中动态代理在Java中又有JDK动态代理和CGLB动态代理两种实现。
下面通过实际的代码案例来分别介绍静态代理和动态代理。
静态代理
案例:假设我们要给添加用户和添加商品的业务增加日志的功能。我们将添加的动作抽象为一个接口,然后分别定义添加用户和添加商品的实现类。
静态代理的实现步骤如下:
- 首先定义一个统一的接口:AddDao;
- 分别为添加商品和添加用户实现AddDao接口:UserAddDaoImpl、GoodsAddDaoImpl;
- 定义实现日志功能的代理类,同样实现AddDao接口;
- 创建代理对象,同时传入需要被代理的对象;
- 调用代理对象的方法;
public class StaticProxy {
public static void main(String[] args) {
// 创建UserAdd的代理对象
AddDao userAddProxyDao = new StaticProxyAddDao(new UserAddDaoImpl());
// 执行代理对象的方法
userAddProxyDao.add();
// 创建GoodsAdd的代理对象
AddDao goodsAddProxyDao = new StaticProxyAddDao(new GoodsAddDaoImpl());
// 执行代理对象的方法
goodsAddProxyDao.add();
}
}
interface AddDao {
/**
* 添加操作,可以实现User的添加或Goods的添加
*/
void add();
}
class UserAddDaoImpl implements AddDao {
@Override
public void add() {
System.out.println("执行添加User的业务代码...");
}
}
class GoodsAddDaoImpl implements AddDao {
@Override
public void add() {
System.out.println("执行添加Goods的业务代码...");
}
}
class StaticProxyAddDao implements AddDao {
/** 持有被代理者的引用,因为核心业务逻辑仍然交给被代理者自己完成 */
private AddDao dao;
public StaticProxyAddDao(AddDao dao) {
this.dao = dao;
}
/**
* 代理对象实现日志功能的增强
*/
@Override
public void add() {
System.out.println("log: add方法开始执行");
long start = System.currentTimeMillis();
dao.add();
long end = System.currentTimeMillis();
System.out.println("log: 运行时间:" + (end-start));
System.out.println("log: add方法执行结束");
}
}
运行结果:
log: add方法开始执行
执行添加User的业务代码...
log: 运行时间:0
log: add方法执行结束
log: add方法开始执行
执行添加Goods的业务代码...
log: 运行时间:0
log: add方法执行结束
动态代理
动态代理分为JDK动态代理和CGLIB动态代理。
两者之间的特点:
- JDK的动态代理机制只能代理实现了接口的类,而没有实现接口的类就不能实现JDK的动态代理;
- cglib动态代理是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强。使用cglib实现动态代理,完全不受代理类必须实现接口的限制。
- cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。
JDK动态代理
案例:为两个不同的接口A和B的实现类增加日志输出功能。
JDK动态代理实现步骤:
- 定义代理类,实现InvocationHandler接口,并重写invoke方法
- 调用Proxy.newProxyInstance方法来生成代理对象
newProxyInstance方法需要的参数有三个:
参数1:被代理者的类加载器对象:ClassLoader loader
参数2:被代理对象实现的接口们: Class<?>[] interfaces
参数3:实现InvocationHandler接口的类对象:InvocationHandler h
具体请看案例代码的实现:
public class JDKDynamicProxy {
public static void main(String[] args) {
/* 创建接口A的代理对象 */
A a = new AImpl();
// 1.参数1:被代理者的类加载器对象
ClassLoader loader = a.getClass().getClassLoader();
// 2.参数2:被代理对象实现的接口们
Class<?>[] interfaces = a.getClass().getInterfaces();
// 3.参数3:实现InvocationHandler接口的类对象
JDKPorxy jdkPorxyHandler = new JDKPorxy(a);
// 4.创建代理对象
A aProxy = (A) Proxy.newProxyInstance(loader, interfaces, jdkPorxyHandler);
// 5.调用方法
aProxy.a();
/* 创建接口B的代理对象 */
B b = new BImpl();
// 1.参数1:被代理者的类加载器对象
ClassLoader loader2 = b.getClass().getClassLoader();
// 2.参数2:被代理对象实现的接口们
Class<?>[] interfaces2 = b.getClass().getInterfaces();
// 3.参数3:实现InvocationHandler接口的类对象
JDKPorxy jdkPorxyHandler2 = new JDKPorxy(b);
// 4.创建代理对象
B bProxy = (B) Proxy.newProxyInstance(loader2, interfaces2, jdkPorxyHandler2);
// 5.调用方法
bProxy.b();
}
}
interface A {
void a();
}
interface B {
void b();
}
class AImpl implements A {
@Override
public void a() {
System.out.println("执行A业务代码 ...");
}
}
class BImpl implements B {
@Override
public void b() {
System.out.println("执行B业务代码 ...");
}
}
class JDKPorxy implements InvocationHandler {
/** 被代理的对象 */
private Object target;
public JDKPorxy(Object target) {
this.target = target;
}
/**
* 这个方法不是程序调用的,是一会执行代理类对象的方法时自动调用
* @param proxy 代理类的对象
* @param method 被代理者要执行的方法
* @param args 被代理者要执行的方法需要的实参列表
* @return 被代理者要执行的方法的返回结果
* @throws
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("log:" + method.getName() + "方法开始执行");
long start = System.currentTimeMillis();
Object value = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("log:" + "运行时间:" + (end-start));
System.out.println("log:" + method.getName() + "方法执行结束");
return value;
}
}
输出结果:
log:a方法开始执行
执行A业务代码 ...
log:运行时间:0
log:a方法执行结束
log:b方法开始执行
执行B业务代码 ...
log:运行时间:0
log:b方法执行结束
CGLIB动态代理
案例:为UserService中的getUserName方法增加日志功能。
CGLIB动态代理实现步骤:
- 定义代理类CglibProxy,实现MethodInterceptor接口,并实现intercept方法
- 提供获取动态代理类对象的方法getTargetProxy
具体请看代码的实现:
public class CglibDynamicProxy {
public static void main(String[] args) {
// 1.创建被代理的对象
UserService c = new UserService();
// 2.创建实现MethodInterceptor接口的对象
CglibProxy cglibProxy = new CglibProxy(c);
// 3.创建代理实例
UserService cProxy = (UserService) cglibProxy.getTargetProxy();
// 4.调用方法
System.out.println("打印返回结果:" + cProxy.getUserName(1001L));
}
}
class UserService {
public String getUserName(Long userId) {
System.out.println("执行业务代码: 获取用户名...");
return userId + ":UserName";
}
}
class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
/**
*
* @param proxy 代理对象,CGLib动态生成的代理类实例
* @param method 目标对象的方法,上文中实体类所调用的被代理的方法引用
* @param args 目标对象方法的参数列表,参数值列表
* @param methodProxy 代理对象的方法,生成的代理类对方法的代理引用
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("log:" + method.getName() + "方法开始执行");
long start = System.currentTimeMillis();
Object value = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("log:" + "运行时间:" + (end-start));
System.out.println("log:" + method.getName() + "方法执行结束");
return value;
}
public Object getTargetProxy() {
// Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展
Enhancer eh = new Enhancer();
// 1.将目标对象所在的类作为Enhancer类的父类
eh.setSuperclass(target.getClass());
// 2.通过实现MethodInterceptor实现方法回调
eh.setCallback(this);
// 3. 创建代理实例
return eh.create();
}
}
结果输出:
log:getUserName方法开始执行
执行业务代码: 获取用户名...
log:运行时间:0
log:getUserName方法执行结束
打印返回结果:1001:UserName