AOP底层实现:JDK动态代理详解

2020-06-10  本文已影响0人  NisyCoding

一.创建代理的三要素:

打个比方,我去卖房子,然后我要带客户去看房子;可是有一天,我不想每天都带那么多的客户去看房子,我该怎么办呢?找中介.让中介代替我,领着客户去看房子.那么中介需要伪装成是房东(此时就要重写原始对象中的方法).

二.JDK动态代理:

Proxy.newProxyInstance(classloader,interfaces,invocationHandler)

2.1 invocationHandler介绍:

2.1.1 MethodInterceptor拦截器回顾:

我们在学习spring动态代理之MethodInterceptor拦截器的时候,需要实现MethodInterceptor接口,此时重写了接口中的invoke方法,invoke参数中有一个MethodInvocation,它此时代表额外功能增加的那个原始方法,代码如下:

public class Arroud implements MethodInterceptor {

    /**
     * spring动态代理之MethodInterceptor拦截器
     * @param methodInvocation :额外功能增加的那个原始方法,如:register(),login()
     * @return: 因为每个方法的返回值都不一样,所以需要object类来接受
     * @throws Throwable
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("额外功能----");
        //表示:该原始方法执行了,如register,login方法
        Object proceed = methodInvocation.proceed();
        System.out.println("方法的返回值:object="+proceed);
        return proceed;
    }
}

我们会发现,这个methodInvocation是被spring进行封装了的,但是我们的JDK的代理还是原生的.

2.1.2 InvocationHandler介绍和演示:

invocationHandler是一个接口.我们需要重写invoke方法,从而来增加额外的功能

/**
         * 2.额外功能
         *
         *
         */
        final InvocationHandler invocationHandler=new InvocationHandler() {
            /**
             * 为什么是method.invoke? 因为动态代理. 省去了service.register,service.login()
             *
             * @param proxy 代理对象
             * @param method 原始对象的原始方法
             * @param args  原始方法的参数
             * @return   原始方法的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //2.1 新增额外功能
                System.out.println("jdk proxy...");
                //2.2 执行原始对象的原始方法
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };

语法糖: jdk1.8之后,我们可以省略掉上面的final修饰符

三. interfaces介绍:

代理创建3要素的第三条,代理对象和原始对象需要实现相同的接口,这里的interfaces就是实现的接口.
此时,我们怎么获取到这个接口呢?通过getClass().getInterfaces()可以获取到类的所有接口定义.

四. classloader介绍:

4.1 类加载器怎么获取呢?

(1).我们知道,一个user.java文件,通过编译成user.class的字节码文件,这里每个类的class文件,自动会分配与之对应的classloader类加载器;
(2).类加载器会把user.class字节码文件放入到JVM虚拟机中;
(3).jvm虚拟机如何去创建一个user对象呢?此时肯定需要拿到user的class对象才能去创建user对象.此时也是通过类加载器去生成Class对象.

此时,问题来了,我现在是动态代理呀,我拿不到具体的.java文件,我也没有具体的.class文件呀,顺其自然的,我也就没有对应的类加载器呀,我该怎么办?
答: 去借一个

五.最终的JDK动态代理代码如下:

package com.baizhiedu.jdl;


import com.baizhiedu.proxy.User;
import com.baizhiedu.proxy.UserService;
import com.baizhiedu.proxy.UserServiceImpl;

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

/**
 * JDK动态代理
 */
public class TestJdkProxy {

    public static void main(String[] args) {

        //1.创建原始对象UserService
        final UserService userService = new UserServiceImpl();


        /**
         * 2.额外功能
         *
         *
         */
        final InvocationHandler invocationHandler=new InvocationHandler() {
            /**
             * 为什么是method.invoke? 因为动态代理. 省去了service.register,service.login()
             *
             * @param proxy 代理对象
             * @param method 原始对象的原始方法
             * @param args  原始方法的参数
             * @return   原始方法的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //2.1 新增额外功能
                System.out.println("jdk proxy...");
                //2.2 执行原始对象的原始方法
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };

        /**
         * 3.代理对象和原始对象实现相同的接口  interfaces:原始对象所实现的接口
         * 先借用userService的classLoader TestJdkProxy也可以
         */

        UserService service = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        service.login(new User());
        service.register("Nisy",20);
    }
}

5.1 运行结果:

jdk proxy...
登录功能的核心代码...
jdk proxy...
注册功能的核心代码...
上一篇下一篇

猜你喜欢

热点阅读