一个例子

// 定义一个切面
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. HashMap是如何进行扩容的?

    HashMap通过resize()方法进行扩容. 源码解析: resize()函数有两种使用情况: 一.当table数组为null时初始化hash表. 二.当table数组不为null时进行扩容. 1 ...

  2. ADT基础(三)—— HashMap

    ADT基础(三)-- HashMap 1 哈希表 哈希表(hash table),也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维 ...

  3. MySQL注入流程

    目录 确认注入点 信息收集 数据获取 提权 写个MySQL注入流程的大纲,类似一份全局地图,能指导下一步工作.MySQL注入流程分为四步: 确认注入点 信息收集 数据获取 提权 确认注入点 参考:ht ...

  4. 500GJava/Hadoop/Spark/机器学习...视频教程免费分享 百度云持续更新

    参加工作这么长时间了,工作中遇到了不少技能都是看视频教程学习的,相比较看书而言看视频确实比较容易理解.分享一下自己看过的和收集的视频教程. 资源包括: 大数据方面的Hadoop(云帆,小象学院,八斗学 ...

  5. 剑指 Offer 19. 正则表达式匹配 + 动态规划

    剑指 Offer 19. 正则表达式匹配 题目链接 一. 字符串匹配大致可以分为三种情况: 第一种:正则串的最后一个字符为正常字符,此时根据主串的最后一个字符是否和它相同来判断是否匹配, 如果相同,则 ...

  6. SpringMVC-02 第一个SpringMVC程序

    SpringMVC-02 第一个SpringMVC程序 第一个SpringMVC程序 配置版 新建一个Moudle , springmvc-02-hello,确定依赖导入进去了 1.配置web.xml ...

  7. Linux发行版及其目标用户

    1.Debian Debian 众所周知,是Deepin,Ubuntu和Mint等流行Linux发行版的母亲,这些发行版提供了可靠的性能,稳定性和无与伦比的用户体验.最新的稳定发行版是Debian 1 ...

  8. 推荐一款小众且好用的 Python 爬虫库 - RoboBrowser

    1. 前言 大家好,我是安果! 今天推荐一款小众轻量级的爬虫库:RoboBrowser RoboBrowser,Your friendly neighborhood web scraper!由纯 Py ...

  9. 翻译:《实用的Python编程》05_00_Overview

    目录 | 上一节 (4 类和对象) | 下一节 (6 生成器) 5. Python 对象的内部工作原理 本节介绍 Python 对象的内部工作原理.来自其它语言的程序员通常会发现 Python 的类概 ...

  10. 2018.9.9 nowcoder 普及组第一场

    2018.9.9 nowcoder 普及组第一场 C-括号 题目大意:一个只包含左右括号的字符串\(S\),希望删掉S中若干个字符,使得剩下的字符串是一个合法的括号串,有多少不同的方案. Soluti ...