面试官:策略模式和代理模式有什么区别?

互联网全栈架构

共 19615字,需浏览 40分钟

 ·

2022-05-15 21:26

大家好,我是田哥,昨天一哥们面试被问到代理模式,刚好,我也正在写MyBatis源码分析:小白系列》专栏中的代理模式。

这里也说明一下,本文是MyBatis源码分析专栏中的一篇文章。

感兴趣的可以扫描了解一下我的MyBatis源码分析:小白系列》

本文目录:

两个有趣故事

老田买火车票

买火车票已经是很老的话题了。老田以前每年都会坐绿皮车回家过年,幸运的是这两年老家市区也有高铁了,少了些许奔波。现在就来回忆下当年的情景:每到年底最头疼的就是抢火车票,每次都需要去火车站排队买票。因为只要网上的票一开始出售,基本上都是手慢无,最后就只能在周末时去火车站买票了。但是,有一次无意间听说黄牛可以帮忙买票,只是要付点手续费。于是,后面每当自己抢不到票时,都会走黄牛,费用也不是很高。相比自己苦逼到火车站买票,还是轻松很多的。

ok,故事就讲到这里,下面再使用java代码来实现。

未知黄牛党之前

//火车站
public interface TrainStationService {
    //坐公交去火车站
    void byBus();
    //排队买票
    void lineUp();
    /**
     * 买票
     * @param idCard  身份证
     * @param address 去往地址
     * @param date    日期
     */

    void buy(String idCard, String address, String date);
}
//绿皮车站(老火车站)
public class GreenSkinTrainStationServiceImpl implements TrainStationService {

    @Override
    public void byBus() {
        System.out.println("坐公交去火车站");
    }

    @Override
    public void lineUp() {
        System.out.println("在火车站苦逼的排队中....");
    }

    @Override
    public void buy(String idCard, String address, String date) {
        System.out.println(idCard + "买到了一张通往" + address + "的绿皮车票,乘坐日期:" + date);
    }
}
//Client可以理解为老田
public class Client {
    public static void main(String[] args) {
        TrainStationService trainStationService = new GreenSkinTrainStationServiceImpl();
        trainStationService.byBus();
        trainStationService.lineUp();
        trainStationService.buy("423268199901011234""老家""2019年2月1日");
    }
}

经过一系列的折腾,老田的结果:

坐公交去火车站 在火车站苦逼的排队中.... 423268199901011234 买到了一张通往老家的绿皮车票,乘坐日期:2019年2月1日

知道黄牛党以后

//黄牛党
public class CattlePerson implements TrainStationService{
    private TrainStationService trainStationService;

    public CattlePerson() {
        this.trainStationService = = new GreenSkinTrainStationServiceImpl();
    }

    @Override
    public void byBus() {
        trainStationService.byBus();
    }
    @Override
    public void buy(String idCard, String address, String date) {
        System.out.println("收手续费");
        this.byBus();
        this.lineUp();
        trainStationService.buy(idCard, address, date);
        System.out.println("黄牛党把买到的票给老田");
    }
    @Override
    public void lineUp() {
        trainStationService.lineUp();
    }

}
//Client可以理解为老田
public class Client {
    public static void main(String[] args) {
        CattlePerson cattlePerson=new CattlePerson();
        cattlePerson.buy("423268199901011234""老家""2019年2月1日");
    }
}

最后老田买车票结果是这样的:

黄牛党收手续费 坐公交去火车站 在火车站苦逼的排队中.... 423268199901011234 买到了一张通往老家的绿皮车票,乘坐日期:2019年2月1日 黄牛党把买到的票给老田

最终老田还是搞到票了,但是不用折腾自己了。

老田玩王者农药

尽管前两年,乃至现在王者荣耀还是挺火的。也许有的人没玩过,但是你绝对见过别人玩过。民间传说“玩Dato的看不起玩英雄联盟的,然而玩英雄联盟的看不起玩王者荣耀”。哈哈哈,笑笑就可以了,管那么多,自己开心就好。这两个游戏本人都玩过,但是没有很投入。

但是去年在同事老王的带领下搞起了王者荣耀,疯狂的时候可以玩通宵,大号不能玩了玩小号,小号不行就换 QQ 号......一直想上王者,悲哀的是一直没上,记得最疯狂的时候还是到了差一颗星就到星耀二,但是始终是上不了王者。无意间看到一个牛逼的玩家,说给他 388 块就能上王者,才发现原来可以找人代打的,后面我细爬了一下,更恐怖的是有专门代打游戏的公司。由此可知,或许很多王者都是别人带着或者代打上去的吧。

下面用java来实现上面的两种常见:

老田玩王者农药

public interface PlayerService {
    //登录
    void login();
    //挑战排位赛
    void challenge();
    //升级
    void upgrade();
}
public class PlayerServiceImpl implements PlayerService {
    private String userName;
    private String password;

