大话AOP之代理模式

目录

1.前言

2.静态代理

2.1 定义一个接口类

2.2 定义一个接口实现类

2.3 定义一个代理类

2.4 测试

3.jdk动态代理

4.cglib动态代理

5.关于效率

参考文献


1.前言

AOP面向切面的编程,是各种框架的核心特性之一。不管是Spring还是Jfinal,对于AOP都有比较好的支持。

对于AOP的理解,我的简单理解是,对于一类方法的执行会自动的操作额外的动作。自动执行的额外动作就是AOP的目标。举两个比较经典的应用场景:

(1)数据库的事物管理,委托类仅仅处理数据库的操作,代理类对操作做事物保证,出现错误了,回退保证数据的准确性。

(2)日志操作,委托类只做业务的处理,代理类可以委托类相关方法的调用做无侵入的日志打印功能。

不管Spring系列的框架也好,Jfinal框架也好,底层对于AOP实现都是用的代理模式,区别在于组织方式不同,对外提供的规则有所差别,另外JFinal主要使用cglib代理,而Spring同时支持jdk动态代理和cglib动态代理。

代理分为动态代理和静态代理,它们的主要区别在于:

静态代理:相关类、代理类等等都是已经编译好的。在代理类、委托类运行之前,代理类已经编译好了。

动态代理:在程序运行的过程中,由反射机制动态的创建代理类和委托类。

下面记录下不同代理的实现。

2.静态代理

2.1 定义一个接口类

以前写代码的时候,喜欢直接写类文件,不写interface,总觉得多一个文件麻烦。但其实,接口和实现类分开来写的方式代码的兼容性和可扩展性要强很多,其实仔细想想现在的微服务架构方式,不就是通过接口进行服务之间的调用和耦合,服务的升级,只需要修改和调整实现类,接口类不变,这样也不会影响调用者(消费方)的版本变更。

所以,对于服务的开发,最好的方式就是先声明接口,然后再去做实现类的开发,从编码设计角度来说,接口类的开发也是构建开发思路的设计过程。

/**
 * 静态代理 接口
 */
public interface Count
{
    public void query();
}

2.2 定义一个接口实现类

我们可以定义多个接口实现类,这就是接口的优势。

public class ACount implements Count
{

    @Override public void query()
    {
        System.out.println("ACount查询中---");
    }
}

2.3 定义一个代理类

对于静态代理而言,我们定义的代理类一定是有针对性的,即这个代理类是针对哪个实现类的代理类,这就是静态代理的特性决定的。例如:我们在调用ACount的query方式时,希望在方法执行前打印一行提示,使用后打印一行提示。按照我们最直观的想法,就是直接修改query的实现算了,但是这样就完全把ACount原本的意思修改了,这时候,我们就需要代理模式来生成代理类,无侵入的实现这样的需求。

public class AServiceProxy implements Count
{
    private ACount aCount;
    public AServiceProxy(ACount aCount)
    {
        this.aCount = aCount;
    }

    @Override public void query()
    {
        System.out.println("调用之前---");
        aCount.query();
        System.out.println("调用之后---");

    }
}

2.4 测试

    public static void testStaticProxy()
    {
        ACount        aCount = new ACount();
        AServiceProxy aProxy = new AServiceProxy(aCount);
        aProxy.query();
    }

测试结果显示:

ACount是委托类,aProxy是代理类,代理类一定会完成委托类的方法,但是在完成的同时,可以根据需要进行其他无侵入的操作。

以上就是静态代理,相比动态代理,最大的区别就是代理类和委托类在运行的过程中,都已经是编译过好的,并非动态生成的。

3.jdk动态代理

首先jdk动态代理一定是基于接口类的,即只能对接口生成代理类。这是和cglib最大的区别。同样的动态代理要先创建一个接口类,然后是接口的实现类和代理类,接口类和实现类,同静态代理一样,我们不在赘述,下面看一下代理类是怎么编写的。


/**
 * 实现动态代理的CallBack方法类,可以简单理解为切面上同一操作的类
 * InvocationHandler是一种实现方法,他有回调方法,可以用在
 * java动态代理,也可以用于cglib动态代理
 */
public class AopInvocationHandler implements InvocationHandler
{
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        System.out.println("切面逻辑,在实际方法调用之前调用--------");
        Object result = method.invoke(target,args);
        System.out.println("切面逻辑,在实际调用方法之后调用--------");
        return result;
    }
}

这里定义个了一个基于Invocationhandler的类,重载其invoke方法,然后测试的时候,代码如下;

    public static void testJdkProxy()
    {
        Service              aService = new AService();
        AopInvocationHandler handler  = new AopInvocationHandler(aService);
        Service aServiceProxy = (Service) Proxy.newProxyInstance(
                aService.getClass().getClassLoader(),
                aService.getClass().getInterfaces(),
                handler);

        aServiceProxy.add();
    }

这里重点看Proxy.newProxyInstance方法,传入的handler就是我们自己定义的回调方法类。测试效果如下:

4.cglib动态代理

我们先定义一个实现MethodInterceptor接口的类,如下:

public class CglibProxy implements MethodInterceptor
{
    @Override
    public Object intercept(Object o, Method method,
            Object[] objects, MethodProxy methodProxy) throws Throwable
    {
        // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
        System.out.println("before-------------");
        // 执行目标类add方法
        methodProxy.invokeSuper(o, objects);
        // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
        System.out.println("after--------------");
        return null;
    }
}

使用时候的代码如下

    public static void testCglibProxy()
    {
        CglibProxy cglibProxy = new CglibProxy();
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CService.class);
        enhancer.setCallback(cglibProxy);
        
        CService cService = (CService)enhancer.create();
        cService.add();
    }

通过Enhancer生成创建代理类,然后调用代理类的相关方法时,会自动在方法调用前和调用后打印相关内容。这里需要注意的是,它默认是拦截增强类的所有方法。

PS:Jfinal框架管理拦截器、事物的实现根本原理就是cglib的动态代理,区别在于对代理类的intercept方法中做了更多的调整,例如获取方法的注解,根据注解不同进行不同的操作等等。

5.关于效率

(1) 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前,是比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

(2) 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,在对JDK动态代理与CGlib动态代理的代码实验中看,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。

参考文献

【1】AOP的底层实现-CGLIB动态代理和JDK动态代理(推荐)

【2】动态代理:JDK动态代理和CGLIB代理的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值