代理模式-结合Mybatis学习


代理模式

  代理模式是一种使用代理对象来执行目标对象的设计模式,并且可以实现对目标对象功能增强。代理模式分为静态代理和动态代理两种。

静态代理

  静态代理较为简单,代理类继承被代理类接口的同事包含一个被代理引用,这样代理对象执行方法时实际是执行引用的方法,从而实现代理,网上有很多实例,这里不具体说了。

动态代理

  动态代理是基于java的反射技术实现的一种代理,借助于java的相关类实现,开发者可以很快实现动态代理,所以我们直接看代码。

  我们举个例子,一只披着羊皮的狼在跑,表面上我们看到的是一只羊在跑,实际上是一只狼在跑,我们可以认为羊“代理”了狼。

定义动物抽象类型:

public interface Animal
{
    public void run();
}

定义狼:

public class Wolf implements Animal
{
    @Override
    public void run()
    {
        System.out.println("wolf run");
    }
    
}

测试代码:

public class Test
{
    public static void main(String[] args)
    {
        Animal wolf = new Wolf();
        Animal sheep = (Animal)Proxy.newProxyInstance(wolf.getClass().getClassLoader() , wolf.getClass().getInterfaces(), new InvocationHandler(){
            @Override
            public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject)
                throws Throwable
            {
                return paramMethod.invoke(wolf, paramArrayOfObject);
            }
            
        });
        sheep.run();
    }
}

运行结果:

wolf run

我们看到结果上是狼在跑,如果是静态代理的话,这个例子会显得更贴切。

动态代理在Mybatis中应用

  Mybatis是Java开发中常用持久化框架,其主要功能是打通了Java对象与sql之间的连接,将接口操作映射为具体的sql语句。我们在使用时发现根本不需要自己去显式实例化或者在配置数据库接操作对象,只是定义了一些操作接口,Mybatis正式使用了动态代理实现了这样的操作。

  Mybatis的MapperProxy中代码:

@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 if (isDefaultMethod(method))
            {
                return invokeDefaultMethod(proxy, method, args);
            }
        }
        catch (Throwable t)
        {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }

  MapperProxy实现了动态代理中需要的InvocationHandler接口,实现了invoke方法,在这个方法中实现代理操作。比如对于用户接口UserDao中的queryById方法,其具体实现是通过MapperMethod调用execute方法得到结果,也正因此我们不需要实例化数据库操作对象就能实现数据库操作。

  回头看我们“披着羊皮的狼”的例子,例子中我们是实例化了“狼”,而Mybatis里面是没有实例化对象的,只是在代理中添加了方法对应的具体操作,我们也可以按照Mybatis的方式写个例子。

public class Test
{
    public static void main(String[] args)
    {
        Animal wolf = (Animal)Proxy.newProxyInstance(Animal.class.getClassLoader() , new Class[]{Animal.class}, new InvocationHandler(){
            @Override
            public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject)
                throws Throwable
            {
                if ("run".equals(paramMethod.getName()))
                {
                    System.out.println("wolf run");
                }
                return new Object();
            }
            
        });
        wolf.run();
    }
}

输出:

wolf run

在上面的代码中,我们没有实例化任何狼,但是通过jdk的反射,我们确实是实现了狼的run方法,与之前实例化的狼得到一样的结果。这个就跟Mybatis中的实现类似,有点“空手套白狼”的意思。