文章目录

  • 前言
  • 正文
    • 基本概念
    • 代理对象的创建
    • 小结
    • AOP链式调用
    • AOP扩展知识
      • 一、自定义全局拦截器Interceptor
      • 二、循环依赖三级缓存存在的必要性
      • 三、如何在Bean创建之前提前创建代理对象
  • 总结

前言

AOP,也就是面向切面编程,它可以将公共的代码抽离出来,动态的织入到目标类、目标方法中,大大提高我们编程的效率,也使程序变得更加优雅。如事务、操作日志等都可以使用AOP实现。这种织入可以是在运行期动态生成代理对象实现,也可以在编译期类加载时期静态织入到代码中。而Spring正是通过第一种方法实现,且在代理类的生成上也有两种方式:JDK Proxy和CGLIB,默认当类实现了接口时使用前者,否则使用后者;另外Spring AOP只能实现对方法的增强。

正文

基本概念

AOP的术语很多,虽然不清楚术语我们也能很熟练地使用AOP,但是要理解分析源码,术语就需要深刻体会其含义。

  • 增强(Advice):就是我们想要额外增加的功能
  • 目标对象(Target):就是我们想要增强的目标类,如果没有AOP,我们需要在每个目标对象中实现日志、事务管理等非业务逻辑
  • 连接点(JoinPoint):程序执行时的特定时机,如方法执行前、后以及抛出异常后等等。
  • 切点(Pointcut):连接点的导航,我们如何找到目标对象呢?切点的作用就在于此,在Spring中就是匹配表达式。
  • 引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
  • 织入(Weaving):即如何将增强添加到目标对象的连接点上,有动态(运行期生成代理)、静态(编译期、类加载时期)两种方式。
  • 代理(Proxy):目标对象被织入增强后,就会产生一个代理对象,该对象可能是和原对象实现了同样的一个接口(JDK),也可能是原对象的子类(CGLIB)。
  • 切面(Aspect、Advisor):切面由切点和增强组成,包含了这两者的定义。

代理对象的创建