    public PlayerServiceImpl(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    @Override
    public void login() {
        //校验用户名和密码
        System.out.println("用户:" + this.userName + ",密码:" + this.password);
        System.out.println(this.userName + "登录了");
    }

    @Override
    public void challenge() {
        System.out.println("挑战排位赛比赛中...");
    }

    @Override
    public void upgrade() {
        System.out.println(this.userName + "又升级了");
    }
}

//Client当做老田
public class Client{
    public static void main(String[] args) {
        PlayerService playerService = new PlayerServiceImpl("老田""123456");
        playerService.login();
        playerService.challenge();
        playerService.upgrade();
    }
}

老田玩王者农药情况:

用户:老田,密码:123456 老田登录了 挑战排位赛比赛中... 老田又升级了

老田找人代打上王者

也是要使用老田的账号登录,也是要一场一场的排位,最后达上王者。不知道能代打游戏之前老田是自己傻傻的一场一场打的。

//替人打游戏的人或公司
public class PlayerProxy implements PlayerService {
    private PlayerService playerService;
    /**
     * 费用
     */

    private BigDecimal fee;
    private String userName;
    private String password;

    public PlayerProxy(String userName, String password, BigDecimal fee) {
        this.playerService = new PlayerServiceImpl(userName, password);
        this.fee = fee;
        this.userName = userName;
        this.password = password;
    }

    @Override
    public void login() {
        playerService.login();
    }

    @Override
    public void player() {
        System.out.println("代理商赚取手续费" + fee);
        this.login();
        playerService.player();
        this.upgrade();
    }

    @Override
    public void upgrade() {
        playerService.upgrade();
    }
}
//老王
public class Client {
    public static void main(String[] args) {
        //告诉代理商或者代打的人账户名+密码+费用
        PlayerProxy playerProxy = new PlayerProxy("老田""123456", BigDecimal.valueOf(100));
        playerProxy.player();
    }
}

最后老田只要等着别人给他打好上王者。

代理商收手续费100 用户:老田,密码:123456 老田登录了 挑战排位赛比赛中... 老田满五星升级

上面两个故事中分别有两个很重要的角色:黄牛党和代打游戏的人。

有黄牛党后老田不用关心票是怎么买的、有了代打游戏的人以后老田也不用关系是怎么上王者的,都全权交给他们去干。

同样的生活中的例子:相亲找媒婆,租房子找中介等,都是中间有个代你办事干活的人。

以上举的例子就是传说中的代理模式。

代理模式的定义

Provide a surrogate or placeholder for another object to control access to it.

代理模式就是由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式是GOF的23种设计模式之一(结构型模式)。

代理模式也叫委托模式,它是一种基本设计技巧,许多其他的设计模式,比如:状态模式,策略模式,访问者模式本质上是在更特殊的场合采用了委托模式。

角色

Subject:抽象主题,抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,没有特殊要求。

RealSubject:真实主题,也叫被委托角色,被代理角色。他是业务逻辑的具体执行者。

Proxy:代理,也叫委托类,代理类。它负责对真实角色的应用,把所有抽象主题定义的方法限制委托给真实主题去实现,并且在真实主题角色处理完毕的前后做预处理和善后处理工作。

通用UML

通用java代码

以上三个角色的java代码实现

public interface Subject {
    void request();
}
public class RealSbject implements Subject {
    @Override
    public void request() {
      System.out.println("访问具体主题角色方法...");
    }
}
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        this.realSubject = new RealSubject();
    }

    @Override
    public void request() {
        preRequest();
        realSubject.request();
        postRequest();
    }

    public void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }

    public void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }
}
public class ProxyClient {
    public static void main(String[] args) {
        Proxy proxy=new Proxy();
        proxy.request();
    }
}

运行结果:

访问真实主题之前的预处理。访问具体主题角色方法... 访问真实主题之后的后续处理。

优缺点

优点:效率高,只要获取代理对象并执行就结束了

缺点:如果接口增加新方法,被代理类得改,代理也得改;每一个被代理类都得有一个代理类。如果系统很大,那么关于代理类的维护的代价是很大的。

装饰器模式

装饰器模式是结构性模式之一,装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。也可以称之为包装模式

特点:动态添加或者覆盖被装饰的接口/抽象类行为。

关系:装饰者和被装饰者有着接口/抽象类层次关系。

角色

抽象组件(Component)角色:定义一个将要接收附加责任的类,即继承该抽象类的类都有了装饰和被装饰的能力。

具体组件(ConcreteComponent)角色:可以被动态加上新行为,被装饰者修饰的类。

装饰者(Decorator)角色:装饰者抽象类,继承该类都具有装饰者的能力。

具体装饰者(ConcreteDecorator)角色:为具体组件添加新行为。

通用UML图

通用java代码

