一个例子

// 定义一个切面
package cn.eagle.li.source.aspect; @Component
@Aspect
public class ServiceAspect {
@Pointcut("execution(* cn.eagle.li.source.service.*.*(..))")
public void pointCut() {
} @Before("pointCut()")
public void methodBefore() {
System.out.println("===== Before =====");
} @After("pointCut()")
public void methodAfter() {
System.out.println("===== After =====");
} @AfterReturning("pointCut()")
public void methodReturn() {
System.out.println("===== AfterReturning =====");
} @Around("pointCut()")
public void doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===== Around before =====");
pjp.proceed();
System.out.println("===== Around after =====");
}
}
// 一个接口
package cn.eagle.li.source.service;
public interface IService {
void doService();
} // 一个实现类
package cn.eagle.li.source.service.impl;
@Service
public class ServiceImpl implements IService {
@Override
public void doService() {
System.out.println("do service");
}
}
// Main类
@Configuration
@ComponentScan(basePackages = {"cn.eagle.li.source"})
@EnableAspectJAutoProxy(exposeProxy = true)
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
IService service = context.getBean(IService.class);
service.doService();
}
}

执行结果如下:

===== Around before =====
===== Before =====
do service
===== AfterReturning =====
===== After =====
===== Around after =====

Spring AOP 原理

从@EnableAspectJAutoProxy注解入手

下面是EnableAspectJAutoProxy注解类内容

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//代理的实现方式,true为CGLIB, false为JDK,默认false
boolean proxyTargetClass() default false; // 要不要暴露代理对象
boolean exposeProxy() default false;
}

除了上面两个参数比较重要外,它还有一个Import注解,在Spring中,只要一种注解组合了另一种注解,它就具有该注解的功能,也就是这个注解拥有了@Import注解的功能。

@Import(AspectJAutoProxyRegistrar.class)

我们看到它import了AspectJAutoProxyRegistrar这个类,我们下面再看看这个类的内容,发现它主要的工作就是注册一个name为:

org.springframework.aop.config.internalAutoProxyCreator,类为AnnotationAwareAspectJAutoProxyCreator的BeanDefinition。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// ......
}
}
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
public abstract class AopConfigUtils {
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
}
public abstract class AopConfigUtils {

  	public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator"; @Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册 BeanDefinition
// AUTO_PROXY_CREATOR_BEAN_NAME = org.springframework.aop.config.internalAutoProxyCreator
// beanDefinition = AnnotationAwareAspectJAutoProxyCreator.class
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
}

此时有一个疑问:那就是什么时候调用这个方法来加载AnnotationAwareAspectJAutoProxyCreator这个类的BeanDefinition呢?

从下面这张图可以看到是在容器进行刷新的时候,调用invokeBeanFactoryPostProcessors这个方法来进行执行的,具体的执行过程就不展开了。

	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
// 在这里把beandefinition加载
invokeBeanFactoryPostProcessors(beanFactory);
// ......
}
}

现在这个类的BeanDefinition准备好了,那什么时候会创建AnnotationAwareAspectJAutoProxyCreator这个实例,然后加载到容器里呢?

我们看一下这个类的继承关系,发现它是一个实现了BeanPostProcessor接口,也就是说它是一个后置处理器。在refresh()方法中有一个步骤是专门用来注册后置处理器的,也就是registerBeanPostProcessors()这个方法。

	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
// 在这里把BeanPostProcessor加载到容器中
registerBeanPostProcessors(beanFactory);
// ......
}
}

在调试的过程中,发现在PostProcessorRegistrationDelegate这个类下的registerBeanPostProcessors()这个方法下会创建AnnotationAwareAspectJAutoProxyCreator,并把它注册到容器中,如下图。

PostProcessorRegistrationDelegate类下
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
}

经过上面的一个讨论,我总结了一下它的一些关键触发路径,如下:

refresh() -> invokeBeanFactoryPostProcessors() -> AspectJAutoProxyRegistrar.registerBeanDefinitions()

name:"org.springframework.aop.config.internalAutoProxyCreator" beanDefinition:AnnotationAwareAspectJAutoProxyCreator -> registerBeanPostProcessors() -> 创建类实例并加载到容器中

