AOP原理之动态代理

2019-09-18 14:14:27 浏览数 (1)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/qq_37933685/article/details/81635537

个人博客:https://suveng.github.io/blog/​​​​​​​

AOP原理之动态代理

实现AOP的底层是反射机制,其中有两种实现方式:

其一是JDK的动态代理;

其二是基于CGLib的动态代理;

这里用简单的demo来演示两种方式的过程:

基于JDK的动态代理

JDK的动态代理是必须要一个接口来实现,那么现在来创建这个接口:

UserDao.java

代码语言:javascript复制
**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/8/13 10:52
 */
public interface UserDao {
    public void say();
    public void ask();
}

接着我们来创建一个接口的实现类:

UserDaoImpl.java

代码语言:javascript复制
/**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/8/13 10:52
 */
public class UserDaoImpl implements UserDao {
    public void say() {
        System.out.println("suveng create say method");
    }

    public void ask() {
        System.out.println("suven create ask method");
    }
}

现在接口和实现类都准备好了,我们可以开始实现一个代理类了。

JDKProxy.java

代码语言:javascript复制
/**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/8/13 10:53
 */
// 思路:先把需要代理增强的东西传进来,接着增强之后再把新的返回出去
public class JDKProxy {
    //新建方法返回增强类
    public static UserDao getUserDaoProxy(final UserDao userDao){
        //1.获取类加载器
        ClassLoader userDaoClassLoader=userDao.getClass().getClassLoader();
        //2. 获取接口
        Class<?>[] interfaces=userDao.getClass().getInterfaces();
        //3.创建代理类
        UserDao userDaoProxy= (UserDao) Proxy.newProxyInstance(userDaoClassLoader, interfaces, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //开始写增强内容
                if (method.getName().equals("say")){
                    System.out.println("这是增强内容");
                }
                return method.invoke(userDao,args);
            }
        });
        return userDaoProxy;
    }
}

好了,到这里我们的代理已经创建好了,主要是使用了Proxy.newProxyInstance()这个方法创建一个代理类,然后把这个代理类返回。接下来我们就可以用JDKProxy来创建一个被代理后的UserDao这个增强类。

为什么需要接口? 因为Proxy.newProxyInstance()这个方法的参数需要传入一个接口的参数,所以用JDK创建动态代理需要一个接口类。

接下来我们来测试一下:

代码语言:javascript复制
@org.junit.Test
public void testJDKProxy(){
    UserDao userDao= JDKProxy.getUserDaoProxy(new UserDaoImpl());
    userDao.say();
}

运行结果如下图所示:

基于CGLib的动态代理:

CGLIB(Code Generation Library)是一个开源项目! 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。 摘自百度百科

JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?

现在我们可以使用CGLIB包

首先我们需要准备一个类,随便一个类。那我们复用UserDaoImpl的类,把它改造一下

User.java

代码语言:javascript复制
/**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/8/13 10:52
 */
public class User  {
    public void say() {
        System.out.println("suveng create say method");
    }

    public void ask() {
        System.out.println("suven create ask method");
    }
}

接下我们创建一个代理类;

CGLibProxy.java

代码语言:javascript复制
/**
 * author: Veng Su
 * email: suveng@163.com
 * date: 2018/8/13 14:33
 */
public class CGLibProxy {
    public static User getProxy(){
        //1.创建CGLib核心类
        Enhancer enhancer=new Enhancer();
        //2. 制定需要代理的类,也就是父类
        enhancer.setSuperclass(User.class);
        //3. 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getName().equals("say")){
                    System.out.println("我是增强内容");
                }
                return methodProxy.invokeSuper(o,objects);
            }
        });
        //4. 生成代理对象
        User userDaoProxy= (User) enhancer.create();
        return userDaoProxy;
    }

}

这就实现了一个CGLib代理类。

注意:methodProxy.invokeSuper(o,objects);这里要调用的父类的方法,如果调用methodProxy.invoke(o,objects);会无线调用导致堆栈溢出。

测试:

代码语言:javascript复制
@Test
public void testCGLibProxy(){
    User user= CGLibProxy.getProxy();
    user.say();
}

运行结构如下图所示:

码云源码地址

https://gitee.com/suwenguang/SpringFrameworkDemo

总结

其实学习动态代理还要很长的路要走,这里只是简单的实现。目前写业务的我理解到这里就OK,够用了,如果还有需要深入理解动态的话,建议去把JDK的反射包的源码和CGLib的源码了解一遍。加油!

0 人点赞