java动态代理的两种实现方式详解

最近复习了一下动态代理的两种实现方式,我觉得学习动态代理的方式就是从springAOP入手,因为springAop中的代理就很好的诠释了两种动态代理的实现方式,springAOP默认的策略是如果目标类是接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理。

一.什么是代理?
先来讲讲我所理解的什么是代理吧,生活中有很多代理的角色,比如银行代理,驾校代理,信用卡代理等等,其实所谓的代理就是帮你去干你不想干的事情的人,那么在程序的世界里,代理其实就是去帮你实现你不想到处都写的业务逻辑,有了代理,你可以不用显式的调用就可以实现业务增强,aop的切面增强技术就是很好的动态代理的实现,它可以很好的解耦我们的代码,比如操作拦截,日志打印等等,我们不必在每个操作都去写上一段相同的代码,而是通过代理技术来拦截我们的操作,动态的进行增强,岂不是一劳永逸,而且便于维护?废话不多说,直接上两个自己写的通过jdk代理以及cglib实现的代理的demo,便于大家理解。

二.jdk的动态代理
1.jdk的动态代理核心就是InvocationHandler接口和Proxy类,要想通过jdk的动态代理去实现自己的业务,就必须要实现InvocationHandler接口,并实现该接口的invoke方法,代理增强的业务代码就写在invoke方法体中,然后通过Proxy类的newInstance方法创建代理对象,通过代理对象执行目标对象的方法时,实际上执行的是增强之后的invoke方法,直接上代码:
需求一:有一个service接口,接口中有一个doService方法,现在我想通过jdk的动态代理去对doService方法进行增强,该怎么做?如下:
1.新建一个Service接口,包含一个doService方法

package com.example.dynamicProxy;

/**
 * @author herong22384
 * @date 2019/7/30 15:32
 */
public interface Waiter {
    String doService();
}

给该接口写一个简单的实现类

package com.example.dynamicProxy.impl;

import com.example.dynamicProxy.Waiter;

/**
 * @author herong22384
 * @date 2019/7/30 15:32
 */
public class ManWaiter implements Waiter {

    @Override
    public String doService() {
        System.out.println("jdk动态代理服务处理中");
        return "hello";
    }
}

2.新建一个动态代理工厂类,实现InvocationHandler接口

package com.example.dynamicProxy;

import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author herong22384
 * @date 2019/7/30 15:05
 *
 * 1、创建代理工厂(通过jdk实现动态代理)
 * 2、给工厂设置目标对象、前置增强、后置增强
 * 3、调用doProxy()得到代理对象
 * 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强
 * 5、其实在Spring中的AOP的动态代理实现的一个织入器也是叫做ProxyFactory
 */
public class JdkProxyFactory implements InvocationHandler{

    private Object targetObject;
    private BeforeAdvice beforeAdvice;
    private AfterAdvice afterAdvice;

    public JdkProxyFactory(){
    }

    public JdkProxyFactory(Object targetObject, BeforeAdvice beforeAdvice, AfterAdvice afterAdvice){
        this.targetObject = targetObject;
        this.beforeAdvice = beforeAdvice;
        this.afterAdvice = afterAdvice;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置增强
        if (beforeAdvice != null){
            beforeAdvice.before();
        }

        //通过反射执行主业务逻辑
        Object result = method.invoke(targetObject,args);

        //后置增强
        if (afterAdvice != null){
            afterAdvice.after();
        }

        return result;
    }

    public Object doProxy(){
        //获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        //获取当前类所实现的所有接口类型
        Class[] interfaces = targetObject.getClass().getInterfaces();
        //获取代理对象
        Object object = Proxy.newProxyInstance(classLoader,interfaces,this);
        return object;
    }

    public Object getTargetObject() {
        return targetObject;
    }

    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }

    public BeforeAdvice getBeforeAdvice() {
        return beforeAdvice;
    }

    public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
        this.beforeAdvice = beforeAdvice;
    }

    public AfterAdvice getAfterAdvice() {
        return afterAdvice;
    }

    public void setAfterAdvice(AfterAdvice afterAdvice) {
        this.afterAdvice = afterAdvice;
    }
}

3.模拟aop的实现,创建两个增强接口(也可通过自定义注解实现)

前置增强接口

package com.example.dynamicProxy.advice;

/**
 * @author herong22384
 * @date 2019/7/30 15:07
 */
/**
 * 前置增强
 * */
public interface BeforeAdvice {

    void before();

}

后置增强接口

package com.example.dynamicProxy.advice;

/**
 * @author herong22384
 * @date 2019/7/30 15:08
 */
/**
 * 后置增强
 * */
public interface AfterAdvice {

    void after();

}

4.创建一个main函数,测试一下这个通过jdk实现的动态代理工厂类是否能达到预期增强doService方法的目的

package com.example.dynamicProxy;

import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import com.example.dynamicProxy.impl.CglibServiceImpl;
import com.example.dynamicProxy.impl.ManWaiter;