在熟悉了AOP术语后,下面就来看看Spring是如何创建代理对象的,是否还记得上一篇提到的AOP的入口呢?在AbstractAutowireCapableBeanFactory类的applyBeanPostProcessorsAfterInitialization方法中循环调用了BeanPostProcessorpostProcessAfterInitialization方法,其中一个就是我们创建代理对象的入口。这里是Bean实例化完成去创建代理对象,理所当然应该这样,但实际上在Bean实例化之前调用了一个resolveBeforeInstantiation方法,这里实际上我们也是有机会可以提前创建代理对象的,这里放到最后来分析,先来看主入口,进入到AbstractAutoProxyCreator类中:

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {//创建当前bean的代理,如果这个bean有advice的话,重点看// Create proxy if we have advice.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);//如果有切面,则生成该bean的代理if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//把被代理对象bean实例封装到SingletonTargetSource对象中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;}

先从缓存中拿,没有则调用wrapIfNecessary方法创建。在这个方法里面主要看两个地方:getAdvicesAndAdvisorsForBeancreateProxy。简单一句话概括就是先扫描后创建,问题是扫描什么呢?你可以先结合上面的概念思考下,换你会怎么做。进入到子类AbstractAdvisorAutoProxyCreatorgetAdvicesAndAdvisorsForBean方法中:

	protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {//找到合格的切面List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();}protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//找到候选的切面,其实就是一个寻找有@Aspectj注解的过程,把工程中所有有这个注解的类封装成Advisor返回List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断候选的切面是否作用在当前beanClass上面,就是一个匹配过程。现在就是一个匹配List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {//对有@Order@Priority进行排序eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

findEligibleAdvisors方法中可以看到有两个步骤,第一先找到所有的切面,即扫描所有带有@Aspect注解的类,并将其中的切点(表达式)增强封装为切面,扫描完成后,自然是要判断哪些切面能够连接到当前Bean实例上。下面一步步来分析,首先是扫描过程,进入到AnnotationAwareAspectJAutoProxyCreator类中:

	protected List<Advisor> findCandidateAdvisors() {// 先通过父类AbstractAdvisorAutoProxyCreator扫描,这里不重要List<Advisor> advisors = super.findCandidateAdvisors();// 主要看这里if (this.aspectJAdvisorsBuilder != null) {advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}return advisors;}

这里委托给了BeanFactoryAspectJAdvisorsBuilderAdapter类,并调用其父类的buildAspectJAdvisors方法创建切面对象:

	public List<Advisor> buildAspectJAdvisors() {List<String> aspectNames = this.aspectBeanNames;if (aspectNames == null) {synchronized (this) {aspectNames = this.aspectBeanNames;if (aspectNames == null) {List<Advisor> advisors = new ArrayList<>();aspectNames = new ArrayList<>();//获取spring容器中的所有bean的名称BeanNameString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);for (String beanName : beanNames) {if (!isEligibleBean(beanName)) {continue;}Class<?> beanType = this.beanFactory.getType(beanName);if (beanType == null) {continue;}//判断类上是否有@Aspect注解if (this.advisorFactory.isAspect(beanType)) {aspectNames.add(beanName);AspectMetadata amd = new AspectMetadata(beanType, beanName);if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {// 当@Aspect的value属性为""时才会进入到这里// 创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);//创建切面advisor对象List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);}else {this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);}else {MetadataAwareAspectInstanceFactory factory =new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}this.aspectBeanNames = aspectNames;return advisors;}}}return advisors;}

这个方法里面首先从IOC中拿到所有Bean的名称,并循环判断该类上是否带有@Aspect注解,如果有则将BeanName和Bean的Class类型封装到BeanFactoryAspectInstanceFactory中,并调用ReflectiveAspectJAdvisorFactory.getAdvisors创建切面对象:

	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {//从工厂中获取有@Aspect注解的类ClassClass<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//从工厂中获取有@Aspect注解的类的名称String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();validate(aspectClass);// 创建工厂的装饰类,获取实例只会获取一次MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new ArrayList<>();//这里循环没有@Pointcut注解的方法for (Method method : getAdvisorMethods(aspectClass)) {//非常重要重点看看Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}//判断属性上是否有引介注解,这里可以不看for (Field field : aspectClass.getDeclaredFields()) {//判断属性上是否有DeclareParents注解,如果有返回切面Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors;}private List<Method> getAdvisorMethods(Class<?> aspectClass) {final List<Method> methods = new ArrayList<>();ReflectionUtils.doWithMethods(aspectClass, method -> {// Exclude pointcutsif (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {methods.add(method);}});methods.sort(METHOD_COMPARATOR);return methods;}

根据Aspect的Class拿到所有不带@Pointcut注解的方法对象(为什么是不带@Pointcut注解的方法?仔细想想不难理解),另外要注意这里对method进行了排序,看看这个METHOD_COMPARATOR比较器:

	private static final Comparator<Method> METHOD_COMPARATOR;static {Comparator<Method> adviceKindComparator = new ConvertingComparator<>(new InstanceComparator<>(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),(Converter<Method, Annotation>) method -> {AspectJAnnotation<?> annotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);return (annotation != null ? annotation.getAnnotation() : null);});Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);}

关注InstanceComparator构造函数参数,记住它们的顺序,这就是AOP链式调用中同一个@Aspect类中Advice的执行顺序。接着往下看,在getAdvisors方法中循环获取到的methods,分别调用getAdvisor方法,也就是根据方法逐个去创建切面:

	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());//获取pointCut对象,最重要的是从注解中获取表达式AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}//创建Advisor切面类,这才是真正的切面类,一个切面类里面肯定要有1、pointCut 2、advice//这里pointCut是expressionPointcut, advice 增强方法是 candidateAdviceMethodreturn new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {//从候选的增强方法里面 candidateAdviceMethod  找有有注解//Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class//并把注解信息封装成AspectJAnnotation对象AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}//创建一个PointCut类,并且把前面从注解里面解析的表达式设置进去AspectJExpressionPointcut ajexp =new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if (this.beanFactory != null) {ajexp.setBeanFactory(this.beanFactory);}return ajexp;}

之前就说过切面的定义,是切点和增强的组合,所以这里首先通过getPointcut获取到注解对象,然后new了一个Pointcut对象,并将表达式设置进去。然后在getAdvisor方法中最后new了一个InstantiationModelAwarePointcutAdvisorImpl对象:

	public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {this.declaredPointcut = declaredPointcut;this.declaringClass = aspectJAdviceMethod.getDeclaringClass();this.methodName = aspectJAdviceMethod.getName();this.parameterTypes = aspectJAdviceMethod.getParameterTypes();this.aspectJAdviceMethod = aspectJAdviceMethod;this.aspectJAdvisorFactory = aspectJAdvisorFactory;this.aspectInstanceFactory = aspectInstanceFactory;this.declarationOrder = declarationOrder;this.aspectName = aspectName;if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {// Static part of the pointcut is a lazy type.Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.// If it's not a dynamic pointcut, it may be optimized out// by the Spring AOP infrastructure after the first evaluation.this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);this.lazy = true;}else {// A singleton aspect.this.pointcut = this.declaredPointcut;this.lazy = false;//这个方法重点看看,创建advice对象this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);}}

这个就是我们的切面类,在其构造方法的最后通过instantiateAdvice创建了Advice对象。注意这里传进来的declarationOrder参数,它就是循环method时的序号,其作用就是赋值给这里的declarationOrder属性以及Advice的declarationOrder属性,在后面排序时就会通过这个序号来比较,因此Advice的执行顺序是固定的,至于为什么要固定,后面分析完AOP链式调用过程自然就明白了。

	public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {//获取有@Aspect注解的类Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();validate(candidateAspectClass);//找到candidateAdviceMethod方法上面的注解,并且包装成AspectJAnnotation对象,这个对象中就有注解类型AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}AbstractAspectJAdvice springAdvice;//根据不同的注解类型创建不同的advice类实例switch (aspectJAnnotation.getAnnotationType()) {case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;case AtAround://实现了MethodInterceptor接口springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtBefore://实现了MethodBeforeAdvice接口,没有实现MethodInterceptor接口springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter://实现了MethodInterceptor接口springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning://实现了AfterReturningAdvice接口,没有实现MethodInterceptor接口springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing://实现了MethodInterceptor接口springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;default:throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);}// Now to configure the advice...springAdvice.setAspectName(aspectName);springAdvice.setDeclarationOrder(declarationOrder);String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);if (argNames != null) {springAdvice.setArgumentNamesFromStringArray(argNames);}//计算argNames和类型的对应关系springAdvice.calculateArgumentBindings();return springAdvice;}

这里逻辑很清晰,就是拿到方法上的注解类型,根据类型创建不同的增强Advice对象:AspectJAroundAdvice、AspectJMethodBeforeAdvice、AspectJAfterAdvice、AspectJAfterReturningAdvice、AspectJAfterThrowingAdvice。完成之后通过calculateArgumentBindings方法进行参数绑定,感兴趣的可自行研究。这里主要看看几个Advice的继承体系:在这里插入图片描述
可以看到有两个Advice是没有实现MethodInterceptor接口的:AspectJMethodBeforeAdvice和AspectJAfterReturningAdvice。而MethodInterceptor有一个invoke方法,这个方法就是链式调用的核心方法,但那两个没有实现该方法的Advice怎么处理呢?稍后会分析。
到这里切面对象就创建完成了,接下来就是判断当前创建的Bean实例是否和这些切面匹配以及对切面排序。匹配过程比较复杂,对理解主流程也没什么帮助,所以这里就不展开分析,感兴趣的自行分析(AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply())。下面看看排序的过程,回到AbstractAdvisorAutoProxyCreator.findEligibleAdvisors方法:

	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//找到候选的切面,其实就是一个寻找有@Aspectj注解的过程,把工程中所有有这个注解的类封装成Advisor返回List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断候选的切面是否作用在当前beanClass上面,就是一个匹配过程。。现在就是一个匹配List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {//对有@Order@Priority进行排序eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

sortAdvisors方法就是排序,但这个方法有两个实现:当前类AbstractAdvisorAutoProxyCreator和子类AspectJAwareAdvisorAutoProxyCreator,应该走哪个呢?
在这里插入图片描述
通过类图我们可以肯定是进入的AspectJAwareAdvisorAutoProxyCreator类,因为AnnotationAwareAspectJAutoProxyCreator的父类是它。

	protected List<Advisor> sortAdvisors(List<Advisor> advisors) {List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());for (Advisor element : advisors) {partiallyComparableAdvisors.add(new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));}List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);if (sorted != null) {List<Advisor> result = new ArrayList<>(advisors.size());for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {result.add(pcAdvisor.getAdvisor());}return result;}else {return super.sortAdvisors(advisors);}}

这里排序主要是委托给PartialOrder进行的,而在此之前将所有的切面都封装成了PartiallyComparableAdvisorHolder对象,注意传入的DEFAULT_PRECEDENCE_COMPARATOR参数,这个就是比较器对象:

	private static final Comparator<Advisor> DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator();

所以我们直接看这个比较器的compare方法:

	public int compare(Advisor o1, Advisor o2) {int advisorPrecedence = this.advisorComparator.compare(o1, o2);if (advisorPrecedence == SAME_PRECEDENCE && declaredInSameAspect(o1, o2)) {advisorPrecedence = comparePrecedenceWithinAspect(o1, o2);}return advisorPrecedence;}private final Comparator<? super Advisor> advisorComparator;public AspectJPrecedenceComparator() {this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE;}

第一步先通过AnnotationAwareOrderComparator去比较,点进去看可以发现是对实现了PriorityOrderedOrdered接口以及标记了PriorityOrder注解的非同一个@Aspect类中的切面进行排序。这个和之前分析BeanFacotryPostProcessor类是一样的原理。而对同一个@Aspect类中的切面排序主要是comparePrecedenceWithinAspect方法:

	private int comparePrecedenceWithinAspect(Advisor advisor1, Advisor advisor2) {boolean oneOrOtherIsAfterAdvice =(AspectJAopUtils.isAfterAdvice(advisor1) || AspectJAopUtils.isAfterAdvice(advisor2));int adviceDeclarationOrderDelta = getAspectDeclarationOrder(advisor1) - getAspectDeclarationOrder(advisor2);if (oneOrOtherIsAfterAdvice) {// the advice declared last has higher precedenceif (adviceDeclarationOrderDelta < 0) {// advice1 was declared before advice2// so advice1 has lower precedencereturn LOWER_PRECEDENCE;}else if (adviceDeclarationOrderDelta == 0) {return SAME_PRECEDENCE;}else {return HIGHER_PRECEDENCE;}}else {// the advice declared first has higher precedenceif (adviceDeclarationOrderDelta < 0) {// advice1 was declared before advice2// so advice1 has higher precedencereturn HIGHER_PRECEDENCE;}else if (adviceDeclarationOrderDelta == 0) {return SAME_PRECEDENCE;}else {return LOWER_PRECEDENCE;}}}private int getAspectDeclarationOrder(Advisor anAdvisor) {AspectJPrecedenceInformation precedenceInfo =AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor);if (precedenceInfo != null) {return precedenceInfo.getDeclarationOrder();}else {return 0;}}

这里就是通过precedenceInfo.getDeclarationOrder拿到在创建InstantiationModelAwarePointcutAdvisorImpl对象时设置的declarationOrder属性,这就验证了之前的说法。
当上面的一切都进行完成后,就该创建代理对象了,回到AbstractAutoProxyCreator.wrapIfNecessary,看关键部分代码:

	//如果有切面,则生成该bean的代理if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//把被代理对象bean实例封装到SingletonTargetSource对象中Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}

注意这里将被代理对象封装成了一个SingletonTargetSource对象,它是TargetSource的实现类。

	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}//创建代理工厂ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {//proxyTargetClass 是否对类进行代理,而不是对接口进行代理,设置为true时,使用CGLib代理。proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}//把advice类型的增强包装成advisor切面Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);////用来控制代理工厂被配置后,是否还允许修改代理的配置,默认为falseproxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}//获取代理实例return proxyFactory.getProxy(getProxyClassLoader());}

这里通过ProxyFactory对象去创建代理实例,这是工厂模式的体现,但在创建代理对象之前还有几个准备动作:需要判断是JDK代理还是CGLIB代理以及通过buildAdvisors方法将扩展的Advice封装成Advisor切面。准备完成则通过getProxy创建代理对象:

	public Object getProxy(@Nullable ClassLoader classLoader) {//根据目标对象是否有接口来判断采用什么代理方式,cglib代理还是jdk动态代理return createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);}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);}}

首先通过配置拿到对应的代理类:ObjenesisCglibAopProxy和JdkDynamicAopProxy,然后再通过getProxy创建Bean的代理,这里以JdkDynamicAopProxy为例:

	public Object getProxy(@Nullable ClassLoader classLoader) {//advised是代理工厂对象Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}

这里的代码你应该不陌生了,就是JDK的原生API,newProxyInstance方法传入的InvocationHandler对象是this,因此,最终AOP代理的调用就是从该类中的invoke方法开始。至此,代理对象的创建就完成了,下面来看下整个过程的时序图:
在这里插入图片描述

小结

代理对象的创建过程整体来说并不复杂,首先找到所有带有@Aspect注解的类,并获取其中没有@Pointcut注解的方法,循环创建切面,而创建切面需要切点增强两个元素,其中切点可简单理解为我们写的表达式,增强则是根据@Before、@Around、@After等注解创建的对应的Advice类。切面创建好后则需要循环判断哪些切面能对当前的Bean实例的方法进行增强并排序,最后通过ProxyFactory创建代理对象。

AOP链式调用

熟悉JDK动态代理的都知道通过代理对象调用方法时,会进入到InvocationHandler对象的invoke方法,所以我们直接从JdkDynamicAopProxy的这个方法开始:

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;//从代理工厂中拿到TargetSource对象,该对象包装了被代理实例beanTargetSource targetSource = this.advised.targetSource;Object target = null;try {//被代理对象的equals方法和hashCode方法是不能被代理的,不会走切面.......Object retVal;// 可以从当前线程中拿到代理对象if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}//这个target就是被代理实例target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);//从代理工厂中拿过滤器链 Object是一个MethodInterceptor类型的对象,其实就是一个advice对象List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用if (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, 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())) {retVal = proxy;}return retVal;}finally {if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}

这段代码比较长,我删掉了不关键的地方。首先来看this.advised.exposeProxy这个属性,这在@EnableAspectJAutoProxy注解中可以配置,当为true时,会将该代理对象设置到当前线程的ThreadLocal对象中,这样就可以通过AopContext.currentProxy拿到代理对象。这个有什么用呢?我相信有经验的Java开发都遇到过这样一个BUG,在Service实现类中调用本类中的另一个方法时,事务不会生效,这是因为直接通过this调用就不会调用到代理对象的方法,而是原对象的,所以事务切面就没有生效。因此这种情况下就可以从当前线程的ThreadLocal对象拿到代理对象,不过实际上直接使用@Autowired注入自己本身也可以拿到代理对象。
接下来就是通过getInterceptorsAndDynamicInterceptionAdvice拿到执行链,看看具体做了哪些事情:

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();//从代理工厂中获得该被代理类的所有切面advisor,config就是代理工厂对象Advisor[] advisors = config.getAdvisors();List<Object> interceptorList = new ArrayList<>(advisors.length);Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());Boolean hasIntroductions = null;for (Advisor advisor : advisors) {//大部分走这里if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;//如果切面的pointCut和被代理对象是匹配的,说明是切面要拦截的对象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 {//接下来判断方法是否是切面pointcut需要拦截的方法match = mm.matches(method, actualClass);}//如果类和方法都匹配if (match) {//获取到切面advisor中的advice,并且包装成MethodInterceptor类型的对象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;}

这也是个长方法,看关键的部分,因为之前我们创建的基本上都是InstantiationModelAwarePointcutAdvisorImpl对象,该类是PointcutAdvisor的实现类,所以会进入第一个if判断里,这里首先进行匹配,看切点当前对象以及该对象的哪些方法匹配,如果能匹配上,则调用getInterceptors获取执行链:

	private final List<AdvisorAdapter> adapters = new ArrayList<>(3);public DefaultAdvisorAdapterRegistry() {registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());}public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();//如果是MethodInterceptor类型的,如:AspectJAroundAdvice//AspectJAfterAdvice//AspectJAfterThrowingAdviceif (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}//处理 AspectJMethodBeforeAdvice  AspectJAfterReturningAdvicefor (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]);}

这里我们可以看到如果是MethodInterceptor的实现类,则直接添加到链中,如果不是,则需要通过适配器去包装后添加,刚好这里有MethodBeforeAdviceAdapterAfterReturningAdviceAdapter两个适配器对应上文两个没有实现MethodInterceptor接口的类。最后将Interceptors返回。

if (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
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();
}

返回到invoke方法后,如果执行链为空,说明该方法不需要被增强,所以直接反射调用原对象的方法(注意传入的是TargetSource封装的被代理对象);反之,则通过ReflectiveMethodInvocation类进行链式调用,关键方法就是proceed

	private int currentInterceptorIndex = -1;public Object proceed() throws Throwable {//如果执行链中的advice全部执行完,则直接调用joinPoint方法,就是被代理方法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 {//调用MethodInterceptor中的invoke方法return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

这个方法的核心就在两个地方:invokeJoinpointinterceptorOrInterceptionAdvice.invoke(this)。当增强方法调用完后就会通过前者调用到被代理的方法,否则则是依次调用Interceptorinvoke方法。下面就分别看看每个Interceptor是怎么实现的。

  • AspectJAroundAdvice
	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);}
  • MethodBeforeAdviceInterceptor -> AspectJMethodBeforeAdvice
	public Object invoke(MethodInvocation mi) throws Throwable {this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());return mi.proceed();}public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {invokeAdviceMethod(getJoinPointMatch(), null, null);}
  • AspectJAfterAdvice
	public Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}finally {invokeAdviceMethod(getJoinPointMatch(), null, null);}}
  • AfterReturningAdviceInterceptor -> AspectJAfterReturningAdvice
	public Object invoke(MethodInvocation mi) throws Throwable {Object retVal = mi.proceed();this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());return retVal;}public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {if (shouldInvokeOnReturnValueOf(method, returnValue)) {invokeAdviceMethod(getJoinPointMatch(), returnValue, null);}}
  • AspectJAfterThrowingAdvice
	public Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}catch (Throwable ex) {if (shouldInvokeOnThrowing(ex)) {invokeAdviceMethod(getJoinPointMatch(), null, ex);}throw ex;}}

这里的调用顺序就是前面创建切面时注解定义的顺序,其核心就是通过proceed方法控制流程,每执行完一个Advice就会回到proceed方法中调用下一个Advice。光看代码可能会比较绕,我画了一张图帮助理解:
在这里插入图片描述
以上就是AOP的链式调用过程,你可以思考下如果有多个@Aspect类,这个调用过程又是怎样的?其核心思想和“栈”差不多,就是“先进后出,后进先出”。

AOP扩展知识

一、自定义全局拦截器Interceptor

在上文创建代理对象的时候有这样一个方法:

	protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {//自定义MethodInterceptor.拿到setInterceptorNames方法注入的Interceptor对象Advisor[] commonInterceptors = resolveInterceptorNames();List<Object> allInterceptors = new ArrayList<>();if (specificInterceptors != null) {allInterceptors.addAll(Arrays.asList(specificInterceptors));if (commonInterceptors.length > 0) {if (this.applyCommonInterceptorsFirst) {allInterceptors.addAll(0, Arrays.asList(commonInterceptors));}else {allInterceptors.addAll(Arrays.asList(commonInterceptors));}}}Advisor[] advisors = new Advisor[allInterceptors.size()];for (int i = 0; i < allInterceptors.size(); i++) {//对自定义的advice要进行包装,把advice包装成advisor对象,切面对象advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));}return advisors;}

这个方法的作用就在于我们可以扩展我们自己的Interceptor,首先通过resolveInterceptorNames方法获取到通过setInterceptorNames方法设置的Interceptor,然后调用DefaultAdvisorAdapterRegistry.wrap方法将其包装为DefaultPointcutAdvisor对象并返回:

	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {if (adviceObject instanceof Advisor) {return (Advisor) adviceObject;}if (!(adviceObject instanceof Advice)) {throw new UnknownAdviceTypeException(adviceObject);}Advice advice = (Advice) adviceObject;if (advice instanceof MethodInterceptor) {return new DefaultPointcutAdvisor(advice);}for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {return new DefaultPointcutAdvisor(advice);}}throw new UnknownAdviceTypeException(advice);}public DefaultPointcutAdvisor(Advice advice) {this(Pointcut.TRUE, advice);}

需要注意DefaultPointcutAdvisor构造器里面传入了一个Pointcut.TRUE,表示这种扩展的Interceptor是全局的拦截器。下面来看看如何使用:

public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("自定义拦截器");return invocation.proceed();}
}

首先写一个类实现MethodInterceptor 接口,在invoke方法中实现我们的拦截逻辑,然后通过下面的方式测试,只要UserService 有AOP拦截就会发现自定义的MyMethodInterceptor也生效了。

    public void costomInterceptorTest() {AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);bean.setInterceptorNames("myMethodInterceptor ");UserService userService = applicationContext.getBean(UserService.class);userService.queryUser("dark");}

但是如果换个顺序,像下面这样:

    public void costomInterceptorTest() {UserService userService = applicationContext.getBean(UserService.class);AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);bean.setInterceptorNames("myMethodInterceptor ");userService.queryUser("dark");}

这时自定义的全局拦截器就没有作用了,这是为什么呢?因为当执行getBean的时候,如果有切面匹配就会通过ProxyFactory去创建代理对象,注意Interceptor是存到这个Factory对象中的,而这个对象和代理对象是一一对应的,因此调用getBean时,还没有myMethodInterceptor这个对象,自定义拦截器就没有效果了,也就是说要想自定义拦截器生效,就必须在代理对象生成之前注册进去。

二、循环依赖三级缓存存在的必要性

在上一篇文章我分析了Spring是如何通过三级缓存来解决循环依赖的问题的,但你是否考虑过第三级缓存为什么要存在?我直接将bean存到二级不就行了么,为什么还要存一个ObjectFactory对象到第三级缓存中?这个在学习了AOP之后就很清楚了,因为我们在@Autowired对象时,想要注入的不一定是Bean本身,而是想要注入一个修改过后的对象,如代理对象。在AbstractAutowireCapableBeanFactory.getEarlyBeanReference方法中循环调用了SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference方法,AbstractAutoProxyCreator对象就实现了该方法:

	public Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {this.earlyProxyReferences.add(cacheKey);}// 创建代理对象return wrapIfNecessary(bean, beanName, cacheKey);}

因此,当我们想要对循坏依赖的Bean做出修改时,就可以像AOP这样做。

三、如何在Bean创建之前提前创建代理对象

Spring的代理对象基本上都是在Bean实例化完成之后创建的,但在文章开始我就说过,Spring也提供了一个机会在创建Bean对象之前就创建代理对象,在AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation方法中:

	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;}

主要是InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation方法中,这里又会进入到AbstractAutoProxyCreator类中:

	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {// We can't create fancy target sources for directly registered singletons.if (this.customTargetSourceCreators != null &&this.beanFactory != null && this.beanFactory.containsBean(beanName)) {for (TargetSourceCreator tsc : this.customTargetSourceCreators) {TargetSource ts = tsc.getTargetSource(beanClass, beanName);if (ts != null) {return ts;}}}// No custom TargetSource found.return null;}

看到这里大致应该明白了,先是获取到一个自定义的TargetSource对象,然后创建代理对象,所以我们首先需要自己实现一个TargetSource类,这里直接继承一个抽象类,getTarget方法则返回原始对象:

public class MyTargetSource extends AbstractBeanFactoryBasedTargetSource {@Overridepublic Object getTarget() throws Exception {return getBeanFactory().getBean(getTargetBeanName());}
}

但这还不够,上面首先判断了customTargetSourceCreators!=null,而这个属性是个数组,可以通过下面这个方法设置进来:

	public void setCustomTargetSourceCreators(TargetSourceCreator... targetSourceCreators) {this.customTargetSourceCreators = targetSourceCreators;}

所以我们还要实现一个TargetSourceCreator类,同样继承一个抽象类实现,并只对userServiceImpl对象进行拦截:

public class MyTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {@Overrideprotected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {if(beanName.equalsIgnoreCase("userServiceImpl")) {return new MyTargetSource();}}return null;}
}

createBeanFactoryBasedTargetSource方法是在AbstractBeanFactoryBasedTargetSourceCreator.getTargetSource中调用的,而getTargetSource就是在上面getCustomTargetSource中调用的。以上工作做完后,还需要将其设置到AnnotationAwareAspectJAutoProxyCreator对象中,因此需要我们注入这个对象:

@Configuration
public class TargetSourceCreatorBean {@Autowiredprivate BeanFactory beanFactory;@Beanpublic AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {AnnotationAwareAspectJAutoProxyCreator creator = new AnnotationAwareAspectJAutoProxyCreator();MyTargetSourceCreator myTargetSourceCreator = new MyTargetSourceCreator();myTargetSourceCreator.setBeanFactory(beanFactory);creator.setCustomTargetSourceCreators(myTargetSourceCreator);return creator;}
}

这样,当我们通过getBean获取userServiceImpl的对象时,就会优先生成代理对象,然后在调用执行链的过程中再通过TargetSource.getTarget获取到被代理对象。但是,为什么我们在getTarget方法中调用getBean就能拿到被代理对象呢?
继续探究,通过断点我发现从getTarget进入时,在resolveBeforeInstantiation方法中返回的bean就是null了,而getBeanPostProcessors方法返回的Processors中也没有了AnnotationAwareAspectJAutoProxyCreator对象,也就是没有进入到AbstractAutoProxyCreator.postProcessBeforeInstantiation方法中,所以不会再次获取到代理对象,那AnnotationAwareAspectJAutoProxyCreator对象是在什么时候移除的呢?
带着问题,我开始反推,发现在AbstractBeanFactoryBasedTargetSourceCreator类中有这样一个方法buildInternalBeanFactory

	protected DefaultListableBeanFactory buildInternalBeanFactory(ConfigurableBeanFactory containingFactory) {DefaultListableBeanFactory internalBeanFactory = new DefaultListableBeanFactory(containingFactory);// Required so that all BeanPostProcessors, Scopes, etc become available.internalBeanFactory.copyConfigurationFrom(containingFactory);// Filter out BeanPostProcessors that are part of the AOP infrastructure,// since those are only meant to apply to beans defined in the original factory.internalBeanFactory.getBeanPostProcessors().removeIf(beanPostProcessor ->beanPostProcessor instanceof AopInfrastructureBean);return internalBeanFactory;}

在这里移除掉了所有AopInfrastructureBean的子类,而AnnotationAwareAspectJAutoProxyCreator就是其子类,那这个方法是在哪里调用的呢?继续反推:

	protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {synchronized (this.internalBeanFactories) {DefaultListableBeanFactory internalBeanFactory = this.internalBeanFactories.get(beanName);if (internalBeanFactory == null) {internalBeanFactory = buildInternalBeanFactory(this.beanFactory);this.internalBeanFactories.put(beanName, internalBeanFactory);}return internalBeanFactory;}}public final TargetSource getTargetSource(Class<?> beanClass, String beanName) {AbstractBeanFactoryBasedTargetSource targetSource =createBeanFactoryBasedTargetSource(beanClass, beanName);// 创建完targetSource后就移除掉AopInfrastructureBean类型的BeanPostProcessor对象,如AnnotationAwareAspectJAutoProxyCreatorDefaultListableBeanFactory internalBeanFactory = getInternalBeanFactoryForBean(beanName);......return targetSource;}

至此,关于TargetSource接口扩展的原理就搞明白了。

总结

本篇篇幅比较长,主要搞明白Spring代理对象是如何创建的以及AOP链式调用过程,而后面的扩展则是对AOP以及Bean创建过程中一些疑惑的补充,可根据实际情况学习掌握。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 线段树统计

    各种线段树统计(全部以求最大为例) 线段树,用于查询区间的一些特定值(如:最大、最小、平均数),支持新建、修改和查询操作,空间要开4倍,注意在新建时要初始化 1、新建 void build(int l,int r,int x) {tree[x]={l,r,0};//在这里不初始化也行,因为已经默认为0了if(l==r)r…...

    2024/4/15 5:11:12
  2. 二维数组的练习

    使用object[][]储存整个表格的数据 package Testshuzubianli;import java.util.Arrays;public class Test04 {public static void main(String[] args) {Object[] emp0 = {1001,"yi",20,"程序员","2020.06.04"};Object[] emp1 = {1001,"e…...

    2024/4/26 22:29:59
  3. Johnny and Another Rating Drop CodeForces - 1362C(规律)

    The last contest held on Johnny’s favorite competitive programming platform has been received rather positively. However, Johnny’s rating has dropped again! He thinks that the presented tasks are lovely, but don’t show the truth about competitors’ skil…...

    2024/4/19 15:45:40
  4. 《程序设计思维与实践》week15 — 作业题

    A - ZJM 与霍格沃兹 题目描述 ZJM 为了准备霍格沃兹的期末考试,决心背魔咒词典,一举拿下咒语翻译题 题库格式:[魔咒] 对应功能 背完题库后,ZJM 开始刷题,现共有 N 道题,每道题给出一个字符串,可能是 [魔咒],也可能是对应功能 ZJM 需要识别这个题目给出的是 [魔咒] 还是…...

    2024/4/15 5:11:09
  5. 继承的理解

    一,什么是继承 是构造函数之间的相关应用 继承的是属性和方法 是子集从父级继承 常见的继承方法有,原型继承,构造函数继承,es6继承 二,构造函数 es5定义构造函数// 构造函数function Father(name,age){this.name=namethis.age=age}Father.prototype.fun1=function(){conso…...

    2024/4/20 13:07:01
  6. 关于go get的第三方包的管理

    旧的解决方案:go get 之前的下载到GOPATH的。一般是“C:\user\username\go\src\项目名“下。 新的解决方案:go get 现在是下载到C:\user\go\pkg\mod\目录下。如果要使用这些包,我们要在项目所在目录的上一级目录下,执行“go mod init 项目目录”,然后就会自动解决依赖。即…...

    2024/4/15 5:11:08
  7. EGM2008大地水准面模型在工程中的应用综述

    结论:(1)在我国大陆,EGM2008 模型高程异常的总体精度为20 cm,华东华中地区12 cm,华北地区达到9 cm,西部地区为24cm。(2)“移去-拟合-恢复”法原理是在利用函数模型( 如二次曲面模型) 进行高程转换前,首先移去用地球重力场模型计算得到高程异常的长波部分或者移去地形…...

    2024/4/15 5:11:08
  8. react-router-dom部分属性的使用

    当某个页面没有被Route包裹时props中是没有history的 此时可以用到类似useHistory的属性 该属性使用时只能加载到组件中使用,无法写到全局中 如果使用中报错可以尝试把react-router-dom的版本重新下载成5.1.2版本 也可以使用createHashHistory 其他属性用法都是类似 "rea…...

    2024/4/24 11:21:19
  9. 判断幻方

    编写程序判定NN(规定N为奇数,且N<20)的矩阵是否是”幻方”,(NN的二维数组各元素的值由用户在程序运行时输入) “幻方”的判定条件是:矩阵每行、每列、主对角线及反对角线上元素之和都相等。输入: 第一行为一个整数,代表矩阵维数N。 第二行开始为矩阵输入数据。输出…...

    2024/4/24 11:21:23
  10. flink实战--jdbc方式将数据写入clickhouse(ClickhouseSink)

    简介Clickhouse 支持http协议的web方式进行访问,也支持JDBC或者ODBC的驱动程序的客户端进行访问,我们使用Flink操作Clickhouse ,可以想操作mysql一样通过JDBC的方式进行访问,本文将具体介绍flink实时写入Clickhouse的实现方式,对于flink自定义sink,参考上篇博客:https:/…...

    2024/4/24 11:21:14
  11. pip安装网络不可达WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None))

    pip install requests -i http://pypi.douban.com/simple --trusted-host pypi.douban.com...

    2024/4/24 11:21:20
  12. 学习笔记 | Pandas使用分组聚合进行组内计算

    01 使用groupby方法拆分数据02 使用agg方法聚合数据03 使用apply方法聚合数据04 使用transform方法聚合数据...

    2024/4/26 20:02:29
  13. 简单了解一下webpack

    webpack打包工具 基本用法都在下面 const path=require("path")//这是用来输出path.resovle const HtmlWebpackPlugin=require("html-webpack-plugin")//用于把HTML页面放入内存 const minicss=require("mini-css-extract-plugin")//webpack4.0以…...

    2024/4/24 11:21:12
  14. 前后端分离项目系列终篇——问题详解

    前言在写这个项目的时候,遇到过各种各样奇奇怪怪的问题,每次都需要花费好长时间去解决,所以说,为了不让以后重蹈覆辙,还是记录一下叭,省的以后忘了,找也找不见,哈哈。如果以后还遇到其他这方面的问题,再继续加叭。 问题描述:前端:1、如何给vue添加自动刷新功能2、如…...

    2024/4/24 11:21:10
  15. 在 C# 中使用 if-elseif-else 语句将决策逻辑添加到代码中 挑战

    挑战6 分钟 这些模块中的代码挑战将强化你学到的知识,帮助你在继续操作之前增强自信。你被要求向你所在公司的软件添加一项功能。 此功能旨在提高软件订阅的续订率。 你的任务是在用户登录软件系统及其订阅即将到期时显示续订消息。 需要添加一些决策语句,将分支逻辑正确添加…...

    2024/4/24 11:21:13
  16. 别人给你机会往往是因为别人给了更差的人机会

    你304分,别人给你机会,其实是别人给两百九十多的也给了机会,别人给你了武汉科技大学机会,其实也是别人给了更差的学校机会,地大,湘潭,北信科,中科院人工智能非全都是这样。华诺也是的。企业和学校是不会让你赚到的。....

    2024/4/24 11:21:08
  17. 食物链(并查集)

    链接:https://ac.nowcoder.com/acm/problem/16884 来源:牛客网 题目描述 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B,B吃C,C吃A。 现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 有人用两种说法对这N…...

    2024/4/24 11:21:07
  18. day29_线程安全丶线程的状态丶线程通信

    线程安全什么是线程安全?如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 为什么会出现线程安全问题?线程安全问题都是由全局变量及静态变量引起的。若每…...

    2024/4/24 11:21:06
  19. Linux开机启动流程

    Linux开机启动流程 标签(空格分隔):课堂笔记整理开机启动流程路开机需要十步 第一步:开机自检(BIOS)就是开始工作之前先对自己的工具进行检查是否正常,如果正常那就可以进行接下来的步骤假如步正常就得检测哪里的问题进行处理。BIOS其实就是主板上的一给自检程序,开机先…...

    2024/4/25 23:35:32
  20. Java学习之ActionListener和ItemListener的区别

    ...

    2024/4/26 12:07:50

最新文章

  1. 【Pytorch】(十三)模型部署: TorchScript

    文章目录 &#xff08;十三&#xff09;模型部署: TorchScriptPytorch动态图的优缺点TorchScriptPytorch模型转换为TorchScripttorch.jit.tracetorch.jit.scripttrace和script的区别总结trace 和script 混合使用保存和加载模型 &#xff08;十三&#xff09;模型部署: TorchScr…...

    2024/4/27 22:36:58
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Golang Gin框架

    1、这篇文章我们简要讨论一些Gin框架 主要是给大家一个基本概念 1、Gin主要是分为路由和中间件部分。 Gin底层使用的是net/http的逻辑&#xff0c;net/http主要是说&#xff0c;当来一个网络请求时&#xff0c;go func开启另一个协程去处理后续(类似epoll)。 然后主协程持续…...

    2024/4/23 6:22:17
  4. java自动化学习-03-02java基础语法01

    1、java基础语法 在JAVA中源文件名称必须和主类名称相同 源文件名&#xff1a;源文件名必须和类名相同。当保存文件的时候&#xff0c;你应该使用类名作为 文件名保存&#xff08;切记 Java 是大小写敏感的&#xff09;&#xff0c;文件名的后缀为 .java。&#xff08;如果文…...

    2024/4/22 0:09:49
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/26 21:56:58
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/26 22:01:59
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57