先看百度百科的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
定义总是抽象而晦涩难懂的,我们通过一个例子简单说明下。
假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的。明星就是一个「目标对象」,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。
用图表示如下:

简单来说,代理模式提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
通过代理模式,我们可以做到两点:

代理模式的主要角色如下:
Subject(抽象主题角色):定义 RealSubject 和 Proxy 角色都应该实现的接口。RealSubject(真实主题角色):真实类,也就是被代理类、委托类,用来真正完成业务服务功能。Proxy(代理类):用来代理和封装真实主题,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。代理模式有三种类型:
静态代理是指代理类在程序运行前就已经存在,这种情况下的代理类通常都是我们在 Java 代码中定义的。在程序运行之前,代理类的 .class 文件就已经生成。
静态代理需要先定义接口,被代理对象与代理对象一起实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

可以看见,代理类无非是在调用委托类方法的前后增加了一些操作。委托类的不同,也就导致代理类的不同。
举例:保存用户功能的静态代理实现
public interface IUserDao {
void save();
}
public class UserDaoImpl implements IUserDao {
@Override
public void save() {
System.out.println("保存用户信息");
}
}
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
//扩展了额外功能
System.out.println("开启事务");
target.save();
System.out.println("提交事务");
}
}
public class TestProxy {
@Test
public void testStaticProxy(){
//目标对象
IUserDao target = new UserDaoImpl();
//代理对象
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();
}
}
开启事务
保存用户信息
提交事务
静态代理总结:
如何解决静态代理中的问题呢?答案是可以使用动态代理方式。
代理类在程序运行时创建的代理方式被成为动态代理。
我们上面静态代理的例子中,代理类(UserDaoProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在代码中定义的,而是在运行时根据我们在代码中的“指示”动态生成的。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
我们先来介绍 JDK 自带的动态代理,代理类需要实现一个 InvocationHandler 接口,并且调用 Proxy 类的静态方法生成一个动态代理类。
还是上面保存用户信息的功能,我们用 jdk 动态代理实现。
public class UserDaoJdkProxy implements InvocationHandler {
private Object target;
public UserDaoJdkProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return returnValue;
}
}
public class TestProxy {
@Test
public void testJdkDynamicProxy() {
//创建一个实例对象,这个对象是被代理的对象
IUserDao target = new UserDaoImpl();
//输出目标对象信息
System.out.println(target.getClass());
//定义一个handler
InvocationHandler handler = new UserDaoJdkProxy(target);
//获得类的class loader
ClassLoader cl = target.getClass().getClassLoader();
//动态产生一个代理者
IUserDao proxy = (IUserDao) Proxy.newProxyInstance(cl, new Class[]{IUserDao.class}, handler);
//输出代理对象信息
System.out.println(proxy.getClass());
//执行代理方法
proxy.save();
}
}
class com.atu.design.pattern.proxy.UserDaoImpl
class com.sun.proxy.$Proxy4
开启事务
保存用户信息
提交事务
JDK 动态代理的原理是将被代理的每个方法都交给 invoke 这个方法去处理,这个代理类可以代理任何类,而不再是具体的固定类了。

JDK 动态代理的特点如下:
InvocationHandler 接口完成代理逻辑。JDK 动态代理有一个最致命的问题是它只能代理实现了某个接口的实现类,并且代理类也只能代理接口中实现的方法,要是实现类中有自己私有的方法,而接口中没有的话,该方法不能进行代理调用。
怎么解决这个问题呢?我们可以用 CGLIB 动态代理机制。
静态代理和 JDK 代理都需要某个对象实现一个接口,有时候代理对象只是一个单独对象,此时可以使用 Cglib 代理。
Cglib 代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
简单来说,Cglib 继承被代理的类,覆写其业务方法来实现代理。因为采用继承机制,所以不能对 final 修饰的类进行代理。

使用 cglib 需要引入 cglib 的 jar 包,如果你已经有 spring-core 的 jar 包,则无需引入,因为 spring 中包含了cglib。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
继续用上述保存用户的例子。
public class UserDaoCGProxy implements MethodInterceptor {
/**
* 维护一个目标对象
*/
private Object target;
public UserDaoCGProxy(Object target) {
this.target = target;
}
/**
* 为目标对象生成代理对象
*
* @return
*/
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("关闭事务");
return returnValue;
}
}
public class TestProxy {
@Test
public void testCglibProxy() {
//目标对象
UserDaoImpl target = new UserDaoImpl();
System.out.println(target.getClass());
//代理对象
UserDaoImpl proxy = (UserDaoImpl) new UserDaoCGProxy(target).getProxyInstance();
System.out.println(proxy.getClass());
//执行代理对象方法
proxy.save();
}
}
class com.atu.design.pattern.proxy.UserDaoImpl
class com.atu.design.pattern.proxy.UserDaoImpl$$EnhancerByCGLIB$$4d1e7fa9
开启事务
保存用户信息
关闭事务
InvocationHandler 接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。