//抽象构件
public abstract class Component {
    public abstract void operate();
}
//具体构件(最终被装饰的类)
public class ConcreteComponent extends Component {
    @Override
    public void operate() {
        System.out.println("doSomething");
    }
}
//抽象装饰者
public class Decorator extends Component {
    private Component component;
    //通过构造函数传递被装饰者
    public Decorator(Component component) {
        this.component = component;
    }
    //委托给被装饰者执行
    @Override
    public void operate() {
        this.component.operate();
    }
}
//具体装饰类
public class ConcreteDecorator extends Decorator {
    //定义被装饰者
    public ConcreteDecorator(Component component) {
        super(component);
    }

    private void before() {
        System.out.println("在调用operate方法前给你添加点东西");
    }

    @Override
    public void operate() {
        //调用前就行装饰
        this.before();
        //调用具体被装饰类的方法
        super.operate();
        //调用后进行装饰
        this.after();
    }

    private void after() {
        System.out.println("在调用operate方法后给你添加点东西");
    }
}
//测试
public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Decorator decorator = new ConcreteDecorator(component);
        decorator.operate();
    }
}

运行结果:

在调用operate方法前给你添加点东西 doSomething 在调用operate方法后给你添加点东西

demo 案例

简单支付场景,请看代码实现

public interface PayService {
    //支付
    void pay();
}
//被装饰的类--支付场景
public class PayServiceImpl implements PayService {
    @Override
    public void pay() {
        System.out.println("执行PayServiceImpl--的--支付--支付方法");
    }
}
public interface PayParamsMsgService extends PayService {
    //支付
    @Override
    void pay();
    //发站内信
    void sendMsg();
    //参数校验
    void checkParams();
}
//装饰类
public class PayParamsMsgServiceImpl implements PayParamsMsgService {
    private PayService payService;

    public PayParamsMsgServiceImpl(PayService payService) {
        this.payService = payService;
    }
    //没有被装饰的支付=支付
    //装饰后的支付=支付前进行参数校验-->支付-->支付后发生信息
    @Override
    public void pay() {
        checkParams();
        payService.pay();
        sendMsg();
    }
    @Override
    public void sendMsg() {
        System.out.println("发送支付成功站内信");
    }
    @Override
    public void checkParams() {
        System.out.println("校验余额是否足够,校验密码是否非法登录等");
    }
}
public class DemoClient {
    public static void main(String[] args) {
        PayService payService=new PayServiceImpl();
        PayService payService1=new PayParamsMsgServiceImpl(payService);
        payService1.pay();
    }
}

运行结果:

校验余额是否足够,校验密码是否非法登录等 执行PayServiceImpl--的--支付--支付方法 发送支付成功站内信

优缺点

优点

  • 装饰类和被装饰类可以独立发展,而不会相互耦合。说明白了就是Component类无须知道Decorator类,Decorator类是从外部来扩展Component,而Decorator也不知道具体的构件。
  • 装饰模式是继承关系的一种替代方案。咱们看装饰类,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
  • 装饰模式可以动态扩展一个实现类的功能,这不需多说,装饰模式定义就是这么讲的。

缺点:多层装饰比较复杂。

使用场景

1、扩展一个类的功能

2、动态增加功能,动态撤销。

开发过程中,一般是针对老系统或者已经正常使用的功能之外添加一些新的功能。不建议新系统使用装饰器设计模式。

代理模式 VS 装饰器模式

这两个模式在实现上很容易陷入难分难解的地步,但是请记住他们的本质区别:装饰模式重在装饰、增强,而代理模式重在访问的权限控制。

如果你写了一个方法,后续考虑要给这个方法添加一些功能,可以选择装饰模式。如果你写了一堆方法,后续考虑统一给这些方法添加功能(比如日志记录,资源回收),可以选择代理模式。

静态代理

字面意义就是静态的代理模式,在上面的几个代理模式案例中,所有的代理类都是咱们自己手写的,所以是已经定好的,也就可以理解为是死的、静态的,由此可知上面案例都是静态代理模式。

定义:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

代理模式就是静态代理模式。就是被代理对象和代理类都要实现同一个接口。

既然有静态代理,不妨假设一下,这个代理不需要咱们来写,那岂不就是动态代理吗?

动态代理

何为动态代理?

动态代理是在实现阶段不用关心代理谁,而是在运行阶段才指定代理哪一个对象。上面得案例中的代理类必须是自己创建的,人都知道偷懒,所以完全不想写那么多代码了(代理类),所以得动态生成。JDK动态代理能解决这个问题;

JDK动态代理

JDK动态代理是一种代理模式实现之一,只能代理接口。

通用java代码实现
//第一步
public interface Subject {
    void request();
}
//第二步
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("访问具体主题角色方法...");
    }
}
//第三步
public class JDKDynamicProxy implements InvocationHandler {
    /**
     * 将目标对象传入进行代理
     */

    private Object target;

    public JDKDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 获取被代理接口实例对象
     */

