上文咱们学习了SpringAOP Cglib动静代理的实现,本文次要是SpringAOP JDK动静代理的案例和实现局部。@pdai
  • Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现

    • 引入

      • 什么是JDK代理?
    • JDK代理的案例

      • 不须要maven依赖
      • 定义实体
      • 被代理的类和接口
      • JDK代理类
      • 应用代理
      • 简略测试
    • JDK代理的流程

      • ProxyGenerator生成代码
      • 从生成的Proxy代码看执行流程
    • SpringAOP中JDK代理的实现

      • SpringAOP Jdk代理的创立
      • SpringAOP Jdk代理的执行
    • 示例源码
    • 更多文章

引入

上文咱们学习了SpringAOP Cglib动静代理的实现,本文次要是SpringAOP JDK动静代理的案例和实现局部。

什么是JDK代理?

JDK动静代理是有JDK提供的工具类Proxy实现的,动静代理类是在运行时生成指定接口的代理类,每个代理实例(实现须要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke办法上。

JDK代理的案例

这里咱们写一个应用jdk代理的简略例子。@pdai

不须要maven依赖

jdk代理不须要任何依赖。

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>tech-pdai-spring-demos</artifactId>        <groupId>tech.pdai</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>006-spring-framework-demo-aop-proxy-jdk</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <!--based on jdk proxy -->    <dependencies>    </dependencies></project>

定义实体

User

package tech.pdai.springframework.entity;/** * @author pdai */public class User {    /**     * user's name.     */    private String name;    /**     * user's age.     */    private int age;    /**     * init.     *     * @param name name     * @param age  age     */    public User(String name, int age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

被代理的类和接口

接口如下

package tech.pdai.springframework.service;import tech.pdai.springframework.entity.User;import java.util.List;/** * @author pdai */public interface IUserService {    /**     * find user list.     *     * @return user list     */    List<User> findUserList();    /**     * add user     */    void addUser();}

实现类如下:

package tech.pdai.springframework.service;import tech.pdai.springframework.entity.User;import java.util.Collections;import java.util.List;/** * @author pdai */public class UserServiceImpl implements IUserService {    /**     * find user list.     *     * @return user list     */    @Override    public List<User> findUserList() {        return Collections.singletonList(new User("pdai", 18));    }    /**     * add user     */    @Override    public void addUser() {        // do something    }}

JDK代理类

代理类如下:

package tech.pdai.springframework.proxy;import tech.pdai.springframework.service.IUserService;import tech.pdai.springframework.service.UserServiceImpl;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;/** * This class is for proxy demo. * * @author pdai */public class UserLogProxy {    /**     * proxy target     */    private IUserService target;    /**     * init.     *     * @param target target     */    public UserLogProxy(UserServiceImpl target) {        super();        this.target = target;    }    /**     * get proxy.     *     * @return proxy target     */    public IUserService getLoggingProxy() {        IUserService proxy;        ClassLoader loader = target.getClass().getClassLoader();        Class[] interfaces = new Class[]{IUserService.class};        InvocationHandler h = new InvocationHandler() {            /**             * proxy: 代理对象。 个别不应用该对象 method: 正在被调用的办法 args: 调用办法传入的参数             */            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                String methodName = method.getName();                // log - before method                System.out.println("[before] execute method: " + methodName);                // call method                Object result = null;                try {                    // 前置告诉                    result = method.invoke(target, args);                    // 返回告诉, 能够拜访到办法的返回值                } catch (NullPointerException e) {                    e.printStackTrace();                    // 异样告诉, 能够拜访到办法呈现的异样                }                // 后置告诉. 因为办法能够能会出异样, 所以拜访不到办法的返回值                // log - after method                System.out.println("[after] execute method: " + methodName + ", return value: " + result);                return result;            }        };        /**         * loader: 代理对象应用的类加载器.         * interfaces: 指定代理对象的类型. 即代理代理对象中能够有哪些办法.         * h: 当具体调用代理对象的办法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 办法         */        proxy = (IUserService) Proxy.newProxyInstance(loader, interfaces, h);        return proxy;    }}

应用代理

启动类中指定代理指标并执行。

package tech.pdai.springframework;import tech.pdai.springframework.proxy.UserLogProxy;import tech.pdai.springframework.service.IUserService;import tech.pdai.springframework.service.UserServiceImpl;/** * Jdk proxy demo. * * @author pdai */public class ProxyDemo {    /**     * main interface.     *     * @param args args     */    public static void main(String[] args) {        // proxy        IUserService userService = new UserLogProxy(new UserServiceImpl()).getLoggingProxy();        // call methods        userService.findUserList();        userService.addUser();    }}

简略测试

咱们启动上述类main 函数,执行的后果如下:

[before] execute method: findUserList[after] execute method: findUserList, return value: [User{name='pdai', age=18}][before] execute method: addUser[after] execute method: addUser, return value: null

JDK代理的流程

JDK代理主动生成的class是由sun.misc.ProxyGenerator来生成的。

ProxyGenerator生成代码

咱们看下sun.misc.ProxyGenerator生成代码的逻辑:

/**    * Generate a proxy class given a name and a list of proxy interfaces.    *    * @param name        the class name of the proxy class    * @param interfaces  proxy interfaces    * @param accessFlags access flags of the proxy class*/public static byte[] generateProxyClass(final String name,                                        Class<?>[] interfaces,                                        int accessFlags){    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);    final byte[] classFile = gen.generateClassFile();    ...}

generateClassFile办法如下:

/**    * Generate a class file for the proxy class.  This method drives the    * class file generation process.    */private byte[] generateClassFile() {    /* 第一步:将所有办法包装成ProxyMethod对象 */        // 将Object类中hashCode、equals、toString办法包装成ProxyMethod对象    addProxyMethod(hashCodeMethod, Object.class);    addProxyMethod(equalsMethod, Object.class);    addProxyMethod(toStringMethod, Object.class);    // 将代理类接口办法包装成ProxyMethod对象    for (Class<?> intf : interfaces) {        for (Method m : intf.getMethods()) {            addProxyMethod(m, intf);        }    }    // 校验返回类型    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {        checkReturnTypes(sigmethods);    }    /* 第二步:为代理类组装字段,构造函数,办法,static初始化块等 */    try {        // 增加构造函数,参数是InvocationHandler        methods.add(generateConstructor());        // 代理办法        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {            for (ProxyMethod pm : sigmethods) {                // 字段                fields.add(new FieldInfo(pm.methodFieldName,                    "Ljava/lang/reflect/Method;",                        ACC_PRIVATE | ACC_STATIC));                // 上述ProxyMethod中的办法                methods.add(pm.generateMethod());            }        }        // static初始化块        methods.add(generateStaticInitializer());    } catch (IOException e) {        throw new InternalError("unexpected I/O Exception", e);    }    if (methods.size() > 65535) {        throw new IllegalArgumentException("method limit exceeded");    }    if (fields.size() > 65535) {        throw new IllegalArgumentException("field limit exceeded");    }    /* 第三步:写入class文件 */    /*        * Make sure that constant pool indexes are reserved for the        * following items before starting to write the final class file.        */    cp.getClass(dotToSlash(className));    cp.getClass(superclassName);    for (Class<?> intf: interfaces) {        cp.getClass(dotToSlash(intf.getName()));    }    /*        * Disallow new constant pool additions beyond this point, since        * we are about to write the final constant pool table.        */    cp.setReadOnly();    ByteArrayOutputStream bout = new ByteArrayOutputStream();    DataOutputStream dout = new DataOutputStream(bout);    try {        /*            * Write all the items of the "ClassFile" structure.            * See JVMS section 4.1.            */                                    // u4 magic;        dout.writeInt(0xCAFEBABE);                                    // u2 minor_version;        dout.writeShort(CLASSFILE_MINOR_VERSION);                                    // u2 major_version;        dout.writeShort(CLASSFILE_MAJOR_VERSION);        cp.write(dout);             // (write constant pool)                                    // u2 access_flags;        dout.writeShort(accessFlags);                                    // u2 this_class;        dout.writeShort(cp.getClass(dotToSlash(className)));                                    // u2 super_class;        dout.writeShort(cp.getClass(superclassName));                                    // u2 interfaces_count;        dout.writeShort(interfaces.length);                                    // u2 interfaces[interfaces_count];        for (Class<?> intf : interfaces) {            dout.writeShort(cp.getClass(                dotToSlash(intf.getName())));        }                                    // u2 fields_count;        dout.writeShort(fields.size());                                    // field_info fields[fields_count];        for (FieldInfo f : fields) {            f.write(dout);        }                                    // u2 methods_count;        dout.writeShort(methods.size());                                    // method_info methods[methods_count];        for (MethodInfo m : methods) {            m.write(dout);        }                                        // u2 attributes_count;        dout.writeShort(0); // (no ClassFile attributes for proxy classes)    } catch (IOException e) {        throw new InternalError("unexpected I/O Exception", e);    }    return bout.toByteArray();}

一共三个步骤(把大象装进冰箱分几步?):

  • 第一步:(把冰箱门关上)筹备工作,将所有办法包装成ProxyMethod对象,包含Object类中hashCode、equals、toString办法,以及被代理的接口中的办法
  • 第二步:(把大象装进去)为代理类组装字段,构造函数,办法,static初始化块等
  • 第三步:(把冰箱门带上)写入class文件

从生成的Proxy代码看执行流程

从上述sun.misc.ProxyGenerator类中能够看到,这个类外面有一个配置参数sun.misc.ProxyGenerator.saveGeneratedFiles,能够通过这个参数将生成的Proxy类保留在本地,比方设置为true 执行后,生成的文件如下:

咱们看下生成后的代码:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.sun.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import java.util.List;import tech.pdai.springframework.service.IUserService;// 所有类和办法都是final类型的public final class $Proxy0 extends Proxy implements IUserService {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;    private static Method m4;    // 构造函数注入 InvocationHandler    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});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final List findUserList() throws  {        try {            return (List)super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    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);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final void addUser() throws  {        try {            super.h.invoke(this, m4, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    static {        try {            // 初始化 methods, 2个IUserService接口中的办法,3个Object中的接口            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));            m3 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("findUserList");            m2 = Class.forName("java.lang.Object").getMethod("toString");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");            m4 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("addUser");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

上述代码是比拟容易了解的,我就不画图了。

次要流程是:

  • ProxyGenerator创立Proxy的具体类$Proxy0
  • 由static初始化块初始化接口办法:2个IUserService接口中的办法,3个Object中的接口办法
  • 由构造函数注入InvocationHandler
  • 执行的时候,通过ProxyGenerator创立的Proxy,调用InvocationHandler的invoke办法,执行咱们自定义的invoke办法

SpringAOP中JDK代理的实现

SpringAOP表演的是JDK代理的创立和调用两个角色,咱们通过这两个方向来看下SpringAOP的代码(JdkDynamicAopProxy类)

SpringAOP Jdk代理的创立

代理的创立比较简单,调用getProxy办法,而后间接调用JDK中Proxy.newProxyInstance()办法将classloader和被代理的接口办法传入即可。

@Overridepublic Object getProxy() {    return getProxy(ClassUtils.getDefaultClassLoader());}@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {    if (logger.isTraceEnabled()) {        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());    }    return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);}

SpringAOP Jdk代理的执行

执行的办法如下:

/**    * Implementation of {@code InvocationHandler.invoke}.    * <p>Callers will see exactly the exception thrown by the target,    * unless a hook method throws an exception.    */@Override@Nullablepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    Object oldProxy = null;    boolean setProxyContext = false;    TargetSource targetSource = this.advised.targetSource;    Object target = null;    try {        // 执行的是equal办法        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {            // The target does not implement the equals(Object) method itself.            return equals(args[0]);        }        // 执行的是hashcode办法        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {            // The target does not implement the hashCode() method itself.            return hashCode();        }        // 如果是包装类,则dispatch to proxy config        else if (method.getDeclaringClass() == DecoratingProxy.class) {            // There is only getDecoratedClass() declared -> dispatch to proxy config.            return AopProxyUtils.ultimateTargetClass(this.advised);        }        // 用反射形式来执行切点        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&                method.getDeclaringClass().isAssignableFrom(Advised.class)) {            // Service invocations on ProxyConfig with the proxy config...            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);        }        Object retVal;        if (this.advised.exposeProxy) {            // Make invocation available if necessary.            oldProxy = AopContext.setCurrentProxy(proxy);            setProxyContext = true;        }        // Get as late as possible to minimize the time we "own" the target,        // in case it comes from a pool.        target = targetSource.getTarget();        Class<?> targetClass = (target != null ? target.getClass() : null);        // 获取拦挡链        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);        // Check whether we have any advice. If we don't, we can fallback on direct        // reflective invocation of the target, and avoid creating a MethodInvocation.        if (chain.isEmpty()) {            // We can skip creating a MethodInvocation: just invoke the target directly            // Note that the final invoker must be an InvokerInterceptor so we know it does            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);        }        else {            // We need to create a method invocation...            MethodInvocation invocation =                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);            // Proceed to the joinpoint through the interceptor chain.            retVal = invocation.proceed();        }        // Massage return value if necessary.        Class<?> returnType = method.getReturnType();        if (retVal != null && retVal == target &&                returnType != Object.class && returnType.isInstance(proxy) &&                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {            // Special case: it returned "this" and the return type of the method            // is type-compatible. Note that we can't help if the target sets            // a reference to itself in another returned object.            retVal = proxy;        }        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {            throw new AopInvocationException(                    "Null return value from advice does not match primitive return type for: " + method);        }        return retVal;    }    finally {        if (target != null && !targetSource.isStatic()) {            // Must have come from TargetSource.            targetSource.releaseTarget(target);        }        if (setProxyContext) {            // Restore old proxy.            AopContext.setCurrentProxy(oldProxy);        }    }}

示例源码

https://github.com/realpdai/t...

更多文章

首先, 从Spring框架的整体架构和组成对整体框架有个认知。
  • Spring根底 - Spring和Spring框架组成

    • Spring是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑Spring和Spring Framework的整体认知。
其次,通过案例引出Spring的外围(IoC和AOP),同时对IoC和AOP进行案例应用剖析。
  • Spring根底 - Spring简略例子引入Spring的外围

    • 上文中咱们简略介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文次要承接上文,向你展现Spring Framework组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出Spring的外围要点,比方IOC和AOP等;在此基础上还引入了不同的配置形式, 如XML,Java配置和注解形式的差别。
  • Spring根底 - Spring外围之管制反转(IOC)

    • 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了IoC的根底含意,同时以此发散了一些IoC相干知识点; 本节将在此基础上进一步解读IOC的含意以及IOC的应用形式
  • Spring根底 - Spring外围之面向切面编程(AOP)

    • 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了AOP的根底含意,同时以此发散了一些AOP相干知识点; 本节将在此基础上进一步解读AOP的含意以及AOP的应用形式。
基于Spring框架和IOC,AOP的根底,为构建下层web利用,须要进一步学习SpringMVC。
  • Spring根底 - SpringMVC申请流程和案例

    • 前文咱们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那咱们如何更好的构建下层的利用呢(比方web 利用),这便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技术根底上,遵循上述Web MVC的标准推出的web开发框架,目标是为了简化Java栈的web开发。 本文次要介绍SpringMVC的申请流程和根底案例的编写和运行。
Spring进阶 - IoC,AOP以及SpringMVC的源码剖析
  • Spring进阶 - Spring IOC实现原理详解之IOC体系结构设计

    • 在对IoC有了初步的认知后,咱们开始对IOC的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看IOC最顶层的结构设计
  • Spring进阶 - Spring IOC实现原理详解之IOC初始化流程

    • 上文,咱们看了IOC设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的
  • Spring进阶 - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)

    • 上文,咱们看了IOC设计要点和设计构造;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中寄存的是Bean的定义即BeanDefinition放到beanDefinitionMap中,实质上是一个ConcurrentHashMap<String, Object>;并且BeanDefinition接口中蕴含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢,这是本文次要钻研的内容?
  • Spring进阶 - Spring AOP实现原理详解之切面实现

    • 前文,咱们剖析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。本文次要介绍Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理加强实现做筹备的过程)。
  • Spring进阶 - Spring AOP实现原理详解之AOP代理

    • 上文咱们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上持续介绍,代理(cglib代理和JDK代理)的实现过程。
  • Spring进阶 - Spring AOP实现原理详解之Cglib代理实现

    • 咱们在前文中曾经介绍了SpringAOP的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍Cglib动静代理的案例和SpringAOP实现的原理。
  • Spring进阶 - Spring AOP实现原理详解之JDK代理实现

    • 上文咱们学习了SpringAOP Cglib动静代理的实现,本文次要是SpringAOP JDK动静代理的案例和实现局部。
  • Spring进阶 - SpringMVC实现原理之DispatcherServlet初始化的过程

    • 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第一篇:DispatcherServlet的初始化过程的源码解析。
  • Spring进阶 - SpringMVC实现原理之DispatcherServlet解决申请的过程

    • 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第二篇:DispatcherServlet解决申请的过程的源码解析。