proxy design pattern
代理模式的概念、代理模式的结构、代理模式的优缺点、代理模式的使用场景、代理模式的实现示例(静态代理、jdk 动态代理、cglib 动态代理)、代理模式的源码分析
1、代理模式的概念
代理模式,为某个对象提供一种代理以控制对对象的访问。即客户端可通过代理对象间接访问目标对象,同时可限制、增强、修改目标对象的一些特性。访问者不想或者不能直接访问目标对象,代理对象作为目标对象和访问者之间的中介。
2、代理模式的结构
- 抽象目标:定义目标对象的行为。
- 具体目标:实现抽象目标,实现其具体行为。
- 代理类:实现抽象目标,拥有对具体目标的引用,同时可对目标对象进行增强处理。
3、代理模式的优缺点
- 优点:
- 代理对象在目标对象和访问者之间起到一个中介的作用,且可以保护目标对象。
- 可以增强目标对象的功能。
- 将访问者与目标对象分离,在一定程度上降低了系统的耦合度。
- 缺点:
- 当使用动态代理实现时,会使系统响应速度降低,因为其底层使用反射。
- 增加代理对象或代理工厂会增加系统的复杂度。
4、代理模式的使用场景
- 虚代理:当需要创建开销很大的对象时,只有用到才创建。
- 保护代理:控制目标对象的访问。如过滤器、防火墙。
- 智能指引:在访问对象时附加一些操作,如对象没有引用时释放资源。
- 远程代理:为一个对象在不同的地址空间提供局部代理。也包括通信代理,如 vpn 等。
常见代理模式:
- 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
- 缓存代理:当请求资源时,先从缓存中取,若没有,则再去数据库中取。
- 远程代理:远程对象的本地代表,通过它可以把远程对象当作本地对象来调用。
- 同步代理:主要在多线程间完成同步工作。
5、代理模式的实现示例
代理模式共有两类三种实现方式,即静态代理和动态代理,同时动态代理则可以使用 jdk 动态代理和 cglib 动态代理实现。
-
静态代理:代理类在编译期生成。
-
动态代理:代理类在运行期生成。
-
jdk 动态代理:
Java.lang.reflect 包中的 proxy 类和 InvocationHandler 接口提供了生成代理类的功能。jdk 动态代理有一个限制,那就是代理目标类必须实现一个或多个接口。
-
cglib 动态代理:
cglib 是一个强大的高性能代码生成包,它可以在运行时扩展 java 类或实现 java 接口。被广泛应用于许多 aop 框架,如 spring aop 和 dynaop。其底层使用一个小而快的字节码处理框架 ASM,通过 asm 来转换字节码并生成类。
-
jdk 动态代理与 cglib 动态代理区别:
- 使用 jdk 代理的目标类必须实现一个或多个接口。使用 cglib 代理的目标类则无需实现,但其不能被 final 修饰,因为其运用了继承关系。
- Jdk 1.8 以后 idk 动态代理效率要高于 cglib。
5.1、静态代理
抽象目标:
public interface Subject {
/**
* 定义目标类行为
*/
void behavior();
}
具体目标类:
public class TargetSubject implements Subject {
@Override
public void behavior() {
System.out.println("我是目标对象");
}
}
代理类:
public class ProxySubject implements Subject {
private Subject targetObject = new TargetSubject();
@Override
public void behavior() {
System.out.println("前置加强处理");
this.targetObject.behavior();
System.out.println("后置加强处理");
}
}
测试:
public class StaticProxyTest {
public static void main(String[] args) {
Subject proxy = new ProxySubject();
proxy.behavior();
}
}
测试结果:
前置加强处理
我是目标对象
后置加强处理
5.2、jdk 动态代理
抽象目标:
public interface Subject {
/**
* 定义目标类行为
*/
void behavior();
}
具体目标类:
public class TargetSubject implements Subject {
@Override
public void behavior() {
System.out.println("我是目标对象");
}
}
代理类:
public class ProxyFactory {
private Subject targetObject = new TargetSubject();
/**
* 获取代理对象
* @return
*/
public Subject getProxyObject() {
return (Subject) Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
((proxy, method, args) -> {
System.out.println("前置加强处理");
Object o = method.invoke(targetObject, args);
System.out.println("后置加强处理");
return o;
})
);
}
}
测试:
public class JdkProxyTest {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
Subject proxy = factory.getProxyObject();
proxy.behavior();
}
}
测试结果:
前置加强处理
我是目标对象
后置加强处理
5.3、cglib 动态代理
目标类:
public class TargetSubject {
public void behavior() {
System.out.println("我是目标对象");
}
}
代理类:
public class ProxyFactory implements MethodInterceptor {
private TargetSubject targetObject = new TargetSubject();
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置加强处理");
Object o1 = method.invoke(targetObject, objects);
System.out.println("后置加强处理");
return o1;
}
/**
* 获取代理对象
* @return
*/
public TargetSubject getProxyObject() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetSubject.class);
enhancer.setCallback(this);
return (TargetSubject) enhancer.create();
}
}
测试:
public class CglibProxyTest {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
TargetSubject proxy = factory.getProxyObject();
proxy.behavior();
}
}
测试结果:
前置加强处理
我是目标对象
后置加强处理
6、代理模式源码分析
mybatis 中的 MapperRegistry 类中的 getMapper 方法即使用了 jdk 的动态代理实现的代理模式。
// 相当于于客户端
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
...
}
// 代理工厂
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
...
}
// 代理类
public class MapperProxy<T> implements InvocationHandler, Serializable {
static {
Method privateLookupIn;
try {
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;
Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
// JDK 1.8
try {
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookup.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
e);
} catch (Exception e) {
lookup = null;
}
}
lookupConstructor = lookup;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
...
}