什么时候会创建代理对象?

容器里有了AnnotationAwareAspectJAutoProxyCreator这个实例,它具体有什么用呢,它什么时候会生成代理对象呢?

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
} Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

大家都知道,Spring在创建一个类实例后,会对这个类进行初始化,然后会执行一系列的后置处理器,就在applyBeanPostProcessorsAfterInitialization()这个方法里面。

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

它会依次地去调用每一个后置处理器,当然也包括了我们刚刚注册的AnnotationAwareAspectJAutoProxyCreator这个后置处理器,具体的执行方法就是postProcessAfterInitialization();

	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

我们来看一下AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization这个方法,发现如果没有包装过,就把它包装一下。

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
return wrapIfNecessary(bean, beanName, cacheKey);

我们看到它会获取一些适配这个类的Advices和Advisors,如果不为null,就创建一个代理对象。

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
} // Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// DO_NOT_PROXY = null;
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
// ......
return proxyFactory.getProxy(targetClassLoader);
}
	public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

Spring AOP有两种代理类,Jdk代理类和Cglib代理类,具体要看proxyTargetClass这个配置项和这个类是否实现了接口。

	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(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);
}
}

我们以JDK动态代理为例,发现它就是用Java的api进行创建的,代理类JdkDynamicAopProxy这个类

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
}

到现在,我们知道,当创建一个bean实例后,在它初始化后,会调用每个后置处理器的初始化后的方法

当调用AOP的后置处理器的时候,会根据有没有适配它的advice和advitor来创建代理类

创建代理类的时候,有两种选择,一是JDK代理类,二是Cglib代理类

如果proxyTargetClass为false或者这个类没有实现接口的话,就选择Cglib代理类,否则选择JDK代理类。

方法执行时怎么实现拦截的?

现在我们获取一个bean时,获取的就是它的代理类,那在调用其方法时,哪些符合的通知是怎么一步步执行的呢?

我们以JDK代理类为例,我们都知道,在JDK动态代理中,实际执行的时候是执行的代理类的invoke方法,所以看一下它的具体内容:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	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 {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else 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; // 如果设置了exposeProxy参数
// 这里暴露了代理对象
// AopContext.currentProxy() 获得
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // 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 != null ? target.getClass() : null); // 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.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation 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 != Object.class && 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);
}
}
}
}

总的看下来,主要关心以下内容:

// 如果设置了exposeProxy参数
// 这里暴露了代理对象
// AopContext.currentProxy() 获得
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 这里是用ThreadLocal实现的
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // 创建拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
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();
}

这个执行链就是一个数组,然后一个一个向下执行,根据之前的例子,这个生成的执行链如下:

AspectJAroundAdvice MethodBeforeAdviceInterceptor AspectJAfterAdvice AspectJAfterReturningAdvice

