这篇关于spring aop 原理的文章写得简单明了,是不可多得的好文档。但是我发现看得人 挺少的,所以再次转发一下。让这批文章继续 发光发热。 感谢作者。
spring aop介绍 面向切面编程是一种编程思想,解决的场景是将项目中一些零散的,通用的逻辑从原业务逻辑的硬编码调用中脱离出来,并在运行时再动态的编织进去。这就是面向切面技术,如日志,注解等功能就经常采用切面技术。 spring aop是spring的一项核心功能,采用的是动态代理的思想,主要有jdk自身的动态代理和cglib动态代理两种。
说到代理,我们看下代理模式的类图:
进入正题 我们知道了spring aop的实现是基于动态代理的,代理可以在原有目标对象的前后增加一些逻辑以实现需求,那具体是怎样增加的呢?
讲之前我们引入几个概念:
Advice:通知,标识逻辑织入的位置,常见的有BeforeAdvice,AfterAdvice等。 PointCut:切入点,标识对什么方法进入代理,常见的有JdkRegexpMethodPointcut根据正则表达式选取切入点,NameMatchMethodPointcut根据方法名选择切入点。 Advisor:通知器,是通知与切入点的集合。 目前spring aop的切点还只是局限在方法级的粒度,所以通知也就是规定织入的逻辑在方法执行的什么时候执行: 如BeforeAdvice有MethodBeforeAdvice实现:
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* Callback before a given method is invoked.
*/
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
同时Pointcut切点也表示的是在哪个方法上织入逻辑,依赖的接口是MethodMatcher:
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
boolean matches(Method method, Class<?> targetClass, Object... args);
}
主要的就是matches方法,判断该方法是否需要织入逻辑,可以根据正则表达式JdkRegexpMethodPointcut、方法名NameMatchMethodPointcut、注解AnnotationMethodMatcher等方式去实现
示例 下面看一个通过ProxyFactory实现的aop示例:
定义目标切点接口:
/**
* <pre>类名: ITargetImpl</pre>
* <pre>描述: aop目标接口</pre>
* <pre>版权: web_chen@163.com</pre>
* <pre>日期: 2020/11/18 17:28</pre>
* <pre>作者: chenwb</pre>
*/
public interface ITarget {
void say();
}
实现类:
/**
* <pre>类名: TargetImpl</pre>
* <pre>描述: aop目标实现类</pre>
* <pre>版权: web_chen@163.com</pre>
* <pre>日期: 2020/11/18 17:28</pre>
* <pre>作者: chenwb</pre>
*/
public class TargetImpl implements ITarget {
@Override
public void say() {
System.out.println("say");
}
}
定义通知Advice,表示在什么时候做什么事:
import java.lang.reflect.Method;
/**
* <pre>类名: MyBeforeAdvice</pre>
* <pre>描述: 定义一个MyBeforeAdvice通知</pre>
* <pre>版权: web_chen@163.com</pre>
* <pre>日期: 2020/11/18 17:29</pre>
* <pre>作者: chenwb</pre>
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before say");
}
}
/**
* <pre>类名: MyAfterReturningAdvice</pre>
* <pre>描述: 定义一个MyAfterReturningAdvice通知</pre>
* <pre>版权: web_chen@163.com</pre>
* <pre>日期: 2020/11/18 17:29</pre>
* <pre>作者: chenwb</pre>
*/
public class MyAfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("afterReturning say");
}
}
两个通知的定义表明:需要在方法执行之前,和之后打印语句。但现在只是一个通知,并不知道需要在哪个方法的执行前后,也就是pointcut
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
/**
* <pre>类名: AopTest</pre>
* <pre>描述: aop测试</pre>
* <pre>版权: web_chen@163.com</pre>
* <pre>日期: 2020/11/18 17:29</pre>
* <pre>作者: chenwb</pre>
*/
public class AopTest {
public static void main(String[] args) {
// 创建ProxyFactory
ITarget target = new TargetImpl();
ProxyFactory pf = new ProxyFactory(target);
// 新建两个通知,表明在什么时机切入
Advice afterReturningAdvice = new MyAfterReturningAdvice();
Advice beforeAdvice = new MyBeforeAdvice();
// Advisor包含Pointcut和Advice,叫通知器,这里就设置了切点pointcut
RegexpMethodPointcutAdvisor regexpAdvisor1 = new RegexpMethodPointcutAdvisor();
regexpAdvisor1.setPattern("aop.ITargetImpl.say()");
regexpAdvisor1.setAdvice(afterReturningAdvice);
RegexpMethodPointcutAdvisor regexpAdvisor2 = new RegexpMethodPointcutAdvisor();
regexpAdvisor2.setPattern("aop.ITargetImpl.say()");
regexpAdvisor2.setAdvice(beforeAdvice);
// 将通知器Advisor注册到ProxyFactory
pf.addAdvisor(regexpAdvisor1);
pf.addAdvisor(regexpAdvisor2);
// 生成代理,执行方法
ITarget proxy = (ITarget) pf.getProxy();
proxy.say();
}
}
上例中使用了RegexpMethodPointcutAdvisor,它是利用正则表达式匹配需要织入逻辑的方法(也就是切点pointcut),同时将Advice也设置进入。最后将Advisor注册到代理工厂,生成代理。
执行结果:
before say
say
afterReturning say
源码 我们示例代码中使用的ProxyFactory生成代理对象,它是一个生成代理的工厂类,而在spring上下文初始化过程中,创建代理对象使用的其实是ProxyFactoryBean。ProxyFactory可以看作是ProxyFactoryBean的一个子集。接下来我们将代理创建还是按照真实场景,通过ProxyFactoryBean实现: 这个类所在路径是aop包下的org.springframework.aop.framework.ProxyFactoryBean
ProxyFactoryBean是一个工厂bean,它是用来创建bean的,FactoryBean是一个接口,其中主要的就是getObject方法:
public Object getObject() throws BeansException {
// 初始化通知器链,看有哪些通知器
initializeAdvisorChain();
// 如果是单例对象,则创建单例的代理对象
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
// 否则是原型对象
return newPrototypeInstance();
}
}
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
// ...省略部分代码
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
for (String name : this.interceptorNames) {
if (name.endsWith(GLOBAL_SUFFIX)) {
// 如果是全局的通知器配置,获取到整个ioc容器中的通知器
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// 如果是特定名字的通知器配置,则从ioc中获取到该bean,加入到通知器链中
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
advice = this.beanFactory.getBean(name);
}
else {
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
}
上面初始化了项目中的通知器链之后,就要开始创建代理对象了。我们以单例对象为例:
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
// 从ioc获取被代理对象
this.targetSource = freshTargetSource();
// ...省略部分代码
// 生成代理对象并返回
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
那么代理对象时如何生成的呢? 首先是createAopProxy()方法,它是创建一个AopProxy的地方,也就是选择是JDK还是Cglib方式的地方。
protected final synchronized AopProxy createAopProxy() {
//...
return getAopProxyFactory().createAopProxy(this);
}
其中ProxyFactoryBean继承了ProxyCreatorSupport,AopProxyFactory的默认实现是DefaultAopProxyFactory,看一下DefaultAopProxyFactory#createAopProxy:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
// 如果目标对象是实现了接口的,或者已经是代理对象,则使用jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则是cglib生成代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
看完了createAopProxy,我们了解了AopProxy的生成过程,以及如何在jdk方式和cglib方式的选择,在选择完AopProxy之后,我们看下是如何生成代理的,以jdk动态代理为例:
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader);
}
public Object getProxy(@Nullable ClassLoader classLoader) {
// 对代理接口进行分类
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
// 接口是否实现了equals或hashCode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 生成代理
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
我们知道,Jdk动态代理的核心就是InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
实现InvocationHandler 接口的实现类和接口传入到 Proxy.newProxyInstance方法中生成代理对象。执行被代理的接口方法就会执行代理对象的invoke方法,执行各类advisor拦截器,所以我们看一下JdkDynamicAopProxy类的invoke方法:
public 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 {
// 如果执行的方法是equals或hashCode等特殊方法,则执行对应的代理方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 获取目标代理对象和类型
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取针对这个method的所有通知器(关键)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 如果通知器为空,则通过AopUtils.invokeJoinpointUsingReflection直接执行原来的方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 如果通知器链不为空,则执行调用链(关键)
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// 收集方法的返回值,方法被代理了,返回对象也可能被修改
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
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 {
// ...
}
}
以上invoke方法的大致逻辑如下图:
这里主要需要看的是获取拦截器链和递归调用拦截器链:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// 为方法拦截器链创建了缓存,这样只有第一次调用代理方法时需要获取拦截器链,后续可以直接在缓存中取
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
// 获取链后放入缓存中
this.methodCache.put(cacheKey, cached);
}
return cached;
}
// 这里就是从我们一开始初始化的通知器中寻找当前method的拦截器
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
// advisors就是所有通知器的集合
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 通知器是否满足ClassFilter过滤
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
// 是否满足MethodMatcher的matches过滤,如果满足,则将通知器中的拦截器加入了拦截器数组
if (match) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
获取到拦截器链之后就开始递归调用了: 我们看一下ReflectiveMethodInvocation的proceed方法:
public Object proceed() throws Throwable {
// 拦截器执行完了,则执行原方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 匹配拦截器,则执行拦截器逻辑
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
// 否则就透传,递归执行下一个拦截器
else {
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
我们讲了获取拦截器链和执行拦截器链,然而还没串起来,关键问题在于我们还有一点没讲,就是获取拦截器链:
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
这个Registry就是GlobalAdvisorAdapterRegistry,默认实现是DefaultAdvisorAdapterRegistry,它是通知器的适配器注册中心,有哪些通知器都定义这里面:
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
这是他的构造器,可以看到MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter都被注册了进去。 我们选择MethodBeforeAdviceAdapter看下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
AdvisorAdapter定义了两个方法,一个是是否支持这个通知,第二个就是返回前置通知的拦截器, 一不做,二不休,看看前置通知拦截器做了啥:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 这里就是执行通知advice中before方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 执行下一个调用
return mi.proceed();
}
}
而getInterceptors方法:
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
// 如果适配器支持这个通知,则将类似前面说的前置拦截器加入进来,
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
最后多个拦截器链就构成了一个ReflectiveMethodInvocation对象,并执行proceed方法,由此就实现了拦截器链的递归调用。 从实现来看,DefaultAdvisorAdapterRegistry中注册的AdvisorAdapter的顺序是重要的,它决定了拦截器的执行顺序。
小结 1、理解aop的实现思路可以帮助我们理解一些开发问题,如:
aop是通过factorybean实现的,在spring ioc初始化的时候,如果发现一个对象是代理对象,则会为该对象生成代理。 为什么在同类中调用类似@Transactional接口,事务会失效? @Transactional注解新增事务是通过aop实现的,需要调用代理对象的invoke(jdk动态代理)方法,才能享受拦截器新增事务的待遇。而在同类中调用注解方法,是方法硬调用,并没有调用到代理对象,所以事务就不生效了。 2、留给大家和自己一个问题,如果一个类有@Transactional注解,那么ioc中会同时存在原对象和代理对象吗? 如果都存在,那在调用对象方法的时候,ioc是如何选择代理对象,还是原对象的?
欢迎大家留言。分享你的答案。 ———————————————— 版权声明:本文为CSDN博主「c&0xff00」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/weixin_3796…