1.Spring AOP中的Joinpoint
AOP的Joinpoint可以有许多种类型,但在Spring AOP中,仅支持方法级别的Joinpoint。更确切的说,只支持方法执行(Method Execution)类型的Joinpoint。但在实际的开发中,这已经满足大部分开发需求了。
2.Spring AOP中的Pointcut
package org.springframework.aop;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.TruePointcut;
public interface Pointcut {
Pointcut TRUE;
//对Joinpoint所处的对象进行Class级别的类型匹配
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
static default {
TRUE = TruePointcut.INSTANCE;
}
}
Spring中以Pointcut接口作为其AOP框架中所有Pointcut的最顶层抽象,该接口定义了2个方法用来帮助捕捉系统中的相应Joinpoint,TrueTruePointcut.INSTANCE默认会对系统中的所有对象以及对象上所有被支持 的Joinpoint进行匹配。
package org.springframework.aop;
import org.springframework.aop.TrueClassFilter;
public interface ClassFilter {
ClassFilter TRUE;
boolean matches(Class<?> clazz);
static default {
TRUE = TrueClassFilter.INSTANCE;
}
}
package org.springframework.aop;
import java.lang.reflect.Method;
import org.springframework.aop.TrueMethodMatcher;
public interface MethodMatcher {
MethodMatcher TRUE;
boolean matches(Method method, Class<?> targetClass);
/**
* 两个重载的matches方法分界线
* true:每次对方法调用的参数进行匹配检查和,DynamicMethodMatcher,
* 当matches(Method method, Class<?> targetClass)为true时,
* matches(Method method, Class<?> targetClass, Object... args)将被执行,以进一步检查条件
* 但如果matches(Method method, Class<?> targetClass)为false时,matches(Method method, Class<?> targetClass, Object... args)将不会被执行
* false:不会考虑具体Joinpoint的方法参数,StaticMethodMatcher,matches(Method method, Class<?> targetClass)将被执行
*/
boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);
static default {
TRUE = TrueMethodMatcher.INSTANCE;
}
扩展Pointcut(Customize Pointcut)
StaticMethodMatcherPointcut,DynamicMethodMatcherPointcut
IoC容器中的Pointcut
Spring容器中的Pointcut实现都是普通的Java对象,所以,可以通过Spring的IoC容器来注册并使用它们。
3.Spring AOP中的Advice
Spring AOP加入了开源组织AOP Alliance,目的在于标准化AOP的使用。
Advice实现了将被织入到Pointcut规定的Joinpoint处的横切逻辑。在Spring中,Advice按照其自身实例能否在目标对象类的所有实例中共享这一标准,可以划分为两大类:即per-class类型的Advice和per-instance类型的Advice。
per-class类型的Advice
最常接触的Advice类型,该类型的Advice实例可以在目标对象类的所以实例间共享,通常只是提供方法拦截的功能,不会为目标对象类保存任何状态和添加新特性。图9-4中除了没有列出的Introduction类型不属于per-class类型Advice外都是。
(1)Before Advice
package org.springframework.aop;
import java.lang.reflect.Method;
import org.springframework.lang.Nullable;
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
(2)ThrowsAdvice
package org.springframework.aop;
public interface ThrowsAdvice extends AfterAdvice { }
对应通常AOP概念中的AfterThrowingAdvice,虽然没有定义任何方法,但在实现时,方法定义需要遵循如下规则:
void afterThrowing([method,args,target],ThrowableSubClass);
通常用于对系统中特定的异常情况进行监控,以统一的方式对所发生的异常进行处理。可以在一种称之为Fault Barrier的模式中使用它。
(3)AfterReturningAdvice
package org.springframework.aop;
import java.lang.reflect.Method;
import org.springframework.lang.Nullable;
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
通过该Advice,可以访问当前Joinpoint的方法返回值,方法,方法参数以及所在的目标对象,但不可以更改返回值。
(4)Around Advice
Spring AOP没有提供After(finally)Advice。
package org.aopalliance.intercept;
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
per-instance类型的Advice
该类型的Advice不会再目标对象类的所有实例间共享,而是会为不同的实例对象保存它们各自的状态以及相关逻辑。在Spring AOP中,Introduction是唯一一种该类型的Advice。
Introduction可以在不改动目标类定义的情况下,为目标类添加新的属性和行为。
package org.springframework.aop;
import org.aopalliance.intercept.MethodInterceptor;
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { }
package org.springframework.aop;
import org.aopalliance.aop.Advice;
public interface DynamicIntroductionAdvice extends Advice {
boolean implementsInterface(Class<?> intf);
}
4.Spring AOP中的Aspect
Spring中的Aspect代表是Advisor,与正常的Aspect不同,Advisor通常只持有一个Pointcut和一个Advice,而理论上,Aspect定义中可以有多个Pointcut和多个Advice。可以认为Advisor是一种特殊的Aspect。
PointcutAdvisor家族
IntroductionAdvisor分支
与PointcutAdvisor最本质上的区别就是,IntroductionAdvisor只能应用于类级别的拦截,只能使用Introduction类型的Advice,纯粹为Introduction而生。
Ordered的作用
指定Advice的优先级
5.Spring AOP的织入
org.springframework.aop.framework.ProxyFactory
是JDK动态代理还是CGLIB子类代理?
如果目标类没有实现任何接口,ProxyFactory采用基于类的代理;
如果ProxyFactory的proxyTargetClass或者optimize属性值设为true,采用基于类的代理;
排除上面的情况,则采用基于接口的代理。
Introduction织入
Introduction型Advice较特殊,
(1)Introduction可以为已经存在的对象类型添加新的行为,只能应用于对象级别的拦截,而不是通常Advice的方法级别的拦截,所以进行Introduction的织入过程中,不需要指定Pointcut,而只需要指定目标接口类型;
(2)Spring的Introduction支持只能通过接口定义为当前对象添加新的行为,所以我们需要在织入的时机,指定新织入的接口类型;
要织入Introduction,只能使用IntroductionAdvisor或者其子类,而不能使用其他的组合。
ProxyFactory的本质
package org.springframework.aop.framework;
import org.springframework.lang.Nullable;
public interface AopProxy {
Object getProxy();
Object getProxy(@Nullable ClassLoader classLoader);
}
不同AopProxy实现的实例化过程采用的是抽象工厂模式进行封装,即通过AopProxyFactory进行.
package org.springframework.aop.framework;
public interface AopProxyFactory {
AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}
目前,具体的实现就一个:
package org.springframework.aop.framework;
import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@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() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
AdvisedSupport:生成代理对象所需的信息载体.
容器中的织如器----ProxyFactoryBean
Proxy + FactoryBean,本质上是一个用来生产Proxy的FactoryBean.与FactoryBean类似,如果容器中的某个对象依赖于ProxyFactoryBean,那么他将会使用到ProxyFactoryBean的getObject()方法所返回的代理对象.
public interface ITask { }
public interface ICounter {
void resetCounter();
int getCounter();
}
public class TaskImpl implements ITask { }
public class CounterImpl implements ICounter{
private int counter;
@Override
public void resetCounter() {
counter = 0;
}
@Override
public int getCounter() {
counter++;
return counter;
}
}
<!--通过IntroductionAdvice将ICounter的行为添加到ITask的相应实现类中-->
<bean id="taskImpl" class="com.hong.service.TaskImpl" scope="prototype"/>
<bean id="introducedTask" class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
<property name="targetName" value="taskImpl"/>
<property name="proxyInterfaces">
<list>
<value>com.hong.service.ITask</value>
<value>com.hong.service.ICounter</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>introductionInterceptor</value>
</list>
</property>
</bean>
<bean id="introductionInterceptor" class="org.springframework.aop.support.DelegatingIntroductionInterceptor" scope="prototype">
<constructor-arg>
<bean class="com.hong.service.CounterImpl"/>
</constructor-arg>
</bean>
@Test
public void test2(){
Object proxy1 = container.getBean("introducedTask");
Object proxy2 = container.getBean("introducedTask");
//在xml中将目标对象的bean定义,ProxyFactoryBean的bean定义,以及相应
//IntroductionInterceptor的bean定义的scope,全部声明为prototype
//且使用targetName而不是target来指定目标对象,这样才能保证每次取得的
//代理对象都持有各自独有的状态和行为
System.out.println(((ICounter)proxy1).getCounter());//1
System.out.println(((ICounter)proxy1).getCounter());//2
System.out.println(((ICounter)proxy2).getCounter());//1
}
}
如果将DelegatingIntroductionInterceptor替换为使用DelegatePerTargetObjectIntroductionInterceptor,则只需要改动如下一点:
<bean id="introductionInterceptor" class="org.springframework.aop.support.DelegatePerTargetObjectIntroductionInterceptor">
<constructor-arg index="0">
<value>com.hong.service.CounterImpl</value>
</constructor-arg>
<constructor-arg index="1">
<value>com.hong.service.ICounter</value>
</constructor-arg>
</bean>
加快织入的自动化进程
Spring AOP的自动代理(AutoProxy)机制,依托于ApplicationContext类型的IoC容器.
自动代理的实现建立在IOC容器的BeanPostProcessor概念上,通过BeanPostProcessor可以干预Bean的实例化过程.
自动代理实现原理的伪代码:
for (bean in IoC Container){
//检查当前bean定义是否符合拦截条件
if (true) {
Object proxy = CreateProxyForBean(bean);
return proxy;
}else {
Object instance = createInstance(bean);
return instance;
}
}
可用的AutoProxyCreator
Spring AOP在org.springframework.aop.framework.autoproxy包中提供了两个常用的AutoProxyCreator:
BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator.
6.TargetSource
TargetSource的作用就好像是为目标对象在外面加了一个壳.
可用的TargetSource实现类
(1)SingletonTargetSource
(2)PrototypeTargetSource
(3)HotSwappableTargetSource
对双数据源互换实现的改进(improve to 2-dataSource swapping)
https://afoo.me/posts/2005-08-10-improve-datasources-swap-solution.html
(3)CommonsPool2TargetSource
(4)ThreadLocalTargetSource