最终包装成ReflectiveMethodInvocation这个类,调用其proceed()方法执行,如下:

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

	public Object proceed() throws Throwable {
// 拦截器链中的最后一个拦截器执行完
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 执行目标方法
return invokeJoinpoint();
} // 每次执行新的拦截器,下标+1
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
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 {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
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);
}
}
}
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
// 调用通知的方法
return invokeAdviceMethod(pjp, jpm, null, null);
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
public Object invoke(MethodInvocation mi) throws Throwable {
// 调用通知方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 下一个拦截器
return mi.proceed();
}
}
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 下一个拦截器
return mi.proceed();
}
finally {
// 调用通知方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
implements MethodInterceptor, AfterAdvice, Serializable {
public Object invoke(MethodInvocation mi) throws Throwable {
// 下一个拦截器
Object retVal = mi.proceed();
// 调用通知方法
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}

===== Around before =====
===== Before =====
do service
===== AfterReturning =====
===== After =====
===== Around after =====

从上面可以看出,代理类执行的时候,就是将符合的advice排序得到一个数组,然后依次的进行执行。

总结

  1. 从EnableAspectJAutoProxy注解入手,Spring会注册AnnotationAwareAspectJAutoProxyCreator这个类
  2. AnnotationAwareAspectJAutoProxyCreator是个BeanPostProcessor,在初始化后,会调用后置处理器,生成代理对象
  3. 在类执行方法时,会生成一个数组,然后将所有相关的通知一个一个的执行

问题

我看看到上面的顺序是 AspectJAroundAdvice MethodBeforeAdviceInterceptor AspectJAfterAdvice AspectJAfterReturningAdvice

但是好多文章都说是AspectJAfterReturningAdvice AspectJAfterAdvice AspectJAroundAdvice MethodBeforeAdviceInterceptor

不同的执行顺序会对你的程序会有不同的影响

后续将会写一篇文章专门介绍这个原因

参考

Spring AOP

SpringAop源码分析(基于注解) 一

SpringAop源码分析(基于注解) 三

SpringAop源码分析(基于注解)四

Spring AOP执行顺序

按照自己的思路去研究Spring AOP源码【1】的更多相关文章

  1. 按照自己的思路研究Spring AOP源码【2】

    目录 问题的提出 哪一步导致了顺序的改变 AbstractAdvisorAutoProxyCreator.sortAdvisors()方法 总结 问题的提出 上面这篇文章介绍了Spring AOP源码 ...

  2. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  3. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  4. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  5. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  6. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  7. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  8. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  9. 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

随机推荐

  1. css常用居中方式

    一.水平居中 1.内联元素 父级元素加 text-align: center 即可 html <div class="container"> <a>内联元素 ...

  2. gRPC-go源码(2):ClientConn

    摘要 在上一篇文章中,我们聊了聊gRPC是怎么管理一条从Client到Server的连接的. 我们聊到了gRPC拥有Resolver,用来解析地址:拥有Balancer,用来做负载均衡. 在这一篇文章 ...

  3. MySQL注入点与SQL语句的关系

    目录 注入位置分类 内联式 - UNION query SQL injection 终止式 - End SQL injection 堆叠式 - Stacked queries SQL injectio ...

  4. HDOJ-3416(最大流+最短路+ISAP算法+向前星dijikstra算法+如何判断一条边是否在最短路中)

    Marriage Match IV HDOJ-3416 这题的题意就是要找两点之间最短路的路径个数,而且边不能重复. 最大流和最短路的结合.首先正向和反向建图,再跑两遍dijikstra.到这里就求出 ...

  5. 【Azure 微服务】Service Fabric, 使用ARM Template方式来更新SF集群的证书(Renew SF Certificate)

    问题描述 因证书过期导致Service Fabric集群挂掉(升级无法完成,节点不可用)一文中,描述了因为证书过期而导致了SF集群不可用,并且通过命令dd-AzServiceFabricCluster ...

  6. 使用createrepo构建本地yum仓库

    rpm包安装的时候会有很多软件会出现因为其他依赖包没有,而导致安装失败的情况.一般可以连接外网的时候我们直接使用 yum 进行安装,可以为我们解决依赖包关系,但是很多工作环境下是没有外网的,内网情况下 ...

  7. 60秒定位问题,十倍程序员的Debug日常

    作者:陶建辉 这是我在 2020 年 5 月写的一篇内部博客,当时是希望研发和技术支持同学能够帮助用户快速定位 Bug,解决问题.2020 年 12 月我又迭代了一版,并还针对此进行了内部的培训.这段 ...

  8. mybatis&plus系列------Mysql的JSON字段的读取和转换

    mybatis&plus系列------Mysql的JSON字段的读取和转换 一. 背景 在平常的开发中,我们可能会有这样的需求: 业务数据在存储的时候,并不是以mysql中的varchar丶 ...

  9. List的主要实现类

    //ArrayList:List的主要实现类 /* * List中相对于Collection,新增加的方法 * void add(int index, Object ele):在指定的索引位置inde ...

  10. MQ 架构与细节

    MQ 架构与细节 MQ 是什么? MQ:MessageQueue,消息队列的简称,用于进程间通信或同一进程的不同线程间的通信方式. 什么时候该使用MQ? 数据驱动的任务依赖 上游不关心执行结果 上游关 ...