代理模式(Proxy Design Pattern ) 原始定义是:让你能够提供对象的替代品或 其占位符。代理控制着对于原对象的访问,并允许将请求提交给对象前后进行 一些处理。
代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策 略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理 模式可以提供非常好的访问控制。在一些著名开源软件中也经常见到它的身影,如Struts2的 Form元素映射就采用了代理模式(准确地说是动态代理模式)。
我们先看一下类图中的三个 角色的定义:
● Subject抽象主题角色 抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
● RealSubject具体主题角色 也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
● Proxy代理主题角色 也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制 委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
public interface UserDao {
void save();
}
publicclassUserDaoImplimplementsUserDao{
@Override
public void save() {
System.out.println("保存数据");
}
}
1、静态代理
public class UserDaoProxy implements UserDao {
private UserDao target;
public UserDaoProxy(UserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务"); //扩展额外的功能
target.save();
System.out.println("提交事务");
}
}
//测试静态代理
@Test
public void testStaticProxy(){
//目标类
UserDao dao = new UserDaoImpl();
//代理对象
UserDaoProxy proxy = new UserDaoProxy(dao);
proxy.save();
}
2、动态代理 2.1 jdk动态代理
Jdk动态代理对象必须实现接口,通过事件处理器 InvocationHandler 调用 invoke 方法;
public class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
//目标类使用的类加载器
target.getClass().getClassLoader(),
//目标对象实现的接口类型
target.getClass().getInterfaces(),
new InvocationHandler() { //事件处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
method.invoke(target,args);
System.out.println("提交事务");
return null;
}
}
);
}
}
@Test
public void testJdkProxy(){
UserDao userDao = new UserDaoImpl();
System.out.println(userDao.getClass()); //目标对象的信息
UserDao proxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();//获取代理对象
System.out.println(proxy.getClass());
proxy.save();//代理方法
}
java.lang.reflect
public interface InvocationHandler {
// 在代理实例上处理方法调用并返回结果。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
2.2 Cglib代理
cglib动态代理的被代理对象不需要实现接口,代理对象实现 MethodInterceptor 接口,实现回调方法 intercept
publicclassUserServiceImpl{
//查询功能
public Integer findUserList(){
return 18;
}
}
public class UserCglibProxy implements MethodInterceptor {
/**
* 生成CGLIB动态代理类方法
* @param target 需要被代理的目标类
* @return: java.lang.Object 代理类对象
*/
public Object getCglibProxy(Object target){
//增强器类,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//设置代理类的父类字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(this);
//创建动态代理对象,并返回
return enhancer.create();
}
/**
* 实现回调方法
* @param o 代理对象
* @param method 目标对象中的方法的Method实例
* @param args 实际参数
* @param methodProxy 代理类对象中的方法的Method实例
* @return: java.lang.Object
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Calendar instance = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(instance.getTime()) + "[ " +method.getName() +"] 查询信息...");
return methodProxy.invokeSuper(o, args);
}
}
@Test
public void testCglibProxy(){
//目标对象
UserServiceImpl userService = new UserServiceImpl();
System.out.println(userService.getClass());
//代理对象
UserServiceImpl proxy = (UserServiceImpl) new UserCglibProxy().getCglibProxy(userService);
System.out.println(proxy.getClass());
Integer i = proxy.findUserList();
System.out.println("用户信息: " +i);
}
3、开源实战
mybatis中的代理模式简单分析
publicclassMapperProxy<T>implementsInvocationHandler,Serializable{
private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
// 记录了关联的sqlSession对象
private final SqlSession sqlSession;
// mapper接口对应的Class对象
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;
// 用于缓存MapperMethod对象,其中key是mapper接口中方法对应的Method对象,value是对应的MapperMethod对象,MapperMethod对象会完成参数转换
// 以及SQL语句的执行功能,需要注意的是,MapperMethod中并不记录任何状态相关的信息,所以可以在多个代理对象之间共享
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
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 {
// 如果目标方法继承自Object,则直接调用目标方法
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);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
非常典型的,该 MapperProxy 类实现了 InvocationHandler 接口,并且实现了该接口的 invoke 方法。
通过这种方式,我们只需要编写 Mapper.java 接口类,当真正执行一个Mapper 接口的时候,就会转发给 MapperProxy.invoke 方法,而该方法则会调用后续的 sqlSession.crud>executor*ex.e**cute>prepareStatement 等一系列方法,完成SQL的执行和返回。