有没有免费网站建设百度广告投放平台
文章目录
- 系列文档索引
- 一、认识AOP
- 1、AOP的引入原因
- 2、AOP常见使用场景
- 日志场景
- 统计场景
- 安防场景
- 性能场景
- 3、AOP概念
- AOP 的概念
- Aspect 概念(切面)
- Join point 概念(连接点)
- Pointcut 概念(切入点)
- Advice 概念(通知、动作)
- Introduction 概念(声明)
- 二、认识Spring AOP
- 1、Spring AOP的设计目标(来自官方文档)
- 2、Spring AOP的工作模式
- (1)代理模式
- ① 静态代理
- ② 动态代理
- (2)判断模式
- (3)拦截模式
- 3、Spring AOP核心特性
- 4、Spring AOP 编程模型
- 注解驱动
- XML 配置驱动
- 底层 API
- 三、Spring AOP用到的代理方式
- 1、JDK 动态代理实现
- (1)getProxyClass0方法
- (3)总结
- 2、CGLIB 动态代理实现(Spring中)
- (1)代码实例
- (2)总结
- 3、AspectJ 适配实现
- (1)AspectJ 语法
- (2)AspectJ 注解
- (3)总结
- (4)Spring AOP 和 AspectJ AOP 存在哪些区别?
- 未完待续
- 参考资料
系列文档索引
SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(一)
SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(二)
SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(三)
SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(四)
一、认识AOP
1、AOP的引入原因
java是静态语言,一旦定义好结构不容易被修改,而且传统的扩展方式都是通过继承和组合的方式组织新的类结构,侵入性太强。
所以aop的出现就是为了解决这个问题,就是让它方便的被修改从而对类的结构进行增强。
Java的Class类一旦被ClassLoader加载之后,就会存储在永久代(JDK8存储在元数据区),要想对其进行修改可太难了,使用ASM或者其他方式对开发者太不友好了,而AOP正是完美解决这个问题,AOP是不需要改变过去的类的结构进行扩展。
2、AOP常见使用场景
日志场景
诊断上下文,如:log4j或logback中的MDC。
记录方法入参出参等关键日志。
辅助信息,如:记录方法执行时间。
统计场景
记录方法调用次数、记录执行异常次数
数据抽样、数值累加
安防场景
熔断,如:Netflix Hystrix
限流和降级:如:Alibaba Sentinel
认证和授权,如:Spring Security
监控,如:JMX
性能场景
缓存,如 Spring Cache
超时控制
3、AOP概念
AOP 的概念
(1)AspectJ中定义的AOP
Aspect-oriented programming is a way of modularizing crosscutting concerns much like object-oriented programming is a way of modularizing common concerns.
面向切面编程是模块化横切关注点的一种方式,就像面向对象编程是模块化公共关注点的一种方式一样。
(2)Spring中定义的AOP
Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns (such as transaction management) that cut across multiple types and objects.
面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。在OOP中模块化的关键单位是类,而在AOP中模块化的关键单位是切面。切面支持跨越多个类型和对象的关注点(例如事务管理)的模块化。
AOP是不需要改变过去的类的结构进行扩展,如果使用OOP需要对过去的类的结构进行改变。
Aspect 概念(切面)
(1)AspectJ中定义的Aspect
aspect are the unit of modularity for crosscutting concerns. They behave somewhat like Java classes, but may also include pointcuts, advice and inter-type declarations.
切面是横切关注点的模块化单位。它们的行为有点像Java类,但也可能包括切入点、通知和类型间声明。
(2)Spring中定义的Aspect
A modularization of a concern that cuts across multiple classes.
跨越多个类的关注点的模块化。
Join point 概念(连接点)
(1)AspectJ中定义的Join point
A join point is a well-defined point in the program flow. A pointcut picks out certain join points and values at those points.
连接点是程序流中定义良好的点。切入点挑选出某些连接点和这些点上的值。
(2)Spring中定义的Join point
A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
程序执行期间的一个点,如方法的执行或异常的处理。在Spring AOP中,一个连接点总是代表一个方法执行。
Pointcut 概念(切入点)
(1)AspectJ中定义的Pointcut
pointcuts pick out certain join points in the program flow.
切入点挑选出程序流中的某些连接点。
(2)Spring中定义的Pointcut
A predicate that matches join points.
匹配连接点的条件。
Advice 概念(通知、动作)
(1)AspectJ中定义的Advice
So pointcuts pick out join points. But they don’t do anything apart from picking out join points. To actually implement crosscutting behavior, we use advice. Advice brings together a pointcut (to pick out join points) and a body of code (to run at each of those join points).
因此,切入点挑选出连接点。但是除了选择连接点之外,它们什么也不做。为了实际实现横切行为,我们使用通知。通知将切入点(用于挑选连接点)和代码体(用于在每个连接点上运行)组合在一起。
(2)Spring中定义的Advice
Action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. Many AOP frameworks, including Spring, model an advice as an interceptor and maintain a chain of interceptors around the join point.
方面在特定连接点上采取的操作。不同类型的建议包括“前后”、“之前”和“之后”的动作。许多AOP框架,包括Spring,都将通知建模为一个拦截器,并围绕连接点维护一个拦截器链。
Introduction 概念(声明)
(1)AspectJ中定义的Introduction
Inter-type declarations in AspectJ are declarations that cut across classes and their hierarchies. They may declare members that cut across multiple classes, or change the inheritance relationship between classes.
AspectJ中的类型间声明是跨越类及其层次结构的声明。它们可以声明跨越多个类的成员,或者改变类之间的继承关系。
(2)Spring中定义的Introduction
Declaring additional methods or fields on behalf of a type. Spring AOP lets you introduce new interfaces (and a corresponding implementation) to any advised object.
代表类型声明额外的方法或字段。Spring AOP允许您向任何被建议的对象引入新的接口(以及相应的实现)。
二、认识Spring AOP
1、Spring AOP的设计目标(来自官方文档)
Spring AOP’s approach to AOP differs from that of most other AOP frameworks. The aim is not to provide the most complete AOP implementation (although Spring AOP is quite capable). Rather, the aim is to provide a close integration between AOP implementation and Spring IoC, to help solve common problems in enterprise applications.
Spring AOP实现AOP的方法不同于大多数其他AOP框架。其目的不是提供最完整的AOP实现(尽管Spring AOP相当能干)。相反,其目的是提供AOP实现和Spring IOC之间的紧密集成,以帮助解决企业应用程序中的常见问题。
Spring AOP never strives to compete with AspectJ to provide a comprehensive AOP solution. We believe that both proxy-based frameworks such as Spring AOP and full-blown frameworks such as AspectJ are valuable and that they are complementary, rather than in competition.Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable all uses of AOP within a consistent Spring-based application architecture. This integration does not affect the Spring AOP API or the AOP Alliance API. Spring AOP remains backwardcompatible.
Spring AOP不与AspectJ竞争以提供全面的AOP解决方案。我们相信基于代理的框架(如Spring AOP)和成熟的框架(如AspectJ)都是有价值的,它们是互补的,而不是竞争的。Spring无缝地将Spring AOP和IOC与AspectJ集成在一起,以支持在一致的基于Spring的应用程序架构中使用AOP。这个集成不会影响Spring AOP API或AOP Alliance API。Spring AOP仍然向后兼容。
实际上,Spring AOP并未对编译期进行字节码的处理,而是都在运行期间进行处理的,通常都是用反射来实现的。
2、Spring AOP的工作模式
(1)代理模式
① 静态代理
Java静态代理,一般常用就是面向对象(OOP)继承和组合相结合的方式进行代理。
代码实例:
public interface EchoService {String echo(String message) throws NullPointerException;
}public class DefaultEchoService implements EchoService {@Overridepublic String echo(String message) {return "[ECHO] " + message;}
}public class ProxyEchoService implements EchoService {private final EchoService echoService;public ProxyEchoService(EchoService echoService) {this.echoService = echoService;}@Overridepublic String echo(String message) {long startTime = System.currentTimeMillis();String result = echoService.echo(message);long costTime = System.currentTimeMillis() - startTime;System.out.println("echo 方法执行的时间:" + costTime + " ms.");return result;}
}public class StaticProxyDemo {public static void main(String[] args) {EchoService echoService = new ProxyEchoService(new DefaultEchoService());echoService.echo("Hello,World");}
}
上面静态代理的实例,就是基于接口,需要实现同一个接口,侵入性很强,一般也称为装饰器模式。
② 动态代理
Java动态代理,常用的是JDK动态搭理或者字节码提升(如CGLIB)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** JDK动态代理实例*/
public class JDKDynamicProxyDemo {public static void main(String[] args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 真实的对象DefaultEchoService realObj = new DefaultEchoService();// 代理的对象Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态前置");Object obj = null;if (EchoService.class.isAssignableFrom(method.getDeclaringClass())) {// 执行真实方法obj = method.invoke(realObj, args);}System.out.println("动态后置");return obj;}});EchoService echoService = (EchoService) proxy;System.out.println(echoService.echo("Hello,World"));}
}
注意!这里需要DefaultEchoService 实现EchoService接口,代理的其实是EchoService接口而不是DefaultEchoService 类。
动态代理对原代码没有侵入性,通常可以动态加载。
(2)判断模式
类型(Class)、方法(Method)、注解(Annotation)、参数(Parameter)、异常(Exception)
SpringAOP通常都是对方法的拦截,此处的操作都是基于方法上的注解、参数、异常等。
public interface EchoService {String echo(String message) throws NullPointerException;
}
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;/*** AOP 目标过滤示例*/
public class TargetFilterDemo {public static void main(String[] args) throws ClassNotFoundException {String targetClassName = "com.demo.EchoService";// 获取当前线程 ClassLoaderClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 获取目标类Class<?> targetClass = classLoader.loadClass(targetClassName);// 方法定义:String echo(String message);// Spring 反射工具类,获取目标类指定方法Method targetMethod = ReflectionUtils.findMethod(targetClass, "echo", String.class);System.out.println(targetMethod);// 查找方法 throws 类型为 NullPointerExceptionReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {@Overridepublic void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {System.out.println("仅抛出 NullPointerException 方法为:" + method);}}, new ReflectionUtils.MethodFilter() {// 对方法进行过滤@Overridepublic boolean matches(Method method) {Class[] parameterTypes = method.getParameterTypes(); // 判断参数Class[] exceptionTypes = method.getExceptionTypes(); // 判断抛出的异常Annotation[] annotations = method.getAnnotations(); // 判断注解return parameterTypes.length == 1&& String.class.equals(parameterTypes[0])&& exceptionTypes.length == 1&& NullPointerException.class.equals(exceptionTypes[0]);}});}
}
(3)拦截模式
前置拦截(Before)、后置拦截(After)、异常拦截(Exception)、finally等等。
还有一种比较特殊的Around围绕模式。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** AOP拦截模式代码实例*/
public class InterceptorDemo {public static void main(String[] args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 真实的对象DefaultEchoService realObj = new DefaultEchoService();// 前置拦截器Interceptor beforeInterceptor = new Interceptor() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args, Object returnResult, Throwable throwable) {return System.currentTimeMillis();}};// 后置拦截器Interceptor afterInterceptor = new Interceptor() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args, Object returnResult, Throwable throwable) {return System.currentTimeMillis();}};// 异常拦截器Interceptor exceptionInterceptor = new Interceptor() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args, Object returnResult, Throwable throwable) {throwable.printStackTrace();return null;}};// finally拦截器Interceptor finallyInterceptor = new Interceptor() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args, Object returnResult, Throwable throwable) {return null;}};// 代理的对象Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Long startTime = 0L;Long endTime = 0L;Object result = null;Object obj = null;if (EchoService.class.isAssignableFrom(method.getDeclaringClass())) {try {// 执行beforestartTime = (Long)beforeInterceptor.invoke(proxy, method, args, null, null);// 执行真实方法obj = method.invoke(realObj, args);// 执行afterendTime = (Long) afterInterceptor.invoke(proxy, method, args, obj, null);} catch (Exception e) {// 执行异常拦截器exceptionInterceptor.invoke(proxy, method, args, null, e);} finally {// 执行finallyfinallyInterceptor.invoke(proxy, method, args, obj, null);System.out.println("共消耗时间:" + (endTime - startTime));}}return obj;}});EchoService echoService = (EchoService) proxy;System.out.println(echoService.echo("Hello,World"));}static interface Interceptor {Object invoke(Object proxy, Method method, Object[] args, Object returnResult, Throwable throwable);}
}
Spring AOP也是基于类似的接口来进行实现的。
3、Spring AOP核心特性
• 纯 Java 实现、无编译时特殊处理、不修改和控制 ClassLoader
• 仅支持方法级别的 Join Points
• 非完整 AOP 实现框架
• 需要Spring IoC 容器整合
• AspectJ 注解驱动整合(非竞争关系)
Spring AOP(JDK动态代理,CGLIB) 是运行期修改class文件,AspectJ是编译期修改class文件。
Spring 并没有使用 AspectJ 的编译器,而是利用反射来实现的(后续慢慢讲解)。
4、Spring AOP 编程模型
注解驱动
实现:
Enable 模块驱动,@EnableAspectJAutoProxy
注解:
• 激活 AspectJ 自动代理:@EnableAspectJAutoProxy
• Aspect : @Aspect
• Pointcut :@Pointcut
• Advice :@Before、@AfterReturning、@AfterThrowing、@After、@Around
• Introduction :@DeclareParents
XML 配置驱动
实现:
Spring Extensble XML Authoring
XML 元素
• 激活 AspectJ 自动代理:<aop:aspectj-autoproxy/>
• 配置:<aop:config/>
• Aspect : <aop:aspect/>
• Pointcut :<aop:pointcut/>
• Advice :<aop:around/>、<aop:before/>、<aop:after-returning/>、<aop:after-throwing/> 和<aop:after/>
• Introduction :<aop:declare-parents/>
• 代理 Scope : <aop:scoped-proxy/>
底层 API
实现:
JDK 动态代理、CGLIB 以及 AspectJ
API:
• 代理:AopProxy
• 配置:ProxyConfig
• Join Point:JoinPoint
• Pointcut :Pointcut
• Advice :Advice、BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice
三、Spring AOP用到的代理方式
1、JDK 动态代理实现
JDK 动态代理实现,一般是基于接口代理。在Spring中的核心类是JdkDynamicAopProxy,它实现了AopProxy接口:
public interface AopProxy {Object getProxy();Object getProxy(@Nullable ClassLoader classLoader);
}
JDK动态代理的实例我们上面已经展示过了,为什么 Proxy.newProxyInstance 会生成新的字节码?
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
});System.out.println(proxy.getClass());// com.sun.proxy.$Proxy0Object proxy2 = Proxy.newProxyInstance(classLoader, new Class[]{Comparable.class}, (proxy1, method, args1) -> {return null;});System.out.println(proxy2.getClass());// com.sun.proxy.$Proxy1
上面代码我们会发现,Java动态代理每生成一个代理,它的class总是com.sun.proxy包下的$Proxy*,从0开始累加,它是如何实现的呢?
我们来分析一下Proxy的newProxyInstance方法:
// java.lang.reflect.Proxy#newProxyInstance
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
{Objects.requireNonNull(h);// 对象克隆final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/// 先从缓存获取(见 (1))Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理对象的构造方法,带着InvocationHandler参数的构造方法final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}// 返回Proxy对象return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}
}
(1)getProxyClass0方法
在getProxyClass0方法中,从proxyClassCache缓存中获取了这个代理类:
// java.lang.reflect.Proxy#getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// If the proxy class defined by the given loader implementing// the given interfaces exists, this will simply return the cached copy;// otherwise, it will create the proxy class via the ProxyClassFactoryreturn proxyClassCache.get(loader, interfaces);
}
而proxyClassCache在初始化时,自动创建了KeyFactory和ProxyClassFactory
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory的核心方法apply,隐藏着代理接口的创建逻辑:
// java.lang.reflect.Proxy.ProxyClassFactory
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>
{// prefix for all proxy class namesprivate static final String proxyClassNamePrefix = "$Proxy";// next number to use for generation of unique proxy class namesprivate static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) { // 遍历我们传入的接口数组/** Verify that the class loader resolves the name of this* interface to the same Class object.*/Class<?> interfaceClass = null;try {// 通过classLoader加载我们的接口interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}/** Verify that the Class object actually represents an* interface.*/if (!interfaceClass.isInterface()) {// 只能代理接口,非接口直接抛异常throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}String proxyPkg = null; // package to define proxy class inint accessFlags = Modifier.PUBLIC | Modifier.FINAL;/** Record the package of a non-public proxy interface so that the* proxy class will be defined in the same package. Verify that* all non-public proxy interfaces are in the same package.*/for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg == null) { // 包名就是com.sun.proxy// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}/** Choose a name for the proxy class to generate.*/long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num; // 依次递增/** Generate the specified proxy class.*/// 代理类生成器,返回字节数组,就是字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {// classLoader加载类,是一个native方法,返回一个Class对象return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}
}
(3)总结
vm options参数设置-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,就可以把生成的代理类的源码保存在com.sun.proxy目录下面,或者用arthas来查看运行中的类信息。
JDK动态代理生成的代理类,我们通过反编译,发现其实是这个样子的:
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 com.demo.EchoService;public final class $Proxy0
extends Proxy
implements EchoService {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m3 = Class.forName("com.demo.EchoService").getMethod("echo", Class.forName("java.lang.String"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String echo(String string) throws NullPointerException {try {return (String)this.h.invoke(this, m3, new Object[]{string});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}
InvocationHandler就是Proxy.newProxyInstance传入的最后一个参数。
当调用代理对象的方法时,会执行InvocationHandler的invoke方法。
注意,JDK生成的代理类的包名不总是com.sun.proxy,只有当接口为Public时是这样的,当接口为非public时,生成的代理类与接口所在包名相同。
2、CGLIB 动态代理实现(Spring中)
CGLIB 动态代理实现,一般是基于类代理(字节码提升)。在Spring中核心类是CglibAopProxy,同样实现了AopProxy接口。
(1)代码实例
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** CGLIB 动态代理示例*/
public class CglibDynamicProxyDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();// 指定 super class = DefaultEchoService.classClass<?> superClass = DefaultEchoService.class;enhancer.setSuperclass(superClass);// 指定拦截接口,可以不写//enhancer.setInterfaces(new Class[]{EchoService.class});enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object source, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {long startTime = System.currentTimeMillis();// 错误使用
// Object result = method.invoke(source, args);// 正确的方法调用Object result = methodProxy.invokeSuper(source, args);long costTime = System.currentTimeMillis() - startTime;System.out.println("[CGLIB 字节码提升] echo 方法执行的实现:" + costTime + " ms.");return result;}});// 创建代理对象EchoService echoService = (EchoService) enhancer.create();// 输出执行结果System.out.println(echoService.echo("Hello,World")); // 8ms,对性能影响较大}
}
(2)总结
Java 动态代理无法满足 AOP 的需要,JDK动态代理:只能针对interface进行动态代理,无法对普通类进行动态代理,Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效。
CGLIB能够代理普通类(可以不需要接口) ,CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效,但是类的创建过程比较耗时。
CGLIB默认是在类同包下生成一个类,通过反编译我们发现生成的代理类如下:
import java.lang.reflect.Method;
import com.demo.DefaultEchoService;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;public class DefaultEchoService$$EnhancerByCGLIB$$c868af31
extends DefaultEchoService
implements Factory {private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$echo$0$Method;private static final MethodProxy CGLIB$echo$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class<?> clazz = Class.forName("com.demo.DefaultEchoService$$EnhancerByCGLIB$$c868af31");Class<?> clazz2 = Class.forName("com.demo.DefaultEchoService");CGLIB$echo$0$Method = ReflectUtils.findMethods(new String[]{"echo", "(Ljava/lang/String;)Ljava/lang/String;"}, clazz2.getDeclaredMethods())[0];CGLIB$echo$0$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/String;)Ljava/lang/String;", "echo", "CGLIB$echo$0");clazz2 = Class.forName("java.lang.Object");Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());CGLIB$equals$1$Method = methodArray[0];CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = methodArray[1];CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = methodArray[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = methodArray[3];CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");}final String CGLIB$echo$0(String string) {return super.echo(string);}public final String echo(String string) {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {return (String)methodInterceptor.intercept(this, CGLIB$echo$0$Method, new Object[]{string}, CGLIB$echo$0$Proxy);}return super.echo(string);}final boolean CGLIB$equals$1(Object object) {return super.equals(object);}public final boolean equals(Object object) {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object2 = methodInterceptor.intercept(this, CGLIB$equals$1$Method, new Object[]{object}, CGLIB$equals$1$Proxy);return object2 == null ? false : (Boolean)object2;}return super.equals(object);}final String CGLIB$toString$2() {return super.toString();}public final String toString() {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {return (String)methodInterceptor.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);}return super.toString();}final int CGLIB$hashCode$3() {return super.hashCode();}public final int hashCode() {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object = methodInterceptor.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);return object == null ? 0 : ((Number)object).intValue();}return super.hashCode();}final Object CGLIB$clone$4() throws CloneNotSupportedException {return super.clone();}protected final Object clone() throws CloneNotSupportedException {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {return methodInterceptor.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);}return super.clone();}public static MethodProxy CGLIB$findMethodProxy(Signature signature) {String string = ((Object)signature).toString();switch (string.hashCode()) {case -1042135322: {if (!string.equals("echo(Ljava/lang/String;)Ljava/lang/String;")) break;return CGLIB$echo$0$Proxy;}case -508378822: {if (!string.equals("clone()Ljava/lang/Object;")) break;return CGLIB$clone$4$Proxy;}case 1826985398: {if (!string.equals("equals(Ljava/lang/Object;)Z")) break;return CGLIB$equals$1$Proxy;}case 1913648695: {if (!string.equals("toString()Ljava/lang/String;")) break;return CGLIB$toString$2$Proxy;}case 1984935277: {if (!string.equals("hashCode()I")) break;return CGLIB$hashCode$3$Proxy;}}return null;}public DefaultEchoService$$EnhancerByCGLIB$$c868af31() {DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = this;DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(defaultEchoService$$EnhancerByCGLIB$$c868af31);}public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {CGLIB$THREAD_CALLBACKS.set(callbackArray);}public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {CGLIB$STATIC_CALLBACKS = callbackArray;}private static final void CGLIB$BIND_CALLBACKS(Object object) {block2: {Object object2;block3: {DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = (DefaultEchoService$$EnhancerByCGLIB$$c868af31)object;if (defaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BOUND) break block2;defaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BOUND = true;object2 = CGLIB$THREAD_CALLBACKS.get();if (object2 != null) break block3;object2 = CGLIB$STATIC_CALLBACKS;if (CGLIB$STATIC_CALLBACKS == null) break block2;}defaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];}}@Overridepublic Object newInstance(Callback[] callbackArray) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(callbackArray);DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = new DefaultEchoService$$EnhancerByCGLIB$$c868af31();DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(null);return defaultEchoService$$EnhancerByCGLIB$$c868af31;}@Overridepublic Object newInstance(Callback callback) {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = new DefaultEchoService$$EnhancerByCGLIB$$c868af31();DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(null);return defaultEchoService$$EnhancerByCGLIB$$c868af31;}@Overridepublic Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31;DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(callbackArray);Class[] classArray2 = classArray;switch (classArray.length) {case 0: {defaultEchoService$$EnhancerByCGLIB$$c868af31 = new DefaultEchoService$$EnhancerByCGLIB$$c868af31();break;}default: {throw new IllegalArgumentException("Constructor not found");}}DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$SET_THREAD_CALLBACKS(null);return defaultEchoService$$EnhancerByCGLIB$$c868af31;}@Overridepublic Callback getCallback(int n) {MethodInterceptor methodInterceptor;DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);switch (n) {case 0: {methodInterceptor = this.CGLIB$CALLBACK_0;break;}default: {methodInterceptor = null;}}return methodInterceptor;}@Overridepublic void setCallback(int n, Callback callback) {switch (n) {case 0: {this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;break;}}}@Overridepublic Callback[] getCallbacks() {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$BIND_CALLBACKS(this);DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = this;return new Callback[]{this.CGLIB$CALLBACK_0};}@Overridepublic void setCallbacks(Callback[] callbackArray) {Callback[] callbackArray2 = callbackArray;DefaultEchoService$$EnhancerByCGLIB$$c868af31 defaultEchoService$$EnhancerByCGLIB$$c868af31 = this;this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];}static {DefaultEchoService$$EnhancerByCGLIB$$c868af31.CGLIB$STATICHOOK1();}
}
我们可以看出,生成这个类比JDK动态代理生成的类复杂多了,但是也不难看出,该代理类继承了我们的父类DefaultEchoService,并不像JDK动态代理那样必须实现同一个接口。
而且代理类的echo方法,是直接调用的super.echo()方法,不像JDK动态代理,使用的是反射调用的,所以CGLIB执行的性能是略优于JDK动态代理的,但是代理类的生成速度是比较差的。
3、AspectJ 适配实现
AspectJ动态代理实现,核心类是AspectJProxyFactory,与AspectJ实现了整合。
Spring aop包含 jdk动态代理、cglib动态代理和aspectj,而DefaultAopProxyFactory 却只包含了前两种创建代理对象的实现,而没包含aspectj,而是单独定义了一个AspectJProxyFactory。实际上 Spring AOP 整合 AspectJ 时,并未使用 AspectJ 的编译器,它是基于 Java 反射来实现,所以只有两种创建代理的方式。这个问题后续会慢慢讲明白。
Spring官网 推荐使用 AspectJ 注解。@AspectJ是一种将方面声明为带有注释的常规Java类的风格。@AspectJ样式是由AspectJ项目作为AspectJ 5发行版的一部分引入的。Spring解释与AspectJ 5相同的注释,使用AspectJ提供的切入点解析和匹配库。不过,AOP运行时仍然是纯Spring AOP,并且不依赖于AspectJ编译器或waever提升器。
(1)AspectJ 语法
• Aspect
• Join Points
• Pointcuts
• Advice
• Introduction
aspectj官方文档
AspectJ 语法用起来对程序员非常不友好,Spring中建议使用AspectJ的注解方式,Spring将其与IOC完美结合了。
(2)AspectJ 注解
• 激活 AspectJ 自动代理:@EnableAspectJAutoProxy
• Aspect : @Aspect
• Pointcut :@Pointcut
• Advice :@Before、@AfterReturning、@AfterThrowing、@After、@Around
• Introduction :@DeclareParents
(3)总结
代理的类型只有两种 JDK 动态代理和 CGLIB 代理,AspectJAopProxy 只是一种利用 AspectJ 表达式的提升,简化了繁杂的 Spring AOP API 的实现。
Spring AOP代理对象还是由JDK动态代理或者是Cglib来实现。AspectJ 主要被 Spring 用于解析其 Pointcut 表达式
(4)Spring AOP 和 AspectJ AOP 存在哪些区别?
• AspectJ 是 _x0008_AOP 完整实现,Spring AOP 则是部分实现
• Spring AOP 比 AspectJ 使用更简单
• Spring AOP 整合 AspectJ 注解与 Spring IoC 容器
• Spring AOP 仅支持基于代理模式的 AOP
• Spring AOP 仅支持方法级别的 Pointcuts
未完待续
参考资料
极客时间《小马哥讲 Spring AOP 编程思想》