    public  getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    /**
    *实现增强逻辑和对真实对象方法的调用
    *@param proxy 生成的动态代理对象
    *@param method 动态代理在客户端执行的方法
    *@param args 该方法的参数列表
    */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    private void before() {//方法执行前
        System.out.println("方法执行前");
    }

    private void after() {//方法执行后
        System.out.println("方法执行后");
    }
}
//第四步
public class Client {
    public static void main(String[] args) {
        //保存生成的代理类的字节码文class文件:$Proxy0.class
        //下面会细说$Proxy0.class
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles""true");
        Subject target = new RealSubject();
        JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(target);
        Subject proxy = jdkDynamicProxy.getProxy();
        proxy.request();
    }
}

运行结果:

方法执行前 访问具体主题角色方法... 方法执行后

现在有了JDKDynamicProxy,那么我们就再也不需要手动写代理类了,而是交给JDK去帮我们自动创建。细心的人你应该会发现JDK的动态代理通用java代码和装饰模式几乎一毛一样,但是请记住他们的目的不同,装饰器设计模式是给一个对象动态的增加方法(功能),而动态代理的目的是控制被代理对象的访问权。

JDK动态代理四大步骤
  1. 定义业务接口
  2. 被代理对象实现业务接口
  3. 通过Proxy的静态方法newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)创建一个代理对象
  4. 使用代理对象
JDK动态代理源码分析

JDK动态代理四个步骤中最让人迷惑的就是第三步,第三步到底做了什么?(源码分析挺乏味的,但是请记住你不读源码很多东西永远只停留在了解或者皮毛阶段,阅读源码至少会让你知道怎么写出高质量的代码)

进入java.lang.reflect.Proxy类中的

 public static Object newProxyInstance(ClassLoader loader,  Class[] interfaces,                                            InvocationHandler h) throws IllegalArgumentException {
        // 判断参数h是否为空,咱们这里的h=new JDKDynamicProxy()
        Objects.requireNonNull(h);
        //接口数组先拷贝一份
        final Class[] intfs = interfaces.clone();
        //安全检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            //检查创建代理类所需的权限
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        //最关键的地方
        //查询(在缓存中已经有)或生成指定的代理类的class对象。后面细说
        Class cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //用invocationHandler生成构造函数
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
}
private static Class getProxyClass0(ClassLoader loader, Class... interfaces) {
        //// 限定代理的接口不能超过65535个
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //// 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类
        return proxyClassCache.get(loader, interfaces);
    }

//Proxy的静态内部类ProxyClassFactory    
private static final class ProxyClassFactory
        implements BiFunction<ClassLoaderClass[], Class>
    
{
        // prefix for all proxy class names 代理类前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // next number to use for generation of unique proxy class names
        //生成代理类名称的计数器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class apply(ClassLoader loader, Class[] interfaces) {

            Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class intf : interfaces) {
                //校验类加载器是否能通过接口名称加载该类
                Class interfaceClass = null;
                try {
                    //根据接口全限定类名和classLoader,获取到接口class对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //如果两次接口class对象不一致,直接抛出异常,说明创建错误
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                //校验该类是否是接口类型,这里就是证明为什么JDK动态代理是代理接口的。
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                //
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
           //代理类包名
            String proxyPkg = null;     
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            //非public接口,代理类的包名与接口的包名相同
            for (Class intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
            //如果都是public接口设定全限定类名com.sun.proxy.
            if (proxyPkg == null) {
                //public代理接口,使用ReflectUtil.PROXY_PACKAGE=com.sun.proxy.包名
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            //为代理类生成名字
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            //真正生成代理类的字节码文件的地方:ProxyGenerator类中
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
            try {
                //使用类加载器将代理类的字节码文件加载到JVM中
                return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }    
    //ProxyGenerator类中
    public static byte[] generateProxyClass(final String var0, Class[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        //是否要将生成代理类的字节码文件保存到磁盘中
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }
                        //如果找不到生成$Proxy0.class可以write方法一直追中下去,将会看到具体的保存路径
                        //WindowsFileSystemProvider
                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        return var4;
 }
//具体生成class字节码文件的方法
 private byte[] generateClassFile() {
        //将object类当中的 hashcode,equals,toString方法添加到动态代理类当中
        //addProxyMethod方法后面有分析
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;
        int var3;
        Class var4;
        //遍历父接口数据
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            //获取每个接口当中的方法
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;
             //遍历接口当中的方法,将接口当中的方法都添加至动态代理类当中
            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }
        Iterator var11 = this.proxyMethods.values().iterator();
       //检查代理类当中的返回类型
        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }
        Iterator var15;
        try {
             // 将构造方法添加至代理类当中的方法集合中
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();
            //遍历代理类当中的方法,此处使用两层循环,是因为方法签名相同的,可能有多个方法
            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();
                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;"10));
                    this.methods.add(var16.generateMethod());
                }
            }
            // 将静态代码块添加进去
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }
        //方法个数不能超过65535
        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {//属性不能超过65535
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            //编写最终类文件
            //在开始编写最终类文件之前,确保为下面的项目保留常量池索引
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }
            //设置只读,在这之前不允许在常量池中增加信息,因为要写常量池表
            this.cp.setReadOnly();

            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                //magic魔法数字
                var14.writeInt(-889275714);
                //次版本
                var14.writeShort(0);
                //主版本
                var14.writeShort(49);
                this.cp.write(var14);
                //访问表示
                var14.writeShort(this.accessFlags);
                //本类名称
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                //接口
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }
                //字段
                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }
                //方法
                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }
                //类文件属性:对于代理类来说没有类文件属性
                //// (no ClassFile attributes for proxy classes)
                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
   //给代理类添加方法
    private void addProxyMethod(Method var1, Class var2) {
        //方法名
        String var3 = var1.getName();
        //方法参数类型
        Class[] var4 = var1.getParameterTypes();
        //返回值类型
        Class var5 = var1.getReturnType();
        //异常类型
        Class[] var6 = var1.getExceptionTypes();
        //方法签名
        String var7 = var3 + getParameterDescriptors(var4);
        //根据方法签名却获得proxyMethods的Value
        Object var8 = (List)this.proxyMethods.get(var7);
        //处理多个代理接口中重复的方法的情况
        if (var8 != null) {
            Iterator var9 = ((List)var8).iterator();

            while(var9.hasNext()) {
                ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
                if (var5 == var10.returnType) {
                    //归约异常类型以至于让重写的方法抛出合适的异常类型,我认为这里可能是多个接口中有相同的方法,
                    //而这些相同的方法抛出的异常类型又不同,
                    //所以对这些相同方法抛出的异常进行了归约
                    ArrayList var11 = new ArrayList();
                    collectCompatibleTypes(var6, var10.exceptionTypes, var11);
                    collectCompatibleTypes(var10.exceptionTypes, var6, var11);
                    var10.exceptionTypes = new Class[var11.size()];
                    //将ArrayList转换为Class对象数组
                    var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
                    return;
                }
            }
        } else {
            var8 = new ArrayList(3);
            this.proxyMethods.put(var7, var8);
        }
        ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
    }

