一、AOP(Aspect Orient Programming,面向切面编程)相关术语:
1. 切面(Aspect):实现通用问题的类,例如日志,事务管理,定义了切入点和通知的类,通知和切入点共同组成了切面:时间、地点、做什么
2. 通知(Advice):类似Spring拦截器或者Servlet过滤器,是方法,定义切面要做什么,何时使用,有before,after,around..
3. 连接点(Joinpoint): 程序能够使用通知(Advice)的一个时机,即程序执行过程中明确的点,一般是方法的调用,或异常被抛出
4. 切入点(Pointcut): 定义切面发生在哪里,带了通知的连接点,例如某个类或方法的名称,在程序中主要体现为切入点表达式
5. 引入(Introduction): 向现有的类添加新的属性或方法
6. 目标(Target): 被通知的对象
7. 代理(Proxy):AOP框架创建的对象,代理就是目标对象的增强,Advice + Target = Proxy
8. 织入(Weaving):把切面应用到目标对象时,创建新的代理对象的过程
a. 编译时织入:AspectJ,静态AOP,生成的字节码融入了增强后的AOP对象,性能更好
b. 加载时织入: Instrument
c. 运行时织入:Spring AOP动态代理,在内存中临时生成一个AOP对象,并在特定的切点做增强处理,仅支持方法级别的切点
二、Spring实现AOP的方式
1. 经典的基于代理的AOP:通过在xml文件配置进行代理
2. 自动代理的AOP:在配置文件中,切点跟通知自动匹配
3. AOP标签配置到切面
4. @AspectJ注解
三、支持的通知类型:Before、After-returning、After-throwing、Around、Introduction
四、Spring AOP工厂
1. 如果AdvisedSupport通知器没有进行自定义设置,默认使用JDK动态代理
2. 如果AdvisedSupport进行了设置,就判断要代理的类是不是接口,接口使用JDK动态代理,否则使用Cglib动态代理
3. JDK动态代理和Cglib动态代理,都是基于 AdvisedSupport 的 MethodInterceptor 链实现的
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
五、基于JDK动态代理实现的AOP,要去实现InvocationHandler接口,调用invoke()方法
1. Spring会根据AdvisedSupport获取一个方法拦截器MethodInterceptor的链条chain
2. 如果chain为空,直接反射调用原方法
3. 否则,调用proceed()方法,而此方法是一个递归方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
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;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Get the interception chain for this method.
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.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
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.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);
}
}
}
六、基于Cglib动态代理实现的AOP,也是基于AdvisedSupport实现Callback(),进行方法拦截
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
..............
}
七、递归拦截器链
1. 所有拦截器都执行完了,才会调用真正的防范,否则就递归调用拦截器
2. Before拦截器,先执行before再执行proceed,是拦截器链的的最后一环
3. After拦截器,先执行proceed再执行after
八、java.lang.instrument包进行静态代理实现,LTW加载时织入(Load Time Weaving)
1. 在JVM启动时会装配并应用ClassTransformer,对类字节码进行转换,进而实现AOP的功能
2. 使用起来比较麻烦,不推荐使用
九、AspectJ静态代理
参考:
https://www.cnblogs.com/kevin-yuan/p/5571200.html
https://blog.csdn.net/u012707422/article/details/93894174
https://blog.csdn.net/u011983531/article/details/80359304
https://www.jianshu.com/p/012f950206ca
https://blog.csdn.net/u011983531/article/details/49391129
来源:oschina
链接:https://my.oschina.net/u/4339899/blog/3434431