spring aop 源码分析(三) @Scope注解创建代理对象
一.源码环境的搭建:
@Component
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyMath implements Calc{ public Integer add(int num1,int num2){
return num1+num2;
}
}
@Configuration
@ComponentScan("com.yang.xiao.hui.aop")
public class App
{
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
MyMath myMath = (MyMath)ctx.getBean("myMath");
System.out.println(myMath.getClass());
System.out.println(ctx.getBean("scopedTarget.myMath").getClass());
}
}
启动main方法:
二.源码分析,先看Scope注解:
scope注解的proxyMode的属性决定了被该注解标注的类是否会被代理,这是一个枚举,有如下几个值:
本次测试代码使用的是cglib代理,被Scope标注的对象,如果代理模式是jdk或者cglib代理的话,会在spring容器中产生2个bean,一个是代理的bean,一个是原始的bean,原始的bean的beanName被命名为:scopedTarget.xx:
debug调试:
省略n步:
我们在这个方法里面可以看到spring是如何解析主启动类,扫描到其他的bean的:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { //解析Component注解
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
} // Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
} // Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( //解析Component注解ComponentScan.class
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); //componentScan解析器,对该注解进行解析
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//..............................省略部分代码
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//通过包名,扫描该包名下的所有bean的定义信息
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); //scope注解解析器,获取Scope注解的属性信息,封装成ScopeMetadata
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//beanName生成器,这里是myMath
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); //这里处理scope注解,下面跟进这个
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) { String originalBeanName = definition.getBeanName();//原始的beanName: myMath
BeanDefinition targetDefinition = definition.getBeanDefinition(); //原始的bean定义信息
String targetBeanName = getTargetBeanName(originalBeanName); //scopedTarget.myMath // Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); //创建一个代理对象
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName)); //将原始的bean定义信息作为被装饰的bean定义信息
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);//设置原始的bean定义信息
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole()); proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
} // Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
} // The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false); // Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);//这里将原始的bean定义信息注册到了spring容器,而bean的名称是scopedTarget.myMath // Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases()); //这里将代理bean定义信息返回,bean的名称是原始的beanName,myMath,该beanHodler返回去后,会被注册到spring
}
总结:
一个被Scope注解标注的类,如果scope的proxyMode不是no 或者defualt,那么会在spring创建2个bean,一个是代理bean,类型为ScopedProxyFactoryBean.class,一个是原始的bean:
这里我们的MyMath类,生成了2个beanDefinition,一个是代理的beanDefinition,beanName为myMath,一个是原始的beanDefinition,beanName为scopedTarget.myMath;
下面我们要分析ScopedProxyFactoryBean的创建过程了,我们知道XXFactoryBean会有一个getObject()方法返回XX代理对象:先看ScopedProxyFactoryBean继承体系
通过继承图,我们知道,ScopedProxyFactoryBean实现了BeanFactoryAware接口,因此在ScopedProxyFactoryBean的创建过程中,会回调setBeanFactory(BeanFactory beanFactory),所以我们debug在该方法:
我们详细看看该方法:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
}
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory; this.scopedTargetSource.setBeanFactory(beanFactory); ProxyFactory pf = new ProxyFactory(); //创建代理工厂
pf.copyFrom(this);
pf.setTargetSource(this.scopedTargetSource); Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
Class<?> beanType = beanFactory.getType(this.targetBeanName);//获取被代理类
if (beanType == null) {
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
"': Target type could not be determined at the time of proxy creation.");
}
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader())); //获取被代理类的所有实现的接口
} // Add an introduction that implements only the methods on ScopedObject.
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));//这里添加了一个增强器,在执行目标方法时,会拦截 // Add the AopInfrastructureBean marker to indicate that the scoped proxy
// itself is not subject to auto-proxying! Only its target bean is.
pf.addInterface(AopInfrastructureBean.class); this.proxy = pf.getProxy(cbf.getBeanClassLoader());//创建代理对象
}
创建代理对象过程,跟之前aop源码分析一和源码分析二的时侯分析的一样了,这里不重复了
spring aop 源码分析(三) @Scope注解创建代理对象的更多相关文章
- 5.2 spring5源码--spring AOP源码分析三---切面源码分析
一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
- Spring AOP 源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- Spring Ioc源码分析系列--@Autowired注解的实现原理
Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...
随机推荐
- Kubernetes入门(一)——Kubernetes v1.18.5 安装部署
Kubernetes的安装有两种方式:一是使用各个厂商封装的Kubernetes发行版,优点是可以一键安装部署,操作简单,缺点也很明显,若安装过程中某一步骤出现问题,很难定位处理:二是使用官方提供的k ...
- CSS 选择器及优先级
CSS 选择器及优先级 1.根据权值计算 div .class1 #people的权值等于1+10+100=111 .class2 li #age的权值等于10+1+100=111 2.权值相同,那么 ...
- SpringCloud Alibaba Nacos 服务治理中心
目录 一.什么是Nacos? 二.Nacos能干吗? 三.Nacos关键特性 四.Nacos中的基本概念 五.如何安装部署Nacos? 六.Nacos数据持久化 一.什么是Nacos? 英文全称Dyn ...
- [NOIP2018]保卫王国 题解
NOIP2018提高组D2T3 ddp虽然好想,但是码量有点大(其实是我不会),因此本文用倍增优化树形DP来解决本题. 题意分析 给一棵树染色,每个节点染色需要一定的花费,要求相邻两个节点至少要有一个 ...
- 现在的市场对 C++ 的需求大吗?
分享 大师助手 先说结论:需求还是很大,但是没有什么初级程序员能干的岗位. 游戏引擎,存储,推荐引擎,infra,各种各样的性能敏感场景.这些都是C++的刚需场景,别的语言基本替代不了的.除了pin ...
- 滑动窗口(Sliding Window)技巧总结
什么是滑动窗口(Sliding Window) The Sliding Problem contains a sliding window which is a sub – list that run ...
- Nodejs模块:fs
/** * @description fs模块常用api */ // fs所有的文件操作都是异步IO,如果要以同步的方式去调用,都会加一个在原同步api的基础上加Sync // 同步的方式会在最后传入 ...
- 要不是真的喜欢学技术,谁会来用Python爬小姐姐啊
养成习惯,先赞后看!!!不用于任何商业价值,只是自己娱乐.否则 爬虫爬的好,牢饭吃到饱.这是我们这次爬取的网址:https://www.vmgirls.com/ 很多人学习python,不知道从何学起 ...
- eclipse 设置默认编码为Utf-8 详细教程。
需要设置的几处地方为: Window->Preferences->General ->Content Type->Text->JSP 最下面设置为UTF-8 Window ...
- Springboot整合Spring Cloud Kubernetes读取ConfigMap,支持自动刷新配置
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Docker & Kubernetes相关文章:容器技术 之前介绍了Spring Cloud Config的用法,但 ...