自此源码分析完毕。现在回过头去看自动生成的代理类里到底有什么东东?

package com.sun.proxy;

import com.tian.swagger.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//继承了Proxy,实现了Subject
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    //代理类的构造函数,其参数正是是InvocationHandler实例,
    //Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //上面三个方法对应的是hashCode、toString、equals

    //重写接口Subject中定义的方法request
    public final void request() throws  {
        try {
            //h就是Proxy类中的变量protected InvocationHandler h;
            //this就是当前$Proxy0对象;
            //m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request", new Class[0]);
            //即是通过全路径名,反射获取的目标对象中的真实方法加参数
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    // 静态代码块对变量进行一些初始化工作
    static {
        try {
            //这里每个方法对象 和类的实际方法绑定
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.tian.swagger.proxy.Subject").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

当代理对象生成后,最后由InvocationHandler的invoke()方法调用目标方法:在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的invoke()方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法。具体方法签名如下:

invoke(Object Proxy,Method method,Object[] args)

从反编译源码分析调用invoke()过程:从反编译后的源码看$Proxy0类继承了Proxy类,同时实现了Subject接口,即代理类接口,所以才能强制将代理对象转换为Subject接口,然后调用$Proxy0中的request()方法。$Proxy0中request()源码:

    //接口Subject中定义的放任request
    public final void request() throws  {
        try {
             //h就是Proxy类中的变量protected InvocationHandler h;
            //this就是当前$Proxy0对象;
            //m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request", new Class[0]);
            //即是通过全路径名,反射获取的目标对象中的真实方法加参数
            //后面那个null是方法参数,因为Subject的方法request参数为空
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

所以成功的调到了InvocationHandler中的invoke()方法,但是invoke()方法在我们自定义的JDKDynamicProxy中实现,JDKDynamicProxy中的invoke()方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     before();
     Object result = method.invoke(target, args);
     after();
     return result;
}

终于回到咱们写的代码里了。不容易吧,简单JDK动态代理绕了这么大一圈。

通过以上分析应该收获:

1,JDK动态代理只能代理接口,源码里有判断,不是接口就直接抛异常

//校验该类是否是接口类型,这里就是证明为什么JDK动态代理是代理接口的。
 if (!interfaceClass.isInterface()) {
    throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");
  }

2,代理类是如何生成的

3,代理类结构

4,是如何调到自己定义的invoke方法的

JDK动态代理源码总结

1,生成代理类$Proxy.class字节码

Proxy.newProxyInstance()-->getProxyClass()--->ProxyClassFactory.apply()--->ProxyGenerator.getGenerateProxyClass()

2,调用Constructor.newInstance()实例化

3,调用第2步生成的代理对象中的invoke方法,最后调到JDKDynamicProxy中的invoke方法,最后调到被代理对象的实现方法中

通过以上的分析我们知道了JDK动态代理的使用和原理,也领略Java动态代理的强大之处,但是不难看出来使用JDK动态代理也有着它的局限性,JDK动态代理是在JVM内部动态的生成class字节码对象(代理类.class),但是JDK动态代理只能针对接口进行操作,也就说它只适用于对接口的实现类去进行代理。因为有时候咱们希望对普通类进行代理,使用JDK动态代理就无法搞下去,于是另外一个强大的动态代理CGlib出现了,CGlib就能解决对普通类进行代理的问题。

CGlib动态代理

定义
Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

CGlib(Code Generation Library)是一个开源项目;是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,它被AOP、测试、数据访问框架用于生成动态代理对象和拦截字段访问。

CGlib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类;CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;所以CGlib可以为无接口的类直接做代理,当然有接口的类也是可以的并无影响。

pom

<dependency>
     <groupId>cglibgroupId>
     <artifactId>cglibartifactId>
     <version>3.2.12version>
dependency>
Java代码
public class TicketConcrete {
    public void buy() {
        System.out.println("买到火车票了");
    }
}
public class CattlePersonMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("黄牛去买火车票");
        Object res = methodProxy.invokeSuper(o, objects);
        System.out.println("黄牛把火车票给老田");
        return res;
    }
}
public class Client {
    public static void main(String[] args) {
        CattlePersonMethodInterceptor cattlePerson = new CattlePersonMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TicketConcrete.class);
        enhancer.setCallback(cattlePerson);
        TicketConcrete person = (TicketConcrete) enhancer.create();
        person.buy();
    }
}

运行结果:

黄牛去买火车票 买到火车票了 黄牛把火车票给老田

上面已经提到,CGlib库是基于ASM的上层应用。对于代理没有实现接口的类,CGlib非常实用。本质上来说,对于需要被代理的类,它只是动态生成一个子类以覆盖非final的方法,同时绑定钩子回调自定义的拦截器。值得说的是,它比JDK动态代理还要快。

CGlib 部分源码分析

cglib.jar包目录:

本文只关心proxy目录(CGlib动态代理相关主要类目录).

net.sf.cglib.
            core:底层字节码操作类;大部分与ASP相关。
            transform:编译期、运行期的class文件转换类。
            proxy:代理创建类、方法拦截类。
            reflect:更快的反射类、C#风格的代理类。
            util:集合排序工具类
            beans:JavaBean相关的工具类

源码从enhancer.create()开始看,因为这里返回的Object直接强转为TicketConcrete(被代理的类)

Enhancer类中

//这里没有具体什么
public Object create() {
       classOnly = false;
       argumentTypes = null;
       return createHelper();
}
//构建帮助器
private Object createHelper() {
   //提前验证回调类型和filter
   preValidate();
    //生成key
   Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
          ReflectUtils.getNames(interfaces),
          filter == ALL_ZERO ? null : new WeakCacheKey(filter),
          callbackTypes,
          useFactory,
          interceptDuringConstruction,
          serialVersionUID);
    //复制个当前key
    this.currentKey = key;
    //使用key构建代理对象,个人认为这里代码其实可以直接写成 return super.create(key);
    //看得出来这里的create方法很关键,AbstractClassGenerator.create()
    Object result = super.create(key);
    return result;
}

AbstractClassGenerator类中

private static volatile Map CACHE = new WeakHashMap();
protected Object create(Object key) {
        try {
            //获取类加载器AppClassLoader
            ClassLoader loader = getClassLoader();
            //这里和jdk动态代理类似,也用到了缓存,classloader为key
            Map cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            //data==null
            if (data == null) {
                synchronized (AbstractClassGenerator.class{
                    cache = CACHE;
                    data = cache.get(loader);
                    //data==null
                    if (data == null) {
                        //没有数据则构建新缓存
                        Map newCache = new                         
                             WeakHashMap(cache);
                        //关键点:这里生成了class并存入缓存
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            //从缓存中取数据
            Object obj = data.get(this, getUseCache());
            //根据返回类型不同,然后进行对应的实例化
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            return nextInstance(obj);
        } catch (RuntimeException e|Error e|Exception e) {
            throw new CodeGenerationException(e);
        }
    }
  public ClassLoaderData(ClassLoader classLoader) {
           //类加载器是否存在
            if (classLoader == null) {
                throw new IllegalArgumentException("classLoader == null is not yet supported");
            }
            // 弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,
            //WeakReference无论内存是否充足,都会回收被弱引用关联的对象
            this.classLoader = new WeakReference(classLoader);
            Function load =
                    new Function() {
                        public Object apply(AbstractClassGenerator gen) {
                            //生成Class对象
                            Class klass = gen.generate(ClassLoaderData.this);
                            return gen.wrapCachedClass(klass);
                        }
                    };
            generatedClasses = new LoadingCache(GET_KEY, 
                                                                                        load);
        }
//生成Class对象
protected Class generate(ClassLoaderData data) {
        Class gen;
        Object save = CURRENT.get();
        CURRENT.set(this);
        try {
            ClassLoader classLoader = data.getClassLoader();
            if (classLoader == null) {
             throw new IllegalStateException("ClassLoader is null while trying to define class " + getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +  "Please file an issue at cglib's issue tracker.");
            }
            synchronized (classLoader) {
              //生成代理类的名字
              //com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1
              //三部分组成:被代理对象的类全名+FastClassBYCGLIB+hashcode
              String name = generateClassName(data.getUniqueNamePredicate());              
              data.reserveName(name);
              this.setClassName(name);
            }
            if (attemptLoad) {
                try {
                    gen = classLoader.loadClass(getClassName());
                    return gen;
                } catch (ClassNotFoundException e) {
                    // ignore
                }
            }
            //根据策略生成不同的字节码
            byte[] b = strategy.generate(this);
            String className = ClassNameReader.getClassName(new ClassReader(b));
            ProtectionDomain protectionDomain = getProtectionDomain();
            //利用反射生成class对象
            synchronized (classLoader) { // just in case
                if (protectionDomain == null) {
                    gen = ReflectUtils.defineClass(className, b, classLoader);
                } else {
                    gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                }
            }
            return gen;
        } catch (RuntimeException e|Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        } finally {
            CURRENT.set(save);
        }
    }
//生成字节码
public byte[] generate(ClassGenerator cg) throws Exception {
        DebuggingClassWriter cw = getClassVisitor();
        transform(cg).generateClass(cw);
        return transform(cw.toByteArray());
}
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
    return cg;
}
public interface ClassGenerator {
    void generateClass(ClassVisitor v) throws Exception;
}

ClassGenerator实现类AbstractClassGeneratorAbstractClassGenerator的子类主要有

Enhancer
FastClass.Generator
KeyFactory.Generator

把生成的class文件保存,会生成以上三个class文件

public class Client {
    public static void main(String[] args) {
        //把生成的class文件保存,会生成以上三个class文件
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
        long s=System.currentTimeMillis();
        CattlePersonMethodInterceptor cattlePerson = new CattlePersonMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TicketConcrete.class);
        enhancer.setCallback(cattlePerson);
        TicketConcrete person = (TicketConcrete) enhancer.create();
        person.buy();
        System.out.println(System.currentTimeMillis()-s);
    }
}

最终生成相关的三个Class

com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f
com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1
com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f$$FastClassByCGLIB$$ba31b84d

重点关注TicketConcrete$$EnhancerByCGLIB$$e39d634f文件中主要内容:

package com.tian.swagger.proxy.cglib;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//继承了TicketConcrete,证明了CGlib是针对被代理类进行生成一个子类来实现代理的
//和jdk动态代理不一样,动态代理是集成Proxy类实现了我们的接口,
//而cglib是集成了我们的类重写了父类中的方法
public class TicketConcrete$$EnhancerByCGLIB$$e39d634f 
       extends TicketConcrete
       implements Factory 
{
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$buy$0$Method;
    private static final MethodProxy CGLIB$buy$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        //var0为代理对象com.tian.swagger.proxy.cglib.TicketConcrete
        Class var0 = Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals""(Ljava/lang/Object;)Z""toString""()Ljava/lang/String;""hashCode""()I""clone""()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z""equals""CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;""toString""CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I""hashCode""CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;""clone""CGLIB$clone$4");
        //被代理的方法被代理方法buy()
        CGLIB$buy$0$Method = ReflectUtils.findMethods(new String[]{"buy""()V"}, (var1 = Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete")).getDeclaredMethods())[0];
        //代理方法
        CGLIB$buy$0$Proxy = MethodProxy.create(var1, var0, "()V""buy""CGLIB$buy$0");
    }
    //这个方法在fastClass中调用,此调用是执行真正的逻辑
    final void CGLIB$buy$0() {
        super.buy();
    }
    //方法重写
    public final void buy() {
        //判断目标类是否有设置回调:enhancer.setCallback(this);
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        //设置了方法的回调则调用拦截器方法intercept
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buy$0$Method, CGLIB$emptyArgs, CGLIB$buy$0$Proxy);
        } else {
            super.buy();
        }
    }
    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        //省略相关的代码
    }
    //省略调equals hashCode clone等方法  
}
CGlib总结
  • 首先生成代理对象。【创建增强类enhancer,设置代理类的父类,设置回调拦截方法,返回创建的代理对象】
  • 调用代理类中的方法。【这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用intercept方法】
  • 调用intercept,方法中会对调用代理方法中的invokeSuper方法。而在invokeSuper中维护了一个FastClassInfo类,其包含四个属性字段,分别为FastClass f1(目标类);FastClass f2 (代理类); int i1(目标类要执行方法的下标); int i2(代理类要执行方法的下标); invokeSuper中会调用的为代理类中的对应方法(代理类继承父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法)
  • 调用代理类中的代理方法,代理方法中通过super.method来真正地调用要执行被代理类的方法。

JDK VS CGlib

JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;JDK动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。

动态代理典型场景的应用

Mybatis中的使用场景

Mybatis应该算持久化框架中使用率最多之一,细心的小伙伴有没有发现,比如我们在使用UserInfoMapper.java是接口,接口不是能实例化的,那为什么我们还能注入呢?

    @Resource
    private UserInfoMapper userInfoMapper;

为什么还能userMapper.insert()这么使用呢?先看看Mybatis没有集成Spring是如何实例化的。

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
bean>

上面的MapperFactoryBean就可以实例化出mapperInterface类型的Bean,因为MapperFactoryBean实现了FactoryBean接口的getObject方法,可以实例化出我们想要的Bean,实际上是通过Jdk动态代理得到的Bean。

public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

调用方式:

SqlSession session = sqlSessionFactory.openSession();  
try {  
  User user= (User) session.selectOne("org.mybatis.example.UserMapper.selectBlog"1);  
finally {  
  session.close();  
}  

上面的UserMapper接口是一个个的配置来实例化的,每次需要openSession,close Session,重复工作太多,这样肯定比较麻烦,下面看看项目中常用的Mybatis与Spring集成的配置方式。

"sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 "mapperLocations" value="classpath*:com/**/mapper/**/*Mapper*.xml" />
     "dataSource" ref="dataSource" />

这个SqlSessionFactoryBean会做哪些事呢,主要是把Mapper.xml文件与Mapper.java加载进来,根据namespace加载对应的接口类到MapperRegistry,把方法名与Mapper.xml里的Select id对应起来等等。MapperRegistry相当于是一个缓存,后面创建代理对象是会用到。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 <property name="annotationClass" value="javax.annotation.Autowire">property
     <property name="basePackage" value="com.***.mapper" />
     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
bean>

这里的值设置,只要是注解就行,不一定是Autowird,它代表只对有注解的接口类创建代理对象,否则,对basePackage下所有接口创建代理对象。

MapperScannerConfigurer可以对basePackage下所有Mapper接口创建代理对象,而不是像上面一个个配置。因为他实现了接口BeanDefinitionRegistryPostProcessor,它就是用来自定义Bean的,看看源码它是如何实现的。

上面设置mapperInterface,BeanClass其实就是上面讲的Mybatis没有集成Spring的配置方式。

我们知道了代理对象是通过MapperFactoryBean创建的。具体看看是如何创建的。

根据接口类创建代理对象:

/**
   * {@inheritDoc}
   */

  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

这里就用到了之前说的MapperRegistry,只是用来校验该接口是否存在。这里又用到了MapperProxy,是一个代理类,实现了InvocationHandler接口。

 public static  newMapperProxy(Class mapperInterface, SqlSession sqlSession) {
    ClassLoader classLoader = mapperInterface.getClassLoader();
    Class[] interfaces = new Class[]{mapperInterface};
    MapperProxy proxy = new MapperProxy(sqlSession);
    return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
  }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getDeclaringClass() == Object.class{
      return method.invoke(this, args);
    }
    final Class declaringInterface = findDeclaringInterface(proxy, method);
    final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
    final Object result = mapperMethod.execute(args);
    if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
      throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

当我们调用Mpper接口方法时,会被MapperProxy代理类拦截调用,主要调用实现就是mapperMethod.execute(args),看看底层实现。

public Object execute(Object[] args) {
    Object result = null;
    if (SqlCommandType.INSERT == type) {
      Object param = getParam(args);
      result = sqlSession.insert(commandName, param);
    } else if (SqlCommandType.UPDATE == type) {
      Object param = getParam(args);
      result = sqlSession.update(commandName, param);
    } else if (SqlCommandType.DELETE == type) {
      Object param = getParam(args);
      result = sqlSession.delete(commandName, param);
    } else if (SqlCommandType.SELECT == type) {
      if (returnsVoid && resultHandlerIndex != null) {
        executeWithResultHandler(args);
      } else if (returnsMany) {
        result = executeForMany(args);
      } else if (returnsMap) {
        result = executeForMap(args);
      } else {
        Object param = getParam(args);
        result = sqlSession.selectOne(commandName, param); //跟上面没有集成Spring调用方式一样的。
      }
    } else {
      throw new BindingException("Unknown execution method for: " + commandName);
    }
    return result;
  }

以上就是代理模式、动态代理(JDK、CGLib)、MyBatis中的动态代理的使用。

好了,就这么多了。



推荐阅读:

MySQL一共可以创建多少张表

出乎意料,刷新认知:读者朋友们藏龙卧虎



欢迎关注微信公众号:互联网全栈架构,收取更多有价值的信息。


浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报