/**
 * @author herong22384
 * @date 2019/7/30 15:23
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        BeforeAdvice beforeAdvice = new BeforeAdvice() {
            @Override
            public void before() {
                System.out.println("前置增强啦");
            }
        };

        AfterAdvice afterAdvice = new AfterAdvice() {
            @Override
            public void after() {
                System.out.println("后置增强啦");
            }
        };
        System.out.println("开始jdk动态代理模式");
        JdkProxyFactory proxy = new JdkProxyFactory(new ManWaiter(),beforeAdvice,afterAdvice);
        Waiter waiter = (Waiter) proxy.doProxy();
        //调用动态代理出来的对象的方法,实际上是调用的动态代理器InvocationHandler的invoke方法
        waiter.doService();
    }
}

执行结果如下:
在这里插入图片描述
可以看到,通过jdk的动态代理,我们已经基本实现了对Waiter接口的doService方法进行增强,通过这个一个简单的实现,我们可以了解到,在通过Proxy类的NewProxyInstance方法创建代理对象时,我们需要传入三个参数,其中ClassLoader用来加载类对象,Class[] 类型的interface用来获取当前类所实现的所有接口类型,最后还要传入一个InvocationHnadler接口的实现类对象,通过跟踪newProxyInstance方法的源码发现,jdk的动态代理在创建代理对象时,是通过实现代理对象所实现的接口,并实现接口中的所有方法,最后对目标对象的方法通过invoke方法进行增强(invoke方法中通过反射来执行目标对象的方法,并在该方法的前后进行相应的增强),那么代理对象在执行目标对象的方法时,实际上执行的就是增强之后的invoke方法,从而达到对目标对象进行增强的目的。

三.cglib实现的动态代理
1.cglib实现的动态代理的核心就是MethodInterceptor接口和Enhancer类(这两个相当于jdk动态代理的核心invocationHandler接口和Proxy类)MethodInterceptor接口的intercept方法用来动态增强目标对象的方法,Enhancer类的create方法用来生成代理对象。通过cglib动态代理实现方法的增强的实现如下:
2.直接创建一个类,他有一个doDynamicService方法,我们直接对这个方法进行增强

package com.example.dynamicProxy.impl;

/**
 * @author herong22384
 * @date 2019/7/31 15:30
 */
public class CglibServiceImpl {

    public void doDynamicService(){
        System.out.println("cglib动态代理服务处理中");
    }
}

3.创建一个cglib的动态代理工厂类,实现MethodInterceptor接口

package com.example.dynamicProxy;

import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author herong22384
 * @date 2019/7/31 13:55
 *
 * 1、创建代理工厂(通过cglib实现动态代理)
 * 2、给工厂设置目标对象、前置增强、后置增强
 * 3、调用getInstance()得到代理对象
 * 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强(实际执行的是经过增强的intercept()方法)
 */
public class CglibProxyFactory implements MethodInterceptor {

    private Object targetObject;
    private BeforeAdvice beforeAdvice;
    private AfterAdvice afterAdvice;

    public CglibProxyFactory(){}

    public CglibProxyFactory(Object targetObject,BeforeAdvice beforeAdvice,AfterAdvice afterAdvice){
        this.targetObject = targetObject;
        this.beforeAdvice = beforeAdvice;
        this.afterAdvice = afterAdvice;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //前置增强
        if (beforeAdvice != null){
            beforeAdvice.before();
        }
        //执行原本的主业务逻辑
        Object object = methodProxy.invokeSuper(o,objects);
        //后置增强
        if (afterAdvice != null){
            afterAdvice.after();
        }
        return object;
    }

    public Object getInstance(){
        //创建生成代理对象的工具类,相当于jdk的proxy类
        Enhancer enhancer = new Enhancer();
        //设置需要被代理的对象类
        enhancer.setSuperclass(this.targetObject.getClass());
        //设置增强的方法
        enhancer.setCallback(this);
        //创建代理对象
        Object object = enhancer.create();
        return object;
    }

    public Object getTargetObject() {
        return targetObject;
    }

    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }

    public BeforeAdvice getBeforeAdvice() {
        return beforeAdvice;
    }

    public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
        this.beforeAdvice = beforeAdvice;
    }

    public AfterAdvice getAfterAdvice() {
        return afterAdvice;
    }

    public void setAfterAdvice(AfterAdvice afterAdvice) {
        this.afterAdvice = afterAdvice;
    }
}

4.通过一个main函数来测试cglib动态代理的增强

package com.example.dynamicProxy;

import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import com.example.dynamicProxy.impl.CglibServiceImpl;
import com.example.dynamicProxy.impl.ManWaiter;

/**
 * @author herong22384
 * @date 2019/7/30 15:23
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        BeforeAdvice beforeAdvice = new BeforeAdvice() {
            @Override
            public void before() {
                System.out.println("前置增强啦");
            }
        };

        AfterAdvice afterAdvice = new AfterAdvice() {
            @Override
            public void after() {
                System.out.println("后置增强啦");
            }
        };
        System.out.println("开始cglib动态代理模式");
        CglibProxyFactory factory = new CglibProxyFactory(new CglibServiceImpl(),beforeAdvice,afterAdvice);
        CglibServiceImpl cglibService = (CglibServiceImpl)factory.getInstance();
        cglibService.doDynamicService();
    }
}

5.执行一下,看下结果
在这里插入图片描述
6.可以看出,我们通过cglib的动态代理也基本实现了针对某个目标对象的方法的增强,那么cglib的动态代理和我们前面jdk的动态代理到底有什么区别呢,大体上看起来其实这两种方式实现的动态代理差不多,都是通过实现一个接口来实现目标对象方法的增强,然后通过一个代理类的方法创建出代理对象,然后执行代理对象的方法实际上执行的是增强之后的方法,是啊,套路都是这么一个套路,但其实他们还是有本质上的区别的,要不然就没有存在两种方式实现的动态代理的必要了。接下来我们就来探讨一下这两种动态代理的区别,以及在什么场景下用jdk的动态代理好点,什么场景下用cglib实现的额动态代理好点,我想这才是大家关注的重点:

JDK动态代理
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。

cglib动态代理
1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体

spring两种代理方式

  1. 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
    优点:因为有接口,所以使系统更加松耦合
    缺点:为每一个目标类创建接口

  2. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
    优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值