Spring框架系列(12) – Spring AOP实现原理详解之JDK代理实现

上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。@pdai

引入

上文我们学习了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

tech.pdai.springframework.entity;

/**
* @author pdai
*/
{

/**
* user's name.
*/
String name;

/**
* user's age.
*/
age;

/**
* init.
*
* @param name name
* @param age age
*/
(String name, age) {
.name = name;
.age = age;
}

String () {
name;
}

(String name) {
.name = name;
}

() {
age;
}

( age) {
.age = age;
}

@Override
String () {
"User{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}

被代理的类和接口

接口如下

tech.pdai.springframework.service;

tech.pdai.springframework.entity.User;

java.util.List;

/**
* @author pdai
*/
{

/**
* find user list.
*
* @return user list
*/
List<User> ();

/**
* add user
*/
();
}

实现类如下:

tech.pdai.springframework.service;

tech.pdai.springframework.entity.User;

java.util.Collections;
java.util.List;

/**
* @author pdai
*/
{

/**
* find user list.
*
* @return user list
*/
@Override
List<User> () {
Collections.singletonList( User("pdai", 18));
}

/**
* add user
*/
@Override
() {
// do something
}

}

JDK代理类

代理类如下:

tech.pdai.springframework.proxy;

tech.pdai.springframework.service.IUserService;
tech.pdai.springframework.service.UserServiceImpl;

java.lang.reflect.InvocationHandler;
java.lang.reflect.Method;
java.lang.reflect.Proxy;
java.util.Arrays;

/**
* This class is for proxy demo.
*
* @author pdai
*/
{

/**
* proxy target
*/
IUserService target;

/**
* init.
*
* @param target target
*/
(UserServiceImpl target) {
();
.target = target;
}

/**
* get proxy.
*
* @return proxy target
*/
IUserService () {
IUserService proxy;
ClassLoader loader = target.getClass().getClassLoader();
Class[] interfaces = Class[]{IUserService.};
InvocationHandler h = InvocationHandler() {
/**
* proxy: 代理对象。 一般不使用该对象 method: 正在被调用的方法 args: 调用方法传入的参数
*/
@Override
Object (Object proxy, Method method, Object[] args) Throwable {
String methodName = method.getName();
// log - before method
System.out.println("[before] execute method: " + methodName);

// call method
Object result = ;
{
// 前置通知
result = method.invoke(target, args);
// 返回通知, 可以访问到方法的返回值
} (NullPointerException e) {
e.printStackTrace();
// 异常通知, 可以访问到方法出现的异常
}
// 后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值

// log - after method
System.out.println("[after] execute method: " + methodName + ", return value: " + result);
result;
}
};
/**
* loader: 代理对象使用的类加载器.
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy = (IUserService) Proxy.newProxyInstance(loader, interfaces, h);
proxy;
}

}

使用代理

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

tech.pdai.springframework;

tech.pdai.springframework.proxy.UserLogProxy;
tech.pdai.springframework.service.IUserService;
tech.pdai.springframework.service.UserServiceImpl;

/**
* Jdk proxy demo.
*
* @author pdai
*/
{

/**
* main interface.
*
* @param args args
*/
(String[] args) {
// proxy
IUserService userService = UserLogProxy( 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
*/
[] generateProxyClass( String name,
Class<?>[] interfaces,
accessFlags)
{
ProxyGenerator gen = ProxyGenerator(name, interfaces, accessFlags);
[] classFile = gen.generateClassFile();
...
}

generateClassFile方法如下:

/**
* Generate a class file for the proxy class. This method drives the
* class file generation process.
*/
[] generateClassFile() {

/* 第一步:将所有方法包装成ProxyMethod对象 */

// 将Object类中hashCode、equals、toString方法包装成ProxyMethod对象
addProxyMethod(hashCodeMethod, Object.);
addProxyMethod(equalsMethod, Object.);
addProxyMethod(toStringMethod, Object.);

// 将代理类接口方法包装成ProxyMethod对象
(Class<?> intf : interfaces) {
(Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}

// 校验返回类型
(List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}

/* 第二步:为代理类组装字段,构造函数,方法,static初始化块等 */
{
// 添加构造函数,参数是InvocationHandler
methods.add(generateConstructor());

// 代理方法
(List<ProxyMethod> sigmethods : proxyMethods.values()) {
(ProxyMethod pm : sigmethods) {

// 字段
fields.add( FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));

// 上述ProxyMethod中的方法
methods.add(pm.generateMethod());
}
}

// static初始化块
methods.add(generateStaticInitializer());

} (IOException e) {
InternalError("unexpected I/O Exception", e);
}

(methods.size() > 65535) {
IllegalArgumentException("method limit exceeded");
}
(fields.size() > 65535) {
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);
(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 = ByteArrayOutputStream();
DataOutputStream dout = DataOutputStream(bout);

{
/*
* 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];
(Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}

// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
(FieldInfo f : fields) {
f.write(dout);
}

// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
(MethodInfo m : methods) {
m.write(dout);
}

// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)

} (IOException e) {
InternalError("unexpected I/O Exception", e);
}

bout.toByteArray();
}

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

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

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

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

Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现

我们看下生成后的代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

com.sun.proxy;

java.lang.reflect.InvocationHandler;
java.lang.reflect.Method;
java.lang.reflect.Proxy;
java.lang.reflect.UndeclaredThrowableException;
java.util.List;
tech.pdai.springframework.service.IUserService;

// 所有类和方法都是final类型的
$ {
Method m1;
Method m3;
Method m2;
Method m0;
Method m4;

// 构造函数注入 InvocationHandler
$Proxy0(InvocationHandler var1) {
(var1);
}

(Object var1) {
{
(Boolean).h.invoke(, m1, Object[]{var1});
} (RuntimeException | Error var3) {
var3;
} (Throwable var4) {
UndeclaredThrowableException(var4);
}
}

List () {
{
(List).h.invoke(, m3, (Object[]));
} (RuntimeException | Error var2) {
var2;
} (Throwable var3) {
UndeclaredThrowableException(var3);
}
}

String () {
{
(String).h.invoke(, m2, (Object[]));
} (RuntimeException | Error var2) {
var2;
} (Throwable var3) {
UndeclaredThrowableException(var3);
}
}

() {
{
(Integer).h.invoke(, m0, (Object[]));
} (RuntimeException | Error var2) {
var2;
} (Throwable var3) {
UndeclaredThrowableException(var3);
}
}

() {
{
.h.invoke(, m4, (Object[]));
} (RuntimeException | Error var2) {
var2;
} (Throwable var3) {
UndeclaredThrowableException(var3);
}
}

{
{
// 初始化 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");
} (NoSuchMethodException var2) {
NoSuchMethodError(var2.getMessage());
} (ClassNotFoundException var3) {
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和被代理的接口方法传入即可。

@Override
Object () {
getProxy(ClassUtils.getDefaultClassLoader());
}

@Override
Object (@Nullable ClassLoader classLoader) {
(logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + .advised.getTargetSource());
}
Proxy.newProxyInstance(classLoader, .proxiedInterfaces, );
}

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
@Nullable
Object (Object proxy, Method method, Object[] args) Throwable {
Object oldProxy = ;
setProxyContext = ;

TargetSource targetSource = .advised.targetSource;
Object target = ;

{
// 执行的是equal方法
(!.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
equals(args[0]);
}
// 执行的是hashcode方法
(!.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
hashCode();
}
// 如果是包装类,则dispatch to proxy config
(method.getDeclaringClass() == DecoratingProxy.) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
AopProxyUtils.ultimateTargetClass(.advised);
}
// 用反射方式来执行切点
(!.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.)) {
// Service invocations on ProxyConfig with the proxy config...
AopUtils.invokeJoinpointUsingReflection(.advised, method, args);
}

Object retVal;

(.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = ;
}

// 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 != ? target.getClass() : );

// 获取拦截链
List<Object> chain = .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.
(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);
}
{
// We need to create a method invocation...
MethodInvocation invocation =
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();
(retVal != && retVal == target &&
returnType != Object. && .() &&
!..(.())) {
// 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;
}
(retVal == && returnType != Void.TYPE && returnType.isPrimitive()) {
AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
retVal;
}
{
(target != && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
(setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

示例源码

​https://github.com/realpdai/tech-pdai-spring-demos​

更多文章

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

  • Spring是什么?它是怎么诞生的?有哪些主要的组件和核心功能呢? 本文通过这几个问题帮助你构筑Spring和Spring Framework的整体认知。

其次,通过案例引出Spring的核心(IoC和AOP),同时对IoC和AOP进行案例使用分析。

  • 上文中我们简单介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文主要承接上文,向你展示Spring Framework组件的典型应用场景和基于这个场景设计出的简单案例,并以此引出Spring的核心要点,比如IOC和AOP等;在此基础上还引入了不同的配置方式, 如XML,Java配置和注解方式的差异。

基于Spring框架和IOC,AOP的基础,为构建上层web应用,需要进一步学习SpringMVC。

  • 前文我们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那我们如何更好的构建上层的应用呢(比如web 应用),这便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发。 本文主要介绍SpringMVC的请求流程和基础案例的编写和运行。

Spring进阶 - IoC,AOP以及SpringMVC的源码分析

  • 在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解。本文将帮助你站在设计者的角度去看IOC最顶层的结构设计
  • 上文,我们看了IOC设计要点和设计结构;紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的
  • 上文,我们看了IOC设计要点和设计结构;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中存放的是Bean的定义即BeanDefinition放到beanDefinitionMap中,本质上是一个ConcurrentHashMap<String, Object>;并且BeanDefinition接口中包含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢,这是本文主要研究的内容?
  • 前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备的过程)。
  • 上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上继续介绍,代理(cglib代理和JDK代理)的实现过程。
  • 我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。
  • 上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。
  • 前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第一篇:DispatcherServlet的初始化过程的源码解析。
  • 前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第二篇:DispatcherServlet处理请求的过程的源码解析。
发表评论

相关文章