代理模式源码解析(jdk+spring+mybatis)

本文深入探讨了代理模式在Spring框架与Mybatis中的应用,详细解析了Spring中ProxyFactoryBean、JdkDynamicAopProxy及CglibAopProxy的作用,以及Mybatis中MapperProxyFactory和MapperProxy的实现机制,展示了代理模式如何提高代码灵活性与可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先是java.lang.reflect,也就是我们刚刚使用的Proxy这个类,这里面coding的时候,也就是debug的时候,

这个就是代理的一个典型应用,还有proxyFactoryBean,这个是一个代理的工厂bean,他呢首先是一个bean

@SuppressWarnings("serial")
public class ProxyFactoryBean extends ProxyCreatorSupport
		implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
		
他是一个什么bean呢,它是代理的工厂类,这么一个bean,那这里面的实现我们就不扩展来讲了,有兴趣的小伙伴可以来这里

看一下,那这个代理工厂bean呢,核心方法就是getObject这么一个方法,里面进行各种判断,同时在这里面生成一个单例对象,

生成一个PrototypeInstance,那我们在使用Spring的时候

	/**
	 * Return a proxy. Invoked when clients obtain beans from this factory bean.
	 * Create an instance of the AOP proxy to be returned by this factory.
	 * The instance will be cached for a singleton, and create on each call to
	 * {@code getObject()} for a proxy.
	 * @return a fresh AOP proxy reflecting the current state of this factory
	 */
	@Override
	@Nullable
	public Object getObject() throws BeansException {
		initializeAdvisorChain();
		if (isSingleton()) {
			return getSingletonInstance();
		}
		else {
			if (this.targetName == null) {
				logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
						"Enable prototype proxies by setting the 'targetName' property.");
			}
			return newPrototypeInstance();
		}
	}
	
不加prototype这种声明的话,他就是一个单例对象,那如果声明成prototype这种类型呢,他就是一个多例对象,也就是每次重新

调用它的时候呢,都会生成一个新的instance,那这个类呢比较复杂,有兴趣的可以来这里看一下,这个类也是Spring的一个核心类,

那Spring实现AOP的话,还有两个重要的类,分别是JdkDynamicAopProxy,它是AOP包下的,这里就是对JDK的动态代理进行了一些封装,

那JDK的动态代理在这里,final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable

我们还要看一下CglibAopProxy,这些都是在Spring中的一些体现

class CglibAopProxy implements AopProxy, Serializable

那在Mybatis当中当然也有使用代理模式,我们来看一下MapperProxyFactory这么一个类,他呢是Mybatis下边的,从名字就可以看出来,

它是Mapper的代理工厂,我们主要看一下这个方法

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
	
这里面传入了SqlSession,哪里调用他了呢,在MapperRegistry里边,有一个getMapper方法,这里面调用了工厂的newInstance,

那这个getMapper又在哪里调用了呢,
	
  @SuppressWarnings("unchecked")
  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);
    }
  }
	
他呢是在Configuration这里边,
	
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
	
getMapper调用了,Configuration里面是对应的各种配置,然后我们看一下里面的声明,方法声明,里面有addMapper,还有

处理一些ResultMap,比如ResultMap,这里面处理ResultMap,各种配置的处理,
	
  public ResultMap getResultMap(String id) {
    return resultMaps.get(id);
  }
	
再回到工厂里面我们看一下,这里面是怎么做的呢,首先它通过具体的各种参数,包括methodCache,

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

这个就是接口,然后创建一个MapperProxy,然后又调用了newInstance(mapperProxy);这个方法,这个方法就是

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

可以看到呢,Proxy.newProxyInstance,和我们刚刚写动态代理调用的方法是一样的,里面传入了ClassLoader,还有对应的interface,

还有MapperProxy,也就是这里生成了一个代理对象,然后把它进行返回,那我们来看一下MapperProxy这里面是怎么写的,进来,

我们主要关注一个invoke方法,

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

invoke方法自然是InvocationHandler这个接口所要实现的方法

public class MapperProxy<T> implements InvocationHandler, Serializable

接着来看,因为我们动态代理也实现了invoke方法, if (Object.class.equals(method.getDeclaringClass()))

这里进行一个判断,判断这个类型是不是Object类型,如果是的就直接返回,return method.invoke(this, args);

然后重点看这两行

final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);

对Method进行一个Cache,怎么Cache的呢,看一下,

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

这里像不像我们这里的享元模式呢,这里面可以理解为享元模式,return就开始执行execute,也就是说,通过这种方式,只需要编写

对应的Mapper.java的interface就可以了,我们用户订单,产品的Mapper呢,通过工具生成一个ProductMapper.java,

而当执行这个接口里面的insert,update操作的时候呢,就会给MapperProxy这个代理,然后就会调用invoke这个方法,如果没有

Object.class.equals(method.getDeclaringClass()),

final MapperMethod mapperMethod = cachedMapperMethod(method);

这里首先通过享元进行一个Cache,提高速度,mapperMethod.execute方法,我们进来看一下

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
  
这里面进行了各种判断,如果是INSERT,UPDATE,DELETE,SELECT,各种判断,还有FLUSH等等,然后把返回结果返回回去,

那这一块就是代理模式在Mybatis里面的应用,而且使用的是JDK的原生动态代理,通过实现InvocationHandler这个接口

就可以看出来,那代理模式在各种框架中,应用非常广泛,也是因为他的动态特性,把代理模式学好,为以后学习其他框架,

也是非常非常有帮助的,相信在代理模式这里,学到的不仅仅是代理模式,还有代理模式在源码中的应用

 

亿联网云建站平台介绍 亿联网云建站平台代理系统源码是昆山亿思互联信息科技有限公司旗下的服务产品。该平台是一个能在线快速搭建企业网站,为企业用户提供快捷、易用、适合SEO优化的互联网建站工具。 平台开设了两种运营服务体系:一种是自助服务,适合有网站设计经验的网络公司或有相关系统操作使用经验的人员; 另一种是全包服务,针对不会美化处理图片、不会操作网站内容管理系统的人群。 亿联网已帮助数百家企业用户有效的提升了消费者体验与降低运营成本。 亿联网目前已完美实现了三站合一功能,无论是PC(电脑端)、手机、平板(移动端)还是微信,只需要一个后台就能操作三种平台数据,网站中所有的数据图片都可以同步各个平台。 亿联网云建站平台代理系统源码功能特色 特色一:采用CMS内容管理系统,多种功能模型,丰富的扩展插件,系统架构完美植入SEO建站理念,使您的站点更利于做优化排名; 特色二:无需懂网页设计代码,选在模板即可创建站点,模板中所有图片、文字均可在后台修改; 特色三:无需会使用图片处理软件,平台采用了缩略图智能裁剪功能,按预设大小裁剪图片,大大提高网页打开的速度; 特色四:无需租用空间,注册完毕后选择模板,系统自动创建站点,无限容量,尽情使用; 特色五:采用多站切换模式,支持多个站点共用同一空间,站点自动识别域名进行管理及访问; 特色六:代理商可以一键打包下载所创建的站点源码 特色七:特色SEO云链系统,汇集众多使用本软件的站点成为庞大站群库;设置友情链接交换条件,自动交换,不花时间管理也能逐步获得优质外链,以提升网站排名; 特色八:一键生成多语版分站点,目前支持:英语、日语、法语、韩语; 亿联网云建站平台代理系统源码 v3.3.2 更新日志 调整的功能 01、优化所有插件配置文件,防止多次重复安装后没删除数据表造成插件无法安装 02、采用自动生成缩略图的方式优化了产品、案例等模块的缩略图调用方式 03、电子地图插件坐标获取方式调整为可修改 04、部门未设置权限时调整为无法管理后台 05、普通管理员增加的栏目设置为可以管理对应栏目的内容 06、恢复编辑器全屏按钮功能 07、后台左侧菜单在低分辨率下自动变窄 08、更换前端首页banner的js效果,更换后自适应图片高度 09、产品模块的产品价格为0时显示面议 10、PC站所有自定义模板的功能在手机站没有对应模板的情况下使用手机站默认模板 亿联网云建站平台代理系统源码前台页面 亿联网云建站平台代理系统源码后台管理 后台路径:域名/admin 默认 管理员账号:eycmsxz 密码:eycmsxz 后台页面 相关阅读 同类推荐:站长常用源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值