《Spring系列》第13章 Aop切面(二) 代理创建
前言
本篇文章主要介绍AOP的源码,要想看懂AOP,那么就看AOP给容器中注入了什么组件,这个组件什么时候工作,这个组件的功能是什么?
@EnableAspectJAutoProxy
会向IOC容器中注入一个后置处理器,它会在Bean的创建过程进行加工- 当我们获取Bean的时候,因为已经存在Aop注入的后置处理器,会在Bean的创建阶段生成代理对象
- 最终生成的Bean代理对象,里面会是一个层层代理的链式结构,因为Aop里面的增强方法是存在顺序的,所以这里也是按照顺序执行
- 当调用目标方法的时候,就会按照顺序依次执行增强方法和目标方法
一、@EnableAspectJAutoProxy
1.注解引入了哪个类
当使用AOP的时候,会通过注解@EnableAspectJAutoProxy
来开启AOP,那么这个注解做了些什么呢?看一下其源码,发现通过@Import
注解引用了AspectJAutoProxyRegistrar.class
,其最终目的就是注入了这个类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) // 在这里引入了一个类
public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false;
}
AspectJAutoProxyRegistrar
是上面注解导入的一个类,它实现了ImportBeanDefinitionRegistrar
接口,该接口同样也是Spring提供的扩展点,它可以把自定义的BeanDefinition
注册到Spring容器中。
那么该类中主要做了2件事
- 通过
AopConfigUtils
注册了一个BeanDefinition
,具体为AnnotationAwareAspectJAutoProxyCreator
- 根据
@EnableAspectJAutoProxy
设置的属性,进行相应的操作
// 导入的这个类实现了 ImportBeanDefinitionRegistrar 接口
// 该接口可以将自定义的组件添加到容器中
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 通过Aop工具类 `注册切面注解自动代理创建器当其需要的时候` 这也是方法名的意思AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);// 获取@EnableAspectJAutoProxy注解的属性 在其注解里面可以设置2个属性AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);// 通过这2个属性 在进行响应的处理if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}
}
上面的方法中看到会借助AopConfigUtils
类来注册BeanDefinition
,进入对应的方法里面可以看到注册的类为AnnotationAwareAspectJAutoProxyCreator
// 第1步:首先会调用到这里@Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);}// 第2步:再次会进入到这里@Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {// 在这里看到注入的Bean为 AnnotationAwareAspectJAutoProxyCreator.classreturn registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}// 第3步@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 判断是否包含这个名称为 internalAutoProxyCreator的Beanif (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {// 获取到BeanBeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");// 如果全限定类名是否相同 也就是上面的名字相同,那么需要在判断类型if (!cls.getName().equals(apcDefinition.getBeanClassName())) {// 当不相同的时候int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);// 判断优先级if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}return null;} else {// 不存在则创建BeanRootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", -2147483648);beanDefinition.setRole(2);registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);return beanDefinition;}}
2.引入类介绍
通过上面的内容得知,@EnableAspectJAutoProxy
注解最终会向Bean的定义信息里面注册一个AnnotationAwareAspectJAutoProxyCreator
类,其对应的beanName
为org.springframework.aop.config.internalAutoProxyCreator
,
通过下面的类图可以看出,AnnotationAwareAspectJAutoProxyCreator
实现了BeanPostProcessor
,证明它是一个后置处理器,不仅需要把它注入到IOC容器中,还需要把它注入到BeanFactory
后置处理器集合中
> 注入到后置处理器集合中
实现逻辑的方法为refresh() -> registerBeanPostProcessors()
,它负责了2件事情:
- 把后置处理器存入IOC容器中,所有的bean都要注入到容器中,这是必须的
- 把后置处理器按照优先级规则存入
beanFactory
中
// PostProcessorRegistrationDelegate// 下面的代码看着很长,其实仔细看很简单,主要就是注册后置处理器,但是需要按照优先级分成三组,顺序注册
// 大体的执行流程未:
// 1) 先获取IOC容器已经定义了的需要创建对象的所有BeanPostProcessor
// 2) 给容器中加别的BeanPostProcess
// 3) 优先注册实现了 PriorityOrdered接口的BeanPostProcessor
// 4) 再给容器中注册实现了Ordered接口的BeanPostProcessor
// 5) 注册没实现优先级接口的BeanPostProcessor
// 6) 注册BeanPostProcessor 实际上就是通过getBean()创建BeanPostProcessor对象,保存在容器中
// 7) 把BeanPostProcessor注册到BeanFactory中public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {// 获取到工厂里面的所有BeanPostProcessorString[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);// Register BeanPostProcessorChecker that logs an info message when// a bean is created during BeanPostProcessor instantiation, i.e. when// a bean is not eligible for getting processed by all BeanPostProcessors.// 数量int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));// Separate between BeanPostProcessors that implement PriorityOrdered,// Ordered, and the rest.List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();// 遍历for (String ppName : postProcessorNames) {// 实现PriorityOrdered接口的后置处理器添加到priorityOrderedPostProcessorsif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);// 实现MergedBeanDefinitionPostProcessor接口的添加到internalPostProcessorsif (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}// 实现Ordered接口的添加到orderedPostProcessorNames// 像使用AOP需要注入的【AnnotationAwareAspectJAutoProxyCreator】在顶层实现了Ordered接口// 所以会在这里else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {// 未实现排序的处理器nonOrderedPostProcessorNames.add(ppName);}}// First, register the BeanPostProcessors that implement PriorityOrdered.// 注册实现PriorityOrdered接口的后置处理器sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// Next, register the BeanPostProcessors that implement Ordered.// 注册实现Ordered接口的后置处理器List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);// Now, register all regular BeanPostProcessors.// 注册未实现排序的处理器List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);// Finally, re-register all internal BeanPostProcessors.// 最终 注册实现MergedBeanDefinitionPostProcessor接口的sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);// Re-register post-processor for detecting inner beans as ApplicationListeners, // moving it to the end of the processor chain (for picking up proxies etc).// 重新注册后置处理器把内部Bean检测为ApplicationListenersbeanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));}
二、获取增强器
带着问题看源码
AOP底层是借助的后置处理器,这个已经说了很多遍了,上面介绍的AnnotationAwareAspectJAutoProxyCreator
类实现了BeanPostProcessor
,并且注入到容器中了,剩下的肯定使用,那么我们需要关心的无非是几个问题:
- 创建成AOP代理对象的方法入口在哪里?
- 获取该bean可以应用的增强器
- 代理对象创建过程
- 创建后的代理对象长啥样?
1.入口方法
既然是后置处理器,肯定是bean初始化的时候,会创建成代理对象,那么bean的初始化也分情况:
- 如果是普通的bean创建,那么会在
doCreateBean() -> initializeBean()
,当创建bean以后,初始化的时候,会执行对应的后置处理器 - 如果是循环依赖的bean创建,那么会在
doCreateBean() -> getEarlyBeanReference()
,在这里也会执行后置处理器
上面我在Debug源码的时候只试了这2种情况,可能还要其它的入口,但到不管有多少个入口,到最后都会到1个地方负责创建代理,那就是AnnotationAwareAspectJAutoProxyCreator
类,找到其重写后置处理器接口的方法就好了。
在源码翻阅的时候,发现其中并没有,但是在其父类AbstractAutoProxyCreator
,其中存在重写的postProcessAfterInitialization()
2.获取该bean可以应用的增强器
> 整体逻辑
AOP又给我们提供了各种类型的切面,当需要把bean给创建成代理对象,整体逻辑如下:
- 创建前需要存在各种特殊判断,判断这个bean到底是否需要创建代理对象,排除一些特殊情况,当判断通过,接下来就是开始创建代理
- 获取代码设置的全部增强器,还有各种切入点
Pointcut
,这些第一次获取以后肯定是缓存起来的 - 从全部的增强器中获取到该bean可以使用的增强器
- 对增强器进行排序
> wrapIfNecessary()
通过上面我们看到,创建代理对象都是走到AbstractAutoProxyCreator#wrapIfNecessary()
这个类的后置处理器方法中
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {// 根据给定bean的class和name构建出一个key,格式: beanClassName_beanNameObject cacheKey = getCacheKey(bean.getClass(), beanName);// 可能存在循环依赖,判断该bean是否已经提前暴露, 存在这里就不用创建代理对象了if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 适合被代理,则需求封装指定beanreturn wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}
wrapIfNecessary()
这个方法通过名字也可以看出意思来,如果该bean可以被代理,那就创建
// 在这里面的类代表无须再次尝试创建AOP代理对象, 包括已经创建完的和不需要创建的
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 1.如果已经处理过if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}// 2.代表该bean无需增强if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 4.获取当前bean的Advices和AdvisorsObject[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 5.如果存在增强器则创建代理if (specificInterceptors != DO_NOT_PROXY) {// 代表该bean已经处理了this.advisedBeans.put(cacheKey, Boolean.TRUE);// 创建搭理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));// 创建完代理后,将cacheKey -> 代理类的class放到缓存this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}// 6.标记为无需处理this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}
> 不能创建代理对象前的特殊情况
在wrapIfNecessary()
前会有几个方法执行,负责判断是否可以把当前bean创建成代理对象,初始化的时候肯定会有一些特殊情况存在,需要特殊排除
isInfrastructureClass()
排除基础设置类
是否是基础配置类,一些固定的类不能被代理
@Overrideprotected boolean isInfrastructureClass(Class<?> beanClass) {// Previously we setProxyTargetClass(true) in the constructor, but that has too// broad an impact. Instead we now override isInfrastructureClass to avoid proxying// aspects. I'm not entirely happy with that as there is no good reason not// to advise aspects, except that it causes advice invocation to go through a// proxy, and if the aspect implements e.g the Ordered interface it will be// proxied by that interface and fail at runtime as the advice method is not// defined on the interface. We could potentially relax the restriction about// not advising aspects in the future.return (super.isInfrastructureClass(beanClass) ||(this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));}
shouldSkip()
是否跳过创建
@Overrideprotected boolean shouldSkip(Class<?> beanClass, String beanName) {// TODO: Consider optimization by caching the list of the aspect names// 获取候选的增强器List<Advisor> candidateAdvisors = findCandidateAdvisors();for (Advisor advisor : candidateAdvisors) {if (advisor instanceof AspectJPointcutAdvisor &&((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {return true;}}return super.shouldSkip(beanClass, beanName);}
> 获取全部的增强器
继续回到wrapIfNecessary()
中,接下来会获取增强器,getAdvicesAndAdvisorsForBean()
获取指定bean的增强方法一定是包含两个步骤的:
- 获取所有的增强,通过
findCandidateAdvisors()
- 寻找所有增强中适用于该bean的增强并应用,通过
findAdvisorsThatCanApply()
// 获取当前Bean的所有增强器@Nullableprotected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {// 获取增强方法List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);// 获取到了则返回,获取不到返回nullreturn advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();}// 获取到能在这个目标方法中匹配的增强器protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 1)找到所有的增强器List<Advisor> candidateAdvisors = this.findCandidateAdvisors();// 2)从所有增强里面获取到能在bean使用的增强器List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 3.扩展方法,留个子类实现this.extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {// 4.对符合条件的Advisor进行排序eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}
a> findCandidateAdvisors() 获取全部增强器
- 通过调用父类方法,来解析通过XML配置的AOP
- 解析通过注解方式设置的AOP
- 将所有的封装成增强器返回
@Override
protected List<Advisor> findCandidateAdvisors() {// 1)通过父类,加载对XML配置的AOPList<Advisor> advisors = super.findCandidateAdvisors();// Build Advisors for all AspectJ aspects in the bean factory.if (this.aspectJAdvisorsBuilder != null) {// 2) 解析注解方式配置的AOPadvisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}return advisors;
}
b> buildAspectJAdvisors() 筛选AOP切面类
【方法功能】:既然要筛选切面类,那肯定要遍历项目,筛选出全部标记@Aspect
的切面类
上面方法中,会分别解析通过XML和注解配置的AOP,这里只介绍第2种,因为现在的开发更多的使用注解配置
那么就详细看一下buildAspectJAdvisors()
,该方法的整体逻辑为:
- 第一次获取,不存在增强器缓存的情况
- 获取所有在beanFactory中注册的beanName
- 遍历所有的beanName,并找出声明
@Aspect
注解的类- 如果是切面类是单例,则获取到该单例bean中的所有增强方法,然后存入缓存
- 其它情况,把当前BeanFactory和切面bean封装为一个多例类型的Factory,存入缓存
- 不是第一次获取,存在增强器缓存的情况
- 执行到这里,代表缓存中是存在的
- 首先从切面缓存中直接获取增强器,代表是一个单例的切面类
- 获取不到,则可能是多例,然后对应的
factory
,然后获取增强类
public List<Advisor> buildAspectJAdvisors() {List<String> aspectNames = this.aspectBeanNames;// 1.尝试从缓存中获取aspectNames,若没有则手动获取if (aspectNames == null) {synchronized (this) {aspectNames = this.aspectBeanNames;// 加锁以后Double Checkif (aspectNames == null) {// 返回结果List<Advisor> advisors = new ArrayList<>();aspectNames = new ArrayList<>();// 1.获取所有的beanNameString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);// 2.遍历for (String beanName : beanNames) {// 不合法的bean则略过, 由子类重写方法定义规则if (!isEligibleBean(beanName)) {continue;}// We must be careful not to instantiate beans eagerly as in this case they// would be cached by the Spring container but would not have been weaved.// 获取对应的bean类型Class<?> beanType = this.beanFactory.getType(beanName, false);if (beanType == null) {continue;}// 如果该bean存在@Aspect注解if (this.advisorFactory.isAspect(beanType)) {aspectNames.add(beanName);// 把切面类封装成一个元数据类, 包含bean的树形AspectMetadata amd = new AspectMetadata(beanType, beanName);// 判断@Aspect注解中标注的是否为singleton类型,默认的切面类都是singleton类型if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);// ---------找到增强类中的增强方法, 也就是那些标记 @Before @After 这些注解的方法---------List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 如果切面类是singleton类型,则将解析得到的Advisor进行缓存if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);}else {// 如果不是单例,则将factory放到缓存,以便再次获取时可以通过factory来解析this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);}else {// 如果@Aspect注解标注的beanName的Bean是单例,但切面实例化模型不是单例,则抛异常if (this.beanFactory.isSingleton(beanName)) {throw new IllegalArgumentException("Bean with name '" + beanName +"' is a singleton, but aspect instantiation model is not singleton");}// 将当前BeanFactory和切面bean封装为一个多例类型的Factory,同时对当前bean和factory进行缓存MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}this.aspectBeanNames = aspectNames;return advisors;}}}if (aspectNames.isEmpty()) {return Collections.emptyList();}List<Advisor> advisors = new ArrayList<>();for (String aspectName : aspectNames) {// 根据aspectName从缓存中获取Advisor集合List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);if (cachedAdvisors != null) {// 缓存获取到了,返回结果advisors.addAll(cachedAdvisors);}else {// 如果不存在于advisorsCache缓存,则代表存在于aspectFactoryCache中,可能是多例的情况,这时候不能bean,而要存beanFactory// 从aspectFactoryCache中拿到缓存的factory,然后解析出Advisor,添加到结果中MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}return advisors;
}
c> getAdvisors() 解析切面类中的增强方法
【方法功能】:上面在buildAspectJAdvisors()
已经晒出出所有的AOP切面类了,然后就是遍历这些类,通过该方法,筛选里面的切面方法
- 获取切面类所有标注了AOP注解的方法
- 遍历处理方法
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {// 1.获取类型Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();// 2.获取beanNameString aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();// 3.验证validate(aspectClass);// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator// so that it will only instantiate once.// 通过上面的英文可以看出将其进行封装,使用装饰器模式,保持每次获取到的都是同一个切面实例MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new ArrayList<>();// 获取所有的没有使用@Pointcut注解标注的方法,然后对其进行遍历for (Method method : getAdvisorMethods(aspectClass)) {// 判断当前方法是否标注有@Before,@After或@Around等注解,如果标注了,则将其封装为一个AdvisorAdvisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);if (advisor != null) {advisors.add(advisor);}}// If it's a per target aspect, emit the dummy instantiating aspect.if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {// 如果Advisor不为空,并且是需要延迟初始化的bean,则在第0位位置添加一个同步实例化增强器(用以保证增强使用之前的实例化)Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}// Find introduction fields.// 判断属性上是否包含有@DeclareParents注解标注的需要新添加的属性,如果有,则将其封装为一个Advisorfor (Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors;
}
d> getAdvisor() 普通增强器获取
【方法功能】:前面已经找到了切面类中的所有方法,接下来就是遍历每个方法,层层遍历
上面的getAdvisor()
获取到了切面类中的所有方法,然后调用该重载方法,负责对切点方法注解的获取,然后根据注解信息生成增强
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {// 校验validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());// 切点信息的获取AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}// 根据切点信息生成增强器return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
e> getPointcut() 切入信息的获取
【方法功能】:负责解析切面注解上的切入点
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {// 1.查找并返回给定方法的第一个AspectJ注解(@Before, @Around, @After, @AfterReturning, @AfterThrowing, @Pointcut)AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}// 2.使用AspectJExpressionPointcut实例封装获取的信息AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);// 提取得到的注解中的表达式,// 例如:@Around("execution(* com.joonwhee.open.aop.*.*(..))"),得到:execution(* com.joonwhee.open.aop.*.*(..))ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if (this.beanFactory != null) {ajexp.setBeanFactory(this.beanFactory);}return ajexp;
}
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {// 从所有的切面注解遍历,找到该方法匹配的注解for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);if (foundAnnotation != null) {return foundAnnotation;}}return null;
}
f> 根据切点信息生成增强器
> 创建增强器包装类并赋初始值
所有的增强都由Advisor
的实现类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;// 实例化增强器this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);}
}
> 创建增强器对象
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,this.aspectInstanceFactory, this.declarationOrder, this.aspectName);return (advice != null ? advice : EMPTY_ADVICE);
}
通过下面方法中的switch可以看到,Spring会根据不同的注解生成不同的增强器
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();validate(candidateAspectClass);AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}// If we get here, we know we have an AspectJ method.// Check that it's an AspectJ-annotated classif (!isAspect(candidateAspectClass)) {throw new AopConfigException("Advice must be declared inside an aspect type: " +"Offending method '" + candidateAdviceMethod + "' in class [" +candidateAspectClass.getName() + "]");}if (logger.isDebugEnabled()) {logger.debug("Found AspectJ method: " + candidateAdviceMethod);}AbstractAspectJAdvice springAdvice;// 根据不同的类创建switch (aspectJAnnotation.getAnnotationType()) {case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;case AtAround:springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtBefore:springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter:springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning:springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing: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);}springAdvice.calculateArgumentBindings();return springAdvice;
}
> 筛选该bean可以使用的增强器
a> findAdvisorsThatCanApply()
【方法描述】:遍历全部增强器,依次判断该bean可否应用
继续回到上面的方法中wrapIfNecessary() -> getAdvicesAndAdvisorsForBean() -> findEligibleAdvisors()
中,当我们获取了全部的增强以后,这个肯定需要缓存起来的,然后创建bean的时候,需要筛选出该bean可以使用的增强器
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {// 空的话直接返回if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}List<Advisor> eligibleAdvisors = new ArrayList<>();// 首先处理引介增强(@DeclareParents)用的比较少可以忽略for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}boolean hasIntroductions = !eligibleAdvisors.isEmpty();// 遍历所有增强器for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor) {// already processedcontinue;}// 判断是否可以应用if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors;}
b> canApply() 判断该bean可否应用增强器
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {// 判断是否上面提到的引介增强,用的比较少if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}// 通常我们的Advisor都是PointcutAdvisor类型else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}
}
这里面就是负责把切入点和方法进行匹配
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");// 判断目标bean是否与增强器上面的切点表达式是否匹配// 也就是判断 @Pointcut(value = "execution(public int com.h3c.aop.bean.*.*(..))") 这个和目前bean是否匹配if (!pc.getClassFilter().matches(targetClass)) {return false;}// 判断如果当前Advisor所指代的方法的切点表达式如果是对任意方法都放行,则直接返回MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...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));// 遍历目标类上的接口方法for (Class<?> clazz : classes) {// Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {// 使用matches判断能否作用于该方法上if (introductionAwareMethodMatcher != null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false;
}
3.执行逻辑图
上面介绍了如何获取增强器,那么在通过下面的图回顾一下:
三、根据增强器创建代理
1.Spring代理介绍
上面已经获取到了其增强器,然后继续回到wrapIfNecessary()
,接下来就是开始创建代理了,那么在看源码之前先把代理的知识聊一下
如果忘了最基础的如何创建代理请阅读《GoF设计模式》 之No.11代理模式》
那么接下来说一下Spring框架,Spring作为一个优秀的框架,提供了多种应用层面上代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory
注意:此处这里指的是Spring提供的应用层得方式,并不是指的底层实现方式。底层实现方式现在只有业界都熟悉的两种:JDK动态代理和CGLIB代理~
ProxyFactoryBean
是将我们的AOP和IOC
融合起来,ProxyFactory
则是只能通过代码硬编码进行编写 一般都是给spring自己使用。AspectJ
是目前大家最常用的集成AspectJ
和Spring
涉及的关键类说明
ProxyConfig
:为上面三个类提供配置属性
AdvisedSupport
:继承ProxyConfig,实现了Advised。封装了对通知(Advise)和通知器(Advisor)的操作
ProxyCreatorSupport
:继承AdvisedSupport,其帮助子类(上面三个类)创建JDK或者cglib的代理对象
1) ProxyFactoryBean
使用Spring提供的类org.springframework.aop.framework.ProxyFactoryBean
是创建AOP的最基本的方式,是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置。
它是将Aop和IOC融合起来,所以它肯定是个Bean,然后我们可以自定义我们的代理实现逻辑,最终交给Spring容器管理即可。
public class ProxyFactoryBean extends ProxyCreatorSupportimplements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
【写个例子】
@Configuration
public class ProxyConfig {// 目标类@Beanpublic HelloService helloService() {return new HelloService();}// 增强类@Beanpublic LogMethodBeforeAdvice logMethodBeforeAdvice() {return new LogMethodBeforeAdvice();}// 创建一个代理,注入IOC,从这里可以看到ProxyFactoryBean会与IOC融合起来@Beanpublic ProxyFactoryBean proxyFactoryBean(HelloService helloService) {ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();// 设置目标对象proxyFactoryBean.setTarget(helloService);// 设置需要被代理的接口proxyFactoryBean.setInterfaces(HelloInterface.class);// 需要植入进目标对象的bean列表 此处需要注意:这些bean必须实现类 org.aopalliance.intercept.MethodInterceptor或 org.springframework.aop.Advisor的bean ,配置中的顺序对应调用的顺序proxyFactoryBean.setInterceptorNames("logMethodBeforeAdvice");return proxyFactoryBean;}public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyConfig.class);HelloInterface helloInterface = (HelloInterface) context.getBean("proxyFactoryBean");helloInterface.hello();}}interface HelloInterface {void hello();
}class HelloService implements HelloInterface {@Overridepublic void hello() {System.out.println("执行hello()-------");}
}class LogMethodBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("before-----------------");}
}
2) ProxyFactory
它和Spring容器没啥关系,可议直接创建代理来使用:
public static void main(String[] args) {ProxyFactory proxyFactory = new ProxyFactory(new HelloService());// 添加两个Advise,一个匿名内部类表示proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args1, target) ->System.out.println("AfterReturningAdvice method=" + method.getName()));proxyFactory.addAdvice(new LogMethodBeforeAdvice());HelloInterface proxy = (HelloInterface) proxyFactory.getProxy();proxy.hello();
}// 输出
before-----------------
执行hello()-------
AfterReturningAdvice method=hello
很显然它代理的Bean都是new出来的,所以比如HelloServiceImpl
就不能和Spring IoC很好的结合了,所以一般都是Spring内部去使用。
public class ProxyFactory extends ProxyCreatorSupport {// 它提供了丰富的构造函数~~~public ProxyFactory() {}public ProxyFactory(Object target) {setTarget(target);setInterfaces(ClassUtils.getAllInterfaces(target));}public ProxyFactory(Class<?>... proxyInterfaces) {setInterfaces(proxyInterfaces);}public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {addInterface(proxyInterface);addAdvice(interceptor);}public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {addInterface(proxyInterface);setTargetSource(targetSource);}// 创建代理的语句:调用父类ProxyCreatorSupport#createAopProxy 此处就不用再解释了public Object getProxy() {return createAopProxy().getProxy();}public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}// 主义这是个静态方法,可以一步到位,代理指定的接口public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();}public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();}// 注意:若调用此方法生成代理,就直接使用的是CGLIB的方式的public static Object getProxy(TargetSource targetSource) {if (targetSource.getTargetClass() == null) {throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTargetSource(targetSource);proxyFactory.setProxyTargetClass(true);return proxyFactory.getProxy();}
}
3) AspectJProxyFactory
在低版本Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置,低版本Spring AOP的配置是被批评最多的地方。Spring听取这方面的批评声音,并下决心彻底改变这一现状。在Spring2.0中,Spring AOP已经焕然一新,你可以使用@AspectJ注解非常容易的定义一个切面,不需要实现任何的接口
AspectJ是目前大家最常用的 起到集成AspectJ和Spring,也就是我们平时长谈的:自动代理模式。它整个代理的过程全部交给Spring内部去完成,无侵入。
我们只需要配置切面、通知、切点表达式就能自动的实现切入的效果,使用起来极其方便,对调用者可以说是非常透明化的。相信这也是为何当下最流行这种方式的原因~
2.源码分析
接下来就是继续看源码了,首先,下面看到的Aop代理源码,采用的ProxyFactory
,也就是上面介绍到的第2种,采用硬编码的方式
> createProxy() 创建代理工厂
// AbstractAutoProxyCreator
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 (Proxy.isProxyClass(beanClass)) {Class[] var6 = beanClass.getInterfaces();int var7 = var6.length;for(int var8 = 0; var8 < var7; ++var8) {Class<?> ifc = var6[var8];proxyFactory.addInterface(ifc);}}} else if (this.shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);} else {this.evaluateProxyInterfaces(beanClass, proxyFactory);}// 获取所有增强器(通知方法)Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);// 保存到proxyFactoryproxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);this.customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (this.advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}ClassLoader classLoader = this.getProxyClassLoader();if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {classLoader = ((SmartClassLoader)classLoader).getOriginalClassLoader();}// 获取代理return proxyFactory.getProxy(classLoader);}
> getProxy 获取代理对象
// ProxyFactory
public Object getProxy(@Nullable ClassLoader classLoader) {// createAopProxy()调用的父类ProxyCreatorSupportreturn createAopProxy().getProxy(classLoader);
}
> createAopProxy 创建AOP代理
// ProxyCreatorSupport
protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);
}public AopProxyFactory getAopProxyFactory() {return this.aopProxyFactory;
}// DefaultAopProxyFactory// 创建代理对象@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 普通对象则使用cglib动态代理return new ObjenesisCglibAopProxy(config);}else {// 存在接口, 则使用JDK动态代理return new JdkDynamicAopProxy(config);}}
> getProxy()
这里会获取代理对象,如果是采用的JDK动态代理,则执行到JdkDynamicAopProxy
中
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());}// 我们熟悉的代理创建return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
如果是采用的Cglib代理,则进入CglibAopProxy
中
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");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);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);// Configure 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();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}
}
s(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();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
“: Common causes of this problem include using a final class or a non-visible class”,
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException(“Unexpected AOP exception”, ex);
}
}
相关文章:

