点一下关注吧!!!非常感谢!!持续更新!!!
大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html
目前已经更新到了:
- MyBatis(正在更新)
代理模式
概念介绍
代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用,代理模式的英文名字叫 Proxy,它是一种对象结构型模式,代理模式分为静态代理和动态代理。
主要角色
- 抽象主题(Subject):定义了真实对象和代理对象的共同接口,使得代理对象可以用在任何需要真实对象的地方。
- 真实主题(RealSubject):实现了抽象主题接口,是真正执行请求的对象。
- 代理(Proxy):实现了抽象主题接口,内部包含对真实主题的引用。代理对象控制对真实对象的访问,并可以在访问前后执行额外的操作。
代理模式的分类
远程代理(Remote Proxy)
为位于不同地址空间的对象提供本地代理,通过代理与远程对象进行通信。
示例:RMI(Remote Method Invocation)。
虚拟代理(Virtual Proxy)
用于创建开销较大的对象,只有在真正需要时才创建该对象。
示例:图像加载器延迟加载图片。
保护代理(Protection Proxy)
控制对真实对象的访问权限,根据访问者的权限执行不同操作。
示例:文件权限管理。
智能引用代理(Smart Reference Proxy)
在访问真实对象时进行额外操作,例如计数、日志记录、缓存等。
代理模式的优点
- 职责分离: 通过代理控制访问,可以将额外的职责(如安全性、日志记录)从真实对象中剥离出来。
- 增强功能: 在访问真实对象时,代理可以动态添加功能。
- 性能优化: 通过虚拟代理延迟加载,提升性能。
代理模式的缺点
- 增加复杂性: 由于引入了代理类,导致类的数量增加,系统复杂度提升。
- 性能问题: 某些场景下,代理可能增加系统的开销,尤其是远程代理。
编写代码
Person
创建一个抽象类,Person 接口,有一个 doSomething 的方法。
package icu.wzk.proxy;
public interface Person {
void doSomething();
}
Bob
package icu.wzk.proxy;
public class Bob implements Person {
@Override
public void doSomething() {
System.out.println("Bob do something!");
}
}
JdkDynamicProxy
package icu.wzk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkDynamicProxy implements InvocationHandler {
private Person target;
public JdkDynamicProxy(Person person) {
this.target = person;
}
public Person getTarget() {
return (Person) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before calling " + method.getName());
Person result = (Person) method.invoke(target, args);
System.out.println("After calling " + method.getName());
return result;
}
}
JdkDynamicProxyTest
package icu.wzk.proxy;
public class JdkDynamicProxyTest {
public static void main(String[] args) {
Person bob = new Bob();
bob.doSomething();
System.out.println("========================");
Person bob2 = new Bob();
JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(bob2);
Person proxyBob = jdkDynamicProxy.getTarget();
proxyBob.doSomething();
}
}
MyBatis 的体现
代理模式可以认为是 MyBatis 的核心使用的模式,正是因为这个模式的存在,我们只需要编写 Mapper 接口,不需要具体的实现,由 MyBatis 后台帮我们完成 SQL 的执行。
当我们使用 Configuration 的 getMapper 方法时,会调用 mapper.Registry.getMapper 方法,而该方法又会调用 mapperProxyFactory.newInstance(sqlSession) 来生成一个具体的代理:
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
// Constructor
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
// Getter for mapperInterface
public Class<T> getMapperInterface() {
return mapperInterface;
}
// Getter for methodCache
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
// Create a new instance of the proxy
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[]{mapperInterface},
mapperProxy
);
}
// Create a new instance of the mapper with a given SqlSession
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
在这里,通过 newInstance(SqlSession sqlSession)方法会得到一个 MapperProxy对象,然后调用 T newInstance(MapperProxy mapperProxy)生成代理对象然后返回,下面是 MapperProxy 的代码:
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object类中的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 如果是默认方法,使用invokeDefaultMethod处理
else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
// 将异常包装并抛出
throw ExceptionUtil.unwrapThrowable(t);
}
// 获取缓存的MapperMethod对象并执行
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
该 MapperProxy 实现了 InvocationHandler 接口,并实现了该接口的 invoke 方法,通过这种方式,我们需要编写 Mapper 接口类,当真正执行一个 Mapper 接口的时候,会转发给 MapperProxy.invoke 方法,而该方法则会调用后续的preparedStatement 等一系列方法,完成 SQL 的执行和返回。