首页 > 编程语言 >(六)Spring源码解析:Spring AOP源码解析

(六)Spring源码解析:Spring AOP源码解析

时间:2023-07-02 12:32:15浏览次数:53  
标签:... return Spring 代理 源码 new 解析 方法 public

〇、AOP概念

Aspect:切面

给业务方法增加到功能,切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。

Pointcut:切入点

切入点指声明的一个或多个连接点的集合,通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

Advice:通知、增强

通知表示切面的执行时间,Advice也叫增强。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。

JoinPoint:连接点

连接切面的业务方法,连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

Target:目标对象

目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。

AOP中重要的三个要素:AspectPointcutAdvice

意思是说:在Advice的时间、在Pointcut的位置,执行Aspect。

为了方便大家理解AOP中的相关概念,请见下图所示:

一、动态AOP的使用示例

当我们对某些类有横切性的逻辑时,为了不破坏目标类,我们则可以使用AOP的方式将增强逻辑注入到目标类上。为了更清晰的了解AOP的用法,下面我们通过一个使用案例,实现一下面向切面编程。

首先,我们创建一个普通的业务类MuseAop

public class MuseAop {
    public void goToWork() {
        System.out.println("Muse去上班");
    }
}

其次,创建Advisor,然后对MuseAop的goToWork()方法进行增强操作

@Aspect
public class MuseAspectJ {
    @Pointcut("execution(* *.goToWork(..))")
    public void goToWork() {}

    @Before("goToWork()")
    public void beforeGoToWork() {
        System.out.println("@Before:起床、洗漱、穿衣");
    }

    @After("goToWork()")
    public void afterGoToWork() {
        System.out.println("@After:开始工作了");
    }

    @SneakyThrows
    @Around("goToWork()")
    public Object aroundGoToWork(ProceedingJoinPoint point) {
        System.out.println("@Around-1:听一首摇滚歌曲提提神");
        Object result = point.proceed();
        System.out.println("@Around-2:听一首钢琴乐舒缓情绪");
        return result;
    }
}

然后,在配置文件中添加bean,并且通过配置 <aop:aspectj-autoproxy /> 来开启aop动态代理

<aop:aspectj-autoproxy />
<bean id="museAop" class="com.muse.springbootdemo.entity.aop.MuseAop"/>
<bean class="com.muse.springbootdemo.entity.aop.MuseAspectJ"/>

最后,编写测试类,查看测试结果

二、动态AOP自定义标签

根据上面我们使用AOP的示例,我们可以看到是通过配置<aop:aspectj-autoproxy>来开启动态代理的,因此我们可以将它为AOP源码分析的切入点。请见下图所示,我们在全项目中搜索了aspectj-autoproxy,然后发现注入了新的BeanDefinitionParser实现类—— AspectJAutoProxyBeanDefinitionParser

那么下面,我们来看一下AspectJAutoProxyBeanDefinitionParser类的具体实现,由于AspectJAutoProxyBeanDefinitionParser实现了BeanDefinitionParser接口,而BeanDefinitionParser只有一个方法,即:parse(element, parserContext),所以,我们来看一下AspectJAutoProxyBeanDefinitionParser 类是如何实现parse方法的。

下面,我们先看一下AopNamespaceUtils类的registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element)方法的具体实现逻辑。

2.1> registerAspectJAnnotationAutoProxyCreatorIfNecessary方法解析

该方法是用于注册或者升级AnnotationAwareAspectJAutoProxyCreator类型的APC,对于AOP的实现,基本都是在AnnotationAwareAspectJAutoProxyCreator类中完成的,它可以根据@Point注解定义的切点来自动代理相匹配的bean。但是为了配置简便,Spring使用了自定义配置来帮助我们自动注册AnnotationAwareAspectJAutoProxyCreator,注册流程如下所示:

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

AUTO_PROXY_CREATOR_BEAN_NAME(“org.springframework.aop.config.internalAutoProxyCreator”)的作用是内部管理的自动代理创建器的bean名称。如果名称为“AUTO_PROXY_CREATOR_BEAN_NAME”的apc实例在容器中已经存在,则试图替换为该bean为AnnotationAwareAspectJAutoProxyCreator类型;否则,在容器中创建AnnotationAwareAspectJAutoProxyCreator类型的APC实例。

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    /** 步骤1:如果容器中已经存在apc实例,则试图替换为AnnotationAwareAspectJAutoProxyCreator类型 */
	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;
	}
    /** 步骤2:如果不存在,则向容器中创建AnnotationAwareAspectJAutoProxyCreator类型的apc实例对象 */
	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
	beanDefinition.setSource(source);
	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;
}

2.1.1> 关于APC优先级别的补充说明

APC的抽象类AbstractAdvisorAutoProxyCreator默认有4个实现类,如下所示:

我们可以通过AopConfigUtils.findPriorityForClass(...)方法来获得当前APC实现类的优先级别,数字越大,优先级别越高这个优先级,其实就是ArrayList中存储的APC实现类的index序号。源码请见下图所示:

/** 默认初始化3个APC(AutoProxyCreator)实现类 */
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
static {
	APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); // 第0级别
	APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); // 第1级别
	APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // 第2级别
}

/** 通过Class获得优先级别 */
private static int findPriorityForClass(Class<?> clazz) {
	return APC_PRIORITY_LIST.indexOf(clazz);
}

/** 通过className获得优先级别 */
private static int findPriorityForClass(@Nullable String className) {
	for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
		Class<?> clazz = APC_PRIORITY_LIST.get(i);
		if (clazz.getName().equals(className)) return i;	
	}
}

第0级别】InfrastructureAdvisorAutoProxyCreator<br> 【第1级别】AspectJAwareAdvisorAutoProxyCreator<br> 【第2级别】AnnotationAwareAspectJAutoProxyCreator

2.2> useClassProxyingIfNecessary方法解析

步骤1:获得属性PROXY_TARGET_CLASS_ATTRIBUTE(“proxy-target-class”)

如果proxy-target-class等于true,才会执行AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry)方法;

步骤2:属性EXPOSE_PROXY_ATTRIBUTE(“expose-proxy”)

如果expose-proxy等于true,才会执行AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry)方法;

useClassProxyingIfNecessary()方法源码如下所示:

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement {
	if (sourceElement != null) {
        // 设置参数proxy-target-class
		boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
		if (proxyTargetClass) AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        
		// 设置参数expose-proxy
        boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
		if (exposeProxy) AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
	}
}

这两个方法逻辑基本一致的,就是首先判断是否存在名字为AUTO_PROXY_CREATOR_BEAN_NAME的BeanDefinition实例对象,如果存在的话,将该对象的属性proxyTargetClass或者属性exposeProxy赋值为true即可。

/** 将definition的proxyTargetClass属性设置为true */
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
	}
}
/** 将definition的exposeProxy属性设置为true */
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
	}
}

proxyTargetClass属性的作用是什么?

<aop:config proxy-target-class="true"> :配置为强制使用CGLIB代理<br> <aop:aspectj-autoproxy proxy-target-class="true"/> :配置为CGLIB代理+@AspectJ自动代理支持

exposeProxy属性的作用是什么?

<aop:aspectj-autoproxy export-proxy="true"/> :支持通过AopContext.currentProxy()来暴露当前代理类。

exposeProxy场景举例:

public interface AService {
    public void a();
    public void b();
}

@Service
public class AserviceImpl implements AService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void a() {
    	this.b(); // 由于this指向target对象,所以不会执行b事务切面
        ((AService) AopContext.currentProxy()).b(); // 暴露了当前的代理类,所以可以执行b事务切面
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void b() {}
}

三、创建AOP代理

通过上面的介绍,我们可以看到AOP在极力的创建AnnotationAwareAspectJAutoProxyCreator对象作为APC的bean实例:

既然是这样的,那我们就来看一下AnnotationAwareAspectJAutoProxyCreator类的继承结构,请见下图所示:

从上图中我们可以看到它实现了BeanPostProcessor接口,那么这个接口里有我们熟悉的postProcessAfterInitialization(...)方法,该方法是由AbstractAutoProxyCreator类实现的,源码请见如下所示:

下面真正执行对bean增强的方法就是wrapIfNecessary(...)了,在这里有如下几种情况是不需要增加的:

情况1targetSourcedBeans中保存的是已经处理过的bean,如果在其中,则不需要增强;<br> 【情况2advisedBeans中value值为false表示不需要增强;<br> 【情况3基础设施类(实现AdvicesPointcutAdvisorsAopInfrastructureBeans这四个接口的类),则不需要增强;<br> 【情况4】如果是Original实例(以beanClass.getName()开头,并且以".ORIGINAL"结尾),则不需要增强;

wrapIfNecessary(...)方法中,主要有两个重要的方法:getAdvicesAndAdvisorsForBean(...)createProxy(...) ,后续我们会针对这两个方法进行解析。

根据上面的描述,我们可以知道getAdvicesAndAdvisorsForBean(...)方法就是用来获得增强器的方法了,这里通过调用findEligibleAdvisors(beanClass, beanName)方法来获得增强器列表,并进行结果返回;如果没有获得增强器,则返回DO_NOT_PROXY(其值为null),其源码如下所示:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
	/** 寻找增强列表 */
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) return DO_NOT_PROXY;
	return advisors.toArray();
}

Eligible的英文翻译是“符合条件的”,那么findEligibleAdvisors(...)方法的主要作用就是——找到符合条件的增强器,具体操作有如下两步:

步骤1】通过调用findCandidateAdvisors(...)方法,获取所有的增强;<br> 【步骤2】通过调用findAdvisorsThatCanApply(...)方法,寻找所有增强中适用于bean的增强并进行应用;

相关源码,请见如下所示:

3.1> findCandidateAdvisors()获取所有增强器

findCandidateAdvisors()方法中,我们可以获得所有增强器,此处一共执行了两个步骤来获得所有增强器:

步骤1】获得xml中配置的AOP声明;<br> 【步骤2】获得带有aspectj注释的AOP声明;

如下就是findCandidateAdvisors()方法的相关源码:

3.1.1> findCandidateAdvisors() 获得xml中配置的AOP声明

protected List<Advisor> findCandidateAdvisors() {
	return this.advisorRetrievalHelper.findAdvisorBeans();
}

findAdvisorBeans()方法中我们可以看到,如下逻辑:

步骤1】首先,尝试从缓存cachedAdvisorBeanNames)中获得Advisor类型的bean名称列表。<br> 【步骤2】如果没有获得到,则试图去IOC容器中获得所有Advisor类型的bean名称列表。<br> 【步骤3】如果都没有获得Advisor类型的bean名称列表,则直接返回空集合。<br> 【步骤4】如果不为空,则通过beanFactory.getBean(name, Advisor.class)来获得Advisor实例集合,并进行返回。

如下就是findAdvisorBeans()方法的相关源码:

public List<Advisor> findAdvisorBeans() {
	//【步骤1】获得所有被缓存的Advisor的bean名称列表
	String[] advisorNames = this.cachedAdvisorBeanNames;

    //【步骤2】如果缓存中没有,那么我们就从BeanFactoryUtils中获得Advisor的bean名称列表,然后作为已缓存的bean名称列表
	if (advisorNames == null) {
        //【官方注释】这里不要初始化FactoryBeans:我们需要保留所有未初始化的常规bean,以便让自动代理创建器应用于它们!
		// 返回容器中所有Advisor类型的bean名称列表
		advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
		this.cachedAdvisorBeanNames = advisorNames;
	}

    //【步骤3】如果缓存也没有并且从从BeanFactoryUtils中也没获得到,则直接返回空集合
	if (advisorNames.length == 0) return new ArrayList<>();

    //【步骤4】从IOC容器中获得Advisor对象实例集合,并返回
	List<Advisor> advisors = new ArrayList<>();
	for (String name : advisorNames) {
		if (isEligibleBean(name)) {
			if (this.beanFactory.isCurrentlyInCreation(name)) 
				if (logger.isTraceEnabled()) logger.trace("Skipping currently created advisor '" + name + "'");
			else {
				try {
					advisors.add(this.beanFactory.getBean(name, Advisor.class));
				} catch (BeanCreationException ex) { ... ... }
			}
		}
	}
	return advisors;
}

3.1.2> buildAspectJAdvisors() 获得带有aspectj注释的AOP声明

buildAspectJAdvisors()方法中,主要逻辑有四个步骤:

步骤1】获得Aspect的beanName列表;<br> 【步骤2】通过beanName来获得MetadataAwareAspectInstanceFactory实例,具体如下所示:如果per-clauses(aspect实例化模式) 类型等于SINGLETON,则创建BeanFactoryAspectInstanceFactory类型的factory;否则,则创建PrototypeAspectInstanceFactory类型的factory;<br> 【步骤3】通过调用advisorFactory.getAdvisors(factory)来获得Advisor列表;<br> 【步骤4】维护advisorsCache缓存或aspectFactoryCache缓存;如果beanName是单例的,则将beanNameAdvisor列表维护到advisorsCache缓存中;否则,将beanNamefactory维护到aspectFactoryCache缓存中;

buildAspectJAdvisors()方法中,源码及注释如下所示:

public List<Advisor> buildAspectJAdvisors() {
    // 获得已经解析过的Aspect的beanName列表
	List<String> aspectNames = this.aspectBeanNames;
    
    // 步骤1:如果aspectNames为空,则试图从IOC中解析出Aspect的beanName列表
	if (aspectNames == null) {
		synchronized (this) { // 加锁
			aspectNames = this.aspectBeanNames; // double check
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
                // 获得IOC容器中所有的beanName
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
				for (String beanName : beanNames) {
					if (!isEligibleBean(beanName)) continue; // isEligibleBean方法默认返回true
					// 官方注释:我们必须小心不要急切地实例化bean,因为在这种情况下,它们将被Spring容器缓存,但不会被织入
					Class<?> beanType = this.beanFactory.getType(beanName, false);
					if (beanType == null) continue;
                    
                    // 一个类如果包含@Aspect注解并且不是被ajc编译的类,则返回true
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 单例则创建BeanFactoryAspectInstanceFactory类型的factory
							MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
							if (this.beanFactory.isSingleton(beanName)) 
                                this.advisorsCache.put(beanName, classAdvisors); // 缓存Advisor
							else 
                                this.aspectFactoryCache.put(beanName, factory); // 缓存MetadataAwareAspectInstanceFactory
							advisors.addAll(classAdvisors);
						} else {
							if (this.beanFactory.isSingleton(beanName)) {
								throw new IllegalArgumentException("Bean with name '" + beanName +
										"' is a singleton, but aspect instantiation model is not singleton");
							}
                            // 非单例则创建PrototypeAspectInstanceFactory类型的factory
							MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
							this.aspectFactoryCache.put(beanName, factory); // 缓存MetadataAwareAspectInstanceFactory
							advisors.addAll(this.advisorFactory.getAdvisors(factory));
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors; // 将解析好的Advisor列表执行返回操作
			}
		}
	}
	if (aspectNames.isEmpty()) return Collections.emptyList();

    // 步骤2:如果没有“经历过”步骤1,则再次处解析Advisor列表
	List<Advisor> advisors = new ArrayList<>();
	for (String aspectName : aspectNames) {
		List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
		if (cachedAdvisors != null) // 情况1:如果在advisorsCache缓存中存在,则直接返回Advisor列表
			advisors.addAll(cachedAdvisors); 
		else { // 情况2:如果在aspectFactoryCache缓存中存在,则需要调用factory的getAdvisors方法来获得Advisor列表
			MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
			advisors.addAll(this.advisorFactory.getAdvisors(factory)); 
		}
	}
	return advisors;
}

在上面的源码中,我们可以发现,其中通过advisorFactory.getAdvisors(factory) 来获得Advisor集合是非常核心的代码,因为只有它才能帮助我们获得Advisor列表,部分截取代码如下所示:

advisorFactory.getAdvisors(factory)方法的源码如下所示:

3.1.3> getAdvisor(...) 获得普通增强器

getAdvisor(...)方法的源码如下所示:

a> 步骤1:获得切点表达式的相关信息

下面我们来看一下步骤1中的获得切点表达式的相关信息getPointcut(...)方法源码逻辑:

方法上的AspectJ相关注解(AspectJAnnotation),查找注解的顺序按照:@Pointcut——>@Around——>@Before——>@After——>@AfterReturning——>@AfterThrowing ,如果找到了某个注解,则直接返回AspectJAnnotation实例对象,不需要继续寻找了。

b> 步骤2:根据切点信息生成增强

我们可以看到,在步骤2中,是通过创建一个InstantiationModelAwarePointcutAdvisorImpl实例对象来生成切点的增强的,源码如下所示:

getAdvice方法中,我们根据上面解析出的方法上面使用的AspectJ注解来生成相应的AbstractAspectJAdvice,代码如下所示:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
						MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder,
                        String aspectName) {
	Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 获得切面类
	validate(candidateAspectClass);
    // 获得切面上的AspectJ相关注解
	AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 
	if (aspectJAnnotation == null) return null; // 如果没有AspectJ相关注解,那么直接返回null即可
    
	// 如果类上有@Aspect注解 并且 该类是被AspectJ编译的,则直接抛出异常
	if (!isAspect(candidateAspectClass))
		throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" +
				candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");

    // 根据不同的AspectJ相关注解,生成对应不同的springAdvice的实例对象
    AbstractAspectJAdvice springAdvice;
	switch (aspectJAnnotation.getAnnotationType()) {
		case AtPointcut: // @Pointcut
			return null;
		case AtAround: // @Around
			springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
		case AtBefore: // @Before
			springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
		case AtAfter: // @After
			springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
		case AtAfterReturning: // @AfterReturning
			springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterReturningAnnotation.returning()))
				springAdvice.setReturningName(afterReturningAnnotation.returning());
			break;
		case AtAfterThrowing: // @AfterThrowing
			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);
	}
    // 为springAdvice实例对象赋值
	springAdvice.setAspectName(aspectName);
	springAdvice.setDeclarationOrder(declarationOrder);
	String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
	if (argNames != null) springAdvice.setArgumentNamesFromStringArray(argNames);
	springAdvice.calculateArgumentBindings();
	return springAdvice;
}

3.1.4> new SyntheticInstantiationAdvisor(...) 创建同步实例化增强器

如果寻找的增强器不为空而且又配置了增强延迟初始化,那么就需要在首位加入同步实例化增强器。同步实例化增强器SyntheticlnstantiationAdvisor源码如下所示:

3.1.5> getDeclareParentsAdvisor(field) 获得@DeclareParents配置的增强器

DeclareParents主要用于引介增强的注解形式的实现,如果属性上使用了@DeclareParents注解,那么我们就来创建DeclareParentsAdvisor类型的增强器,其中关于@DeclareParents注解的使用场景,请参照3.1.6部分即可,源码如下所示:

private Advisor getDeclareParentsAdvisor(Field introductionField) {
    // 获得属性上使用了@DeclareParents注解的注解类
	DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
	if (declareParents == null) return null;
	if (DeclareParents.class == declareParents.defaultImpl()) throw new IllegalStateException(...);
	// 创建DeclareParentsAdvisor类型的增强器
	return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

3.1.6> @DeclareParents注解的使用

创建接口IPay和实现类OnlinePay

public interface IPay {
    void pay();
}
@Service
public class OnlinePay implements IPay {
    @Override
    public void pay() {
        System.out.println("-------OnlinePay--------");
    }
}

创建接口IPayPlugin和实现类AlipayPlugin

public interface IPayPlugin {
    void payPlugin();
}
@Service
public class AlipayPlugin implements IPayPlugin {
    @Override
    public void payPlugin() {
        System.out.println("-------Alipay--------");
    }
}

使用@DeclareParents注解配置切面。该注解的作用是:可以在代理目标类上增加新的行为(即:新增新的方法)。

@Aspect
@Component
public class PayAspectJ {
    @DeclareParents(value = "com.muse.springbootdemo.entity.aop.IPay+",defaultImpl = AlipayPlugin.class)
    public IPayPlugin alipayPlugin;
}

创建配置类MuseConfig,开启AspectJ的自动代理,然后就会为使用@Aspect注解的bean创建一个代理类。

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class MuseConfig {
}

进行测试,我们发现从IOC中获得的bean实现了IPay和IPayPlugin这两个接口,并且会在后台输出“-------OnlinePay--------”和“-------Alipay-------

3.2> findAdvisorsThatCanApply(...)寻找匹配的增强器

在3.1中,我们已经分析完获取所有增强器的方法findCandidateAdvisors(),那么本节我们将在获取的所有增强(candidateAdvisors)基础上,再去寻找匹配的增强器,即:findAdvisorsThatCanApply(...)方法,相关源码如下图所示:

findAdvisorsThatCanApply(...)方法中,其主要功能是获得所有增强器candidateAdvisors中,适用于当前clazz的增强器列表。而由于针对引介增强普通增强的处理是不同的, 所以采用分开处理的方式,请见下图所示:

那么,什么是引介增强呢? 引介增强是一种特殊的增强,其它的增强是方法级别的增强,即只能在方法前或方法后添加增强。而引介增强则不是添加到方法上的增强, 而是添加到类级别的增强,即:可以为目标类动态实现某个接口,或者动态添加某些方法。具体实现请见下图所示:

那么,在上面的findAdvisorsThatCanApply(...)方法源码中,我们可以发现,canApply(...)方法是其中很重要的判断方法,那么它内部主要做了什么操作呢?在其方法内部,依然根据引介增强普通增强两种增强形式分别进行的判断,其中,如果是引介增强的话,则判断该增强是否可以应用在targetClass上,如果可以则返回true,否则返回false。那么,如果是普通增强,则需要再调用canApply(...)方法继续进行逻辑判断,相关源码请见下图所示:

canApply(...)方法中,主要是逻辑是获得 targetClass类(非代理类)targetClass类的相关所有接口 中的所有方法去匹配,是否满足对targetClass类的增强,如果找到了,则返回false;如果找不到,则返回true;相关源码,请见下图所示:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    // 如果Pointcut不能应用于targetClass类上,则直接返回false
	if (!pc.getClassFilter().matches(targetClass)) return false;
	
	MethodMatcher methodMatcher = pc.getMethodMatcher();
	if (methodMatcher == MethodMatcher.TRUE) return true;
	
	IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
	if (methodMatcher instanceof IntroductionAwareMethodMatcher) 
		introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
	
	Set<Class<?>> classes = new LinkedHashSet<>();
	if (!Proxy.isProxyClass(targetClass)) classes.add(ClassUtils.getUserClass(targetClass)); // 只添加非代理类
	classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); // 添加targetClass的所有接口类
	
    // 遍历所有相关类的所有方法,只要有与targetClass匹配的方法,则返回ture
    for (Class<?> clazz : classes) {
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
		for (Method method : methods) {
			if (introductionAwareMethodMatcher != null ?
					introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
					methodMatcher.matches(method, targetClass)) {
				return true;
			}
		}
	}
	return false;
}

3.3> createProxy(...) 创建AOP代理

在3.1和3.2章节中,我们介绍了如何获取bean所对应的Advisor增强器,那么,下面我们就该开始利用这些增强器去配合创建代理对象了,这部分工作由createProxy()方法负责,源码如下所示:

/** 为beanClass创建AOP代理 */
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, 
                             TargetSource targetSource) {
	if (this.beanFactory instanceof ConfigurableListableBeanFactory)
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, 
                                         beanName, beanClass);
	
    ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this); // 将当前对象中的信息复制到proxyFactory实例中

    // 调用proxyFactory.setProxyTargetClass(...)用于设置是否应该使用targetClass而不是它的接口代理 
    // 调用proxyFactory.addInterface(...)用于添加代理接口
	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) proxyFactory.setProxyTargetClass(true);
		else evaluateProxyInterfaces(beanClass, proxyFactory);
	}

    /** 3.3.1> 获得所有增强器 */
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors); // 添加增强器集合
	proxyFactory.setTargetSource(targetSource); // 设置被代理的类
	customizeProxyFactory(proxyFactory); // 定制代理(空方法)
	proxyFactory.setFrozen(this.freezeProxy); // 默认false,表示代理被配置后,就不允许修改它的配置了
	if (advisorsPreFiltered()) proxyFactory.setPreFiltered(true);
	ClassLoader classLoader = getProxyClassLoader();
	if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader())
		classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();

    /** 3.3.2> 获得代理对象 */
	return proxyFactory.getProxy(classLoader);
}

从上面的源码我们可以整理出createProxy(...)方法的操作流程:<br> 【步骤1】创建ProxyFactory实例对象,后续会对其各个参数进行初始化赋值,为最后调用proxyFactory的getProxy(...)方法做准备;<br> 【步骤2】将当前对象(this)中的部分信息复制到proxyFactory实例中;<br> 【步骤3】调用proxyFactory.setProxyTargetClass(...)用于设置是否应该使用targetClass而不是它的接口代理;<br> 【步骤4】调用proxyFactory.addInterface(...)用于添加代理接口;<br> 【步骤5】获得所有增强器Advisor并添加到proxyFactory中;<br> 【步骤6】向proxyFactory中设置被代理的类;<br> 【步骤7】可以对proxyFactory进行定制化操作,默认是空方法;<br> 【步骤8】通过调用proxyFactory的setFrozen(...)方法,来控制代理工厂被配置之后,是否还允许修改通知;<br> 【步骤9】调用proxyFactory的getProxy(...)方法获得代理对象

3.3.1> buildAdvisors(...)获得所有增强器

buildAdvisors(...)方法中,我们可以看到大致做了两个步骤:

步骤1】获得所有拦截器(普通的+指定的);<br> 【步骤2】通过这些拦截器生成Advisor增强集合,

buildAdvisors(...)方法中的源码及注释如下所示:

收集拦截器的代码并不复杂,那么下面我们再来看wrap(...)方法是如何通过拦截器生成Advisor增强的,源码及注释请见下图所示:

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    // 如果待封装的adviceObject本来就是Advisor类型,则直接返回即可
	if (adviceObject instanceof Advisor) return (Advisor) adviceObject;	
    // 如果既不是Advisor类型也不是Advice类型,则直接抛出异常,无法执行包装操作
	if (!(adviceObject instanceof Advice)) throw new UnknownAdviceTypeException(adviceObject);

	Advice advice = (Advice) adviceObject;
    // 如果adviceObject是MethodInterceptor类型,则包装成DefaultPointcutAdvisor实例对象
	if (advice instanceof MethodInterceptor) return new DefaultPointcutAdvisor(advice);
	// 遍历适配器列表adapters,如果也支持advice,则包装成DefaultPointcutAdvisor实例对象
	for (AdvisorAdapter adapter : this.adapters) 
		if (adapter.supportsAdvice(advice)) return new DefaultPointcutAdvisor(advice);
		
	throw new UnknownAdviceTypeException(advice);
}

3.3.2>proxyFactory.getProxy(...) 获得代理对象

通过上面的操作,我们已经获得了增强Advisor列表了,并且也做好了对proxyFactory的赋值准备操作,下面就该到了获得代理对象的步骤了,具体逻辑代码在getProxy(...)方法中,源码如下所示:

a> createAopProxy() 创建AOP代理

创建aop代理的时候,首先激活所有的Listener,然后再去创建AOP代理,这部分代码很少,很好理解,请见如下所示:

在上面代码中,我们需要再继续分析红框内的createAopProxy(this)方法,

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (!NativeDetector.inNativeImage() &&
			(config.isOptimize() || // 默认false
			 config.isProxyTargetClass() || // 默认false
			 hasNoUserSuppliedProxyInterfaces(config))) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) throw new AopConfigException(...);
		
		// 如果是接口或者是代理类,则使用JDK动态代理
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) 
			return new JdkDynamicAopProxy(config);
		
		// 否则,使用CGlib代理
		return new ObjenesisCglibAopProxy(config);
	}
	else return new JdkDynamicAopProxy(config); // 使用JDK动态代理
}

isOptimize() :用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理,对于JDK动态代理(默认代理)无效。<br> isProxyTargetClass() :这个属性为true时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为true, CGLIB代理将被创建,设置方式为:<aop:aspectj-autoproxy-proxy-target-class="true"/> 。<br> hasNoUserSuppliedProxyInterfaces(config) :是否存在代理接口。

通过createAopProxy(config)方法,根据不同情况,会返回不同代理对象,在下面内容中,我们会分别分析不同代理对象的代理流程:

如果采用JDK动态代理,则返回JdkDynamicAopProxy代理对象;<br> 如果采用CGlib代理,则返回ObjenesisCglibAopProxy代理对象;

b> JdkDynamicAopProxy.getProxy(classLoader) 获得代理对象

我们先来看一下JdkDynamicAopProxy类对getProxy(...)方法的实现,我们发现,里面只调用了Proxy.newProxyInstance(...)方法,源码如下所示:

public Object getProxy(@Nullable ClassLoader classLoader) {
	return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

自己编写过JDK动态代理的朋友应该对getProxy(...)方法中的内容并不陌生,它的编写结构如下所示:

概括来说就是3点:<br> 【第1点】要实现InvocationHandler接口;<br> 【第2点】重写invoke方法;<br> 【第3点】通过调用Proxy.newProxyInstance(...)获得代理对象;

那么我们再来看JdkDynamicAopProxy类,它也实现了InvocationHandler接口,即:满足了第1点;

那么就剩下第2点,即:重写invoke方法了,我们把目光注视到JdkDynamicAopProxy类的invoke方法,看看它是如何实现的:

将拦截器封装到ReflectiveMethodInvocation类中,并通过调用proceed方法逐一调用拦截器,下面是proceed源码内容:

public Object proceed() throws Throwable {
    // 执行完所有增强后执行切点方法
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) 
		return invokeJoinpoint();

	// 获取下一个要执行的拦截器
	Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	//  动态匹配
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 
		InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
		if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) 
			return dm.interceptor.invoke(this); // 调用拦截器
		else 
			return proceed(); // 不匹配不调用拦截器
	}
    // 普通拦截器,直接调用拦截器即可
	else return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); // 将this作为参数传递以保证当前实例中调用链的执行
}

proceed方法中,或许代码逻辑并没有我们想象得那么复杂,ReflectiveMethodlnvocation中的主要职责是维护了链接调用的计数器,记录着当前调用链接的位置,以便链可以有序地进行下去,那么在这个方法中并没有我们之前设想的维护各种增强的顺序,而是将此工作委托给了各个增强器,使各个增强器在内部进行逻辑实现

c> ObjenesisCglibAopProxy.getProxy(classLoader) 获得代理对象

上面介绍完JDK动态代理之后,我们下面来介绍Cglib动态代理是如果获得代理对象的。对于Cglib大家一定不会陌生,下面我们就手写一下通过Cglib获得代理对象的示例,请见如下代码所示:

public class CGlibTest {
    public static void main(String[] args) {
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CGlibTest.class);
        enhancer.setCallback(new MyMethodInterceptor());
        // 通过增强器,获得CGlib代理对象
        CGlibTest test = (CGlibTest) enhancer.create();
        test.play();
    }

    public void play() {
        System.out.println("play game!");
    }
}

/**
 * 创建GClib拦截器
 */
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("-----------------preExecutor-----------------");
        Object result =  methodProxy.invokeSuper(o, objects);
        System.out.println("-----------------afterExecutor-----------------");
        return result;
    }
}

重温了cglib动态代理之后,我们来看Spring AOP是如何通过它来获得代理的,此处我们来看一下ObjenesisCglibAopProxy类的getProxy(...)方法是如何实现的:

public Object getProxy(@Nullable ClassLoader classLoader) {
	try {
		Class<?> rootClass = this.advised.getTargetClass();
		Class<?> proxySuperClass = rootClass;
        // 如果类名中包含"$$"
		if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
			proxySuperClass = rootClass.getSuperclass();
			Class<?>[] additionalInterfaces = rootClass.getInterfaces();
			for (Class<?> additionalInterface : additionalInterfaces) 
				this.advised.addInterface(additionalInterface);
		}
		// 针对类进行验证操作
		validateClassIfNecessary(proxySuperClass, classLoader);
        
		// 创建Cglib的Enhancer并对其进行配置操作
		Enhancer enhancer = createEnhancer();
		if (classLoader != null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) 
				enhancer.setUseCache(false);
		}
		enhancer.setSuperclass(proxySuperClass);
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
        
		/** 设置拦截器 */
		Callback[] callbacks = getCallbacks(rootClass);
		Class<?>[] types = new Class<?>[callbacks.length];
		for (int x = 0; x < types.length; x++) types[x] = callbacks[x].getClass();
		enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);
        
		// 生成代理类并创建代理实例对象
		return createProxyClassAndInstance(enhancer, callbacks);
	} 
    catch (CodeGenerationException | IllegalArgumentException ex) {...}
}

/** 创建代理对象 */
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
	enhancer.setInterceptDuringConstruction(false);
	enhancer.setCallbacks(callbacks);
	return (this.constructorArgs != null && this.constructorArgTypes != null ?
			enhancer.create(this.constructorArgTypes, this.constructorArgs) : // 采用有参构造函数创建实例对象
			enhancer.create()); // 采用无参构造函数创建实例对象
}

通过上面的代码,我们可以大致了解getProxy方法的处理逻辑分为3个步骤:<br> 【步骤1】创建Enhancer实例对象,并对其进行初始化;<br> 【步骤2】获得Callback拦截器,并赋值到Enhancer实例对象中;<br> 【步骤3】通过Enhancer实例对象的create(...)方法来创建代理对象;

由于我们手工创建过Cglib动态代理了,所以对于上述步骤都会比较熟悉,但是对于第二步获得拦截器,我们还是比较陌生的,那么我们就来着重分析一下这个方法,其源码如下所示:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
	// Parameters used for optimization choices...
	boolean exposeProxy = this.advised.isExposeProxy();
	boolean isFrozen = this.advised.isFrozen();
	boolean isStatic = this.advised.getTargetSource().isStatic();
	// 步骤1:创建aopInterceptor拦截器
	Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
	
    // 步骤2:创建targetInterceptor拦截器
	Callback targetInterceptor;
	if (exposeProxy) targetInterceptor = (isStatic ?
			new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
			new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
	else targetInterceptor = (isStatic ?
			new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
			new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
	
    // 步骤3:创建targetDispatcher调度器
	Callback targetDispatcher = (isStatic ?
			new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
	
    // 步骤4:将不同的拦截器或调度器都保存到Callback数组中
	Callback[] mainCallbacks = new Callback[] {
			aopInterceptor,  // 用于一般的增强器
			targetInterceptor,  // 如果被优化了,调用target而不考虑调用增强
			new SerializableNoOp(),  // 映射到this的方法不能重写
			targetDispatcher,
			this.advisedDispatcher,
			new EqualsInterceptor(this.advised),
			new HashCodeInterceptor(this.advised)
	};
	
	// 步骤5:如果目标是静态的并且Advice链是冻结的,那么我们可以通过使用该方法的固定链直接将AOP调用发送到目标来进行一些优化。
	Callback[] callbacks;
    if (isStatic && isFrozen) {
		Method[] methods = rootClass.getMethods();
		Callback[] fixedCallbacks = new Callback[methods.length];
		this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);
		for (int x = 0; x < methods.length; x++) {
			Method method = methods[x];
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
			fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
					chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
			this.fixedInterceptorMap.put(method, x);
		}
		// callbacks = mainCallbacks + fixedCallbacks;
		callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
		System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
		System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
		this.fixedInterceptorOffset = mainCallbacks.length;
	}
	else 
        callbacks = mainCallbacks; // callbacks = mainCallbacks;
	return callbacks;
}

从上面我们自定义演示Cglib例子中可以看到,通过enhancer.setCallback(new MyMethodInterceptor())这段代码,可以将我们自定义的拦截器注入到增强中,那么,在上面源码中,我们在步骤1中将advised保存到DynamicAdvisedInterceptor中,并在下面的步骤里,将其保存到Callback数组中,那么,当执行Cglib代理调用的时候,就会调用DynamicAdvisedInterceptor类中的intercept(...)方法了,代码如下所示:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	Object target = null;
	TargetSource targetSource = this.advised.getTargetSource();
	try {
		if (this.advised.exposeProxy) {
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);
        // 获取拦截器链
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// 没有拦截链,直接调用原方法即可
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else // 调用拦截链
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) targetSource.releaseTarget(target);
		if (setProxyContext) AopContext.setCurrentProxy(oldProxy);
	}
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

标签:...,return,Spring,代理,源码,new,解析,方法,public
From: https://blog.51cto.com/u_15003301/6604286

相关文章

  • 部署springboot项目到linux服务器
    1.使用Xshell连接服务器xshell免费版地址:家庭/学校免费-NetSarangWebsite(xshell.com)2.安装java运行环境sudoaptinstallopenjdk-8-jdk3.使用xftp上传后端jar包文件xftp免费版地址:家庭/学校免费-NetSarangWebsite(xshell.com)4.在xshell中使用命令......
  • SpringCloud学习(四)
    参考:https://blog.csdn.net/qq_25928447/article/details/124340264?spm=1001.2014.3001.5501消息队列之前如果需要进行远程调用,一般可以通过发送HTTP请求来完成,现在,可以使用第二种方式,就是消息队列,它能够将发送方发送的信息放入队列中,当新的消息入队时,会通知接收方进行处理......
  • 菜鸟学习Spring——SpringMVC注解版前台向后台传值的两种方式
    一、概述。在很多企业的开法中常常用到SpringMVC+Spring+Hibernate(mybatis)这样的架构,SpringMVC相当于Struts是页面到Contorller直接的交互的框架也是界面把信息传输到Contorller层的一种架构,通过这个架构可以让我们把页面和Contorller层解耦,使得开发人员的分工更加明确。二、代码演......
  • Spring 配置事务管理器方法的理解
    @BeanpublicPlatformTransactionManagertransactionManager(DataSourcedataSource){DataSourceTransactionManagertransactionManager=newDataSourceTransactionManager();transactionManager.setDataSource(dataSource);return......
  • SpringMVC的执行流程
    SpringMVC的执行流程要求掌握SpringMVC的执行流程了解SpringMVC的重要组件的作用概要我把整个流程分成三个阶段准备阶段匹配阶段执行阶段准备阶段在Web容器第一次用到DispatcherServlet的时候,会创建其对象并执行init方法init方法内会创建SpringWeb......
  • 【springboot】支持热部署
    加入依赖坐标<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency> 配置Compiler选项 按2下shift键,选择registry 修改配置后保存(ctrl+S)即可以热部署了或者直接点击更快......
  • 【springboot】配置项
    主要是properties和yml,可以互相转换(toyaml),如果是自己定义的变量,是可以通过@Value注解来从配置文件中注入对应配置的值。先看properties文件中的配置test.hello=Hello4再来看代码里面怎么用@RestControllerpublicclassTestController{@Value("${test.hello:TEST}")......
  • DNS 域名解析服务
    DNS域名解析服务拓扑图:需求描述:➢主域名服务器地址为www.bdqn.com(192.168.100.50)、*.bdqn.com(192.168.100.60)➢从域名服务器地址为推荐步骤:主辅助挂载系统光盘系统光盘挂载到/mt目录安装修改检查主配置文件是否错误配置检查配置检查启动服务设置开机自动启动检查二、挂载系统光......
  • SpringBoot整合Kafka
    1、安装kafka这里我是用的是docker-compose方式安装(1)安装docker和docker-composesudoyuminstall-yyum-utilssudoyum-config-manager\--add-repo\https://download.docker.com/linux/centos/docker-ce.reposudoyuminstalldocker-cedocker-ce-clico......
  • 在 Kubernetes(k8s) 上部署 Spring Boot 应用程序:应用程序使用环境变量中的错误端口属
    如果我使用此配置启动部署(即先启动服务,然后启动部署)则pod在启动期间会失败。在日志中,我可以找到以下消息:***************************APPLICATIONFAILEDTOSTART***************************Description:Bindingtotargetorg.springframework.boot.autoconfigu......