《Spring系列》第13章 Aop切面(二) 代理创建
前言 本篇文章主要介绍AOP的源码,要想看懂AOP,那么就看AOP给容器中注入了什么组件,这个组件什么时候工作,这个组件的功能是什么? EnableAspectJAutoProxy会向IOC容器中注入一个后置处理器,它会在Bean的创…...

算法-贪心
贪心算法1信息学竞赛课堂贪心算法2贪心法实际生活中,经常需要求一些问题的“可行解”和“最优解”,这就是所谓的“最优化”问题。一般来说,每个最优化问题都包含一组“限制条件”和一个“目标函数”,符合限制条件的问题求解方案称…...

【数据结构与算法】树(Tree)【详解】
文章目录前言树一、树的基本概念1、树的定义2、基本术语3、树的性质二、树的存储结构1、双亲表示法2、孩子表示法3、孩子兄弟表示法二叉树一、二叉树的概念1、二叉树的定义2、几个特殊的二叉树3、二叉树的性质4、二叉树的存储结构二、遍历二叉树1、先序遍历2、中序遍历3、后序遍…...

OSPF------LSA 详解
LSA头部 [r1]display ospf lsdb 链路状态老化时间(Link-State Age) 16bits,单位s当该LSA被始发路由器产生时,该值被设置为0,之后随着该LSA在网络中被洪泛,老化时间逐渐累积。(但是不能让它一直增长&#x…...

js加解密入门
首先,让我们简单介绍一下百度公司的文心一言。文心一言是百度公司推出的一项文本生成工具,它可以根据给定的主题或关键词生成一句简短而富有启发性的文字。在我们的加密和解密方法中,我们将利用文心一言的特点来生成随机的字符串,…...

vue+Echarts导入自定义地图
在vue项目先安装echarts //在vue文件中的<script>中引入 import * as echarts from "echarts"; import geoJson from ../assets/map/Fmap.json; //自定义地图的位置 import * as topojson from "topojson-client"; //使用组件topojson-client自定…...

dp-组合总和 Ⅳ
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。题目数据保证答案符合 32 位整数范围。示例 1:输入:nums [1,2,3], target 4输出:7解释:所…...

46-堆
目录 1.概念 2.表示 3.三大操作 4.代码实现最大堆(基于数组,编号从0开始) 4.1.根据孩子节点k获取当前父节点的索引 4.2.根据父节点k求左孩子节点下标 4.3.根据父节点k求右孩子节点下标 4.4.判空 4.5.toString()方法 4.6.判断数组中…...

Mysql高可用高性能存储应用系列3 - mysqld_multi配置主从集群
概述 主从复制要解决的问题,1)写操作锁表,影响读操作,影响业务。2)数据库备份。3)随着数据增加,I/O操作增多,单机出现瓶颈。 主从复制就是从服务器的主节点,复制到多个从节点,默认采用异步的方…...

天干地支(Java)
题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(w)、己&a…...

码住,虹科工业树莓派应用小tips
在应用虹科工业树莓派进行项目开发的过程中,我们会应用到各种功能,部分功能看似不起眼,但是在实际应用开发过程中却非常重要。接下来虹科分享几个工业树莓派在应用过程中经常会遇到的几个问题,并分享解决方案,帮助大家…...

美国新规-带绳窗帘亚马逊ANSI/WCMA A100.1-20测试标准详解
亚马逊要求所有有线窗帘都经过测试,符合下列特定法规或标准要求: 商品法规/标准要求带绳窗帘以下所有项: 显示检测结果符合 ANSI/WCMA A100.1-2018(带绳窗帘商品的美国国家安全标准)的检测报告。 美国消费品安全委员…...

【华为OD机试 2023最新 】 模拟商场优惠打折(C++)
题目描述 模拟商场优惠打折,有三种优惠券可以用,满减券、打折券和无门槛券。 满减券:满100减10,满200减20,满300减30,满400减40,以此类推不限制使用; 打折券:固定折扣92折,且打折之后向下取整,每次购物只能用1次; 无门槛券:一张券减5元,没有使用限制。 每个…...

前端直接生成GIF动态图实践
前言去年在博客中发了两篇关于GIF动态生成的博客,GIF图像动态生成-JAVA后台生成和基于FFmpeg的Java视频Mp4转GIF初探,在这两篇博客中都是采用JAVA语言在后台进行转换。使用JAVA的同学经过自己的改造和开发也可以应用在项目上。前段时间有朋友私下问&…...

2023年Java岗面试八股文及答案整理(金三银四最新版)
春招,秋招,社招,我们Java程序员的面试之路,是挺难的,过了HR,还得被技术面,小刀在去各个厂面试的时候,经常是通宵睡不着觉,头发都脱了一大把,还好最终侥幸能够…...

centos8上安装redis
一、安装前准备 在安装Redis之前,需要确保CentOS 8系统已经安装了EPEL存储库和Redis的依赖库。 安装EPEL存储库 EPEL存储库是一个由Fedora项目提供的额外软件包仓库,包含了许多常用的软件包。在CentOS 8系统上,可以通过以下命令安装EPEL存储…...

新六级阅读通关特训
词汇题(55道) 1. You should carefully think over_____ the manager said at the meeting. A. that B. which C. what D. whose 1.选C,考察宾语从句连接词,主句谓语动词think over后面缺宾语,后面的宾语从句谓语动…...

【AI绘画】如何使用Google Colab安装Stable Diffusion
【AI绘画】如何在Colab安装的Stable Diffusion背景准备安装查看资源仓库跳转到Colab运行Stable Diffusion基础设置启动运行访问Stable Diffusion WebUI界面模型资源推荐背景 本地部署Stable Diffusion过程麻烦,对机器配置要求高,GPU 4G,然而…...

C++:STL架构图
STL架构图1:仿函数2:算法架构图算法库 再看一下这个实例 #include<vector> #include<algorithm> #include<functional> #include<iostream> using namespace std;int main() {int i[6] {1,2,3,4,5,6};vector<int,allocato…...

[Ubuntu][网络][教程]端口转发以及端口管理
1. 平台介绍 Ubuntu 20.04 LTS Armv7 2. 端口管理 进行端口转发之前,要先对端口进行一系列设置 2.1 安装ufw sudo apt install ufw2.2 开启22端口 开启ufw之后,默认的22端口不会自动打开,使用SSH的话需要手动打开 sudo ufw allow 22…...

@Scheduled 定时任务不执行
一、排查代码中添加的定时任务步骤是否正确 启动类上加 EnableScheduling 注解定时任务类上加Component定时方法上加Scheduled Scheduled(cron "0 19 16 * * ?")public void cron() {log.info("定时任务开启:---");}二、排查是否任务阻塞&am…...

我是怎样被卷的(二)
被卷的过程,虽然是辛苦种种(加班熬夜陪着爆肝),但终有所值。没有这样的高压环境,我都不知道自己居然可以这么的优秀。 我要答复的问题,分为4类。一是我自己已经掌握的,二是需要找别人获取的&am…...

Linux- 浅谈ELF目标文件格式
理解了进程的描述和创建之后,自然会想到我们编写的可执行程序是如何作为一个进程工作的?这就涉及可执行文件的格式、编译、链接和装载等相关知识。 这里先提一个常见的名词“目标文件”,是指编译器生成的文件。“目标”指目标平台,…...

C++ MVC模式
概述 C是一种流行的编程语言,它可以用于构建各种类型的应用程序,包括Web应用程序、桌面应用程序和移动应用程序。在这里,我将为您介绍C中的MVC模式,以及如何在C中实现MVC模式。 MVC(Model-View-Controller࿰…...

IntelliJ IDEA2021安装教程
1.鼠标右击【JetBrains 2021】压缩包(win11系统需先点击“显示更多选项”)选择【解压到“JetBrains 2021”】 2.打开解压后的文件夹,鼠标右击您需要安装的软件名称(如:IdealU-2021.3.1)选择【以管理员身份运…...

day16—选择题
文章目录1.计算每位学生的多学科加总成绩的SQL是(C)2.以下哪个不是与Mysql服务器相互作用的通讯协议(B)3.设有两个事务T1,T2,其并发操作如下所示,下面评价正确的是(D)4.如果事务T获得了数据项Q上的排它锁&a…...

LLVM 的中间代码(IR) 基本语法
LLVM 的中间代码(IR) 基本语法 以下是 LLVM IR 的基本语法和每个语法的实例代码: 1.数据类型 LLVM IR 支持多种数据类型,包括整型、浮点型、指针型和向量型等。以下是 LLVM IR 中定义不同类型的语法和示例代码: 整…...

多标签在单行出现省略的实现
简言 实现在有宽度的一行内,标签过多会出现省略号。 实现 首先要实现单行省略的效果。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&quo…...

第二十七章 纹理总结
OpenGL 纹理是左下角(0,0) 右上角(1,1)。 需要告诉OpenGL纹理环绕方式,主要有四种:GL_REPEAT(重复纹理图像),GL_MIRRORED_REPEAT(重复纹理图像,但是每次重复图片是镜像放置的), GL_CLAMP_TO_EDGE(坐标再0-1直接,超出部分会重复纹理坐标的边缘,有边缘拉伸效果),GL…...

【Linux面试】-(腾讯,百度,美团,滴滴)
文章目录Linux 面试题-(腾讯,百度,美团,滴滴) 分析日志 t.log(访问量),将各个 ip 地址截取,并统计出现次数,并按从大到小排序(腾讯) http://192.168.200.10/index1.html http://192.168.200.10/index2.html http://192.168.200.20/index1.html http://19…...