当前位置: 首页 > article >正文

SpringBoot扩展篇:@Scope和@Lazy源码解析

SpringBoot扩展篇:@Scope和@Lazy源码解析

    • 1. 研究主题及Demo
    • 2. 注册BeanDefinition
    • 3. 初始化属性
      • 3.1 解决依赖注入
      • 3.2 创建代理 ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary
      • 3.3 代理拦截处理
      • 3.4 单例bean与原型bean创建的区别
    • 4. 总结

1. 研究主题及Demo

A class

@Component
public class A {

    @Lazy
    @Autowired
    public B b;

    public B getB() {
        return b;
    }
}

B class

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class B {
}

测试类

@SpringBootApplication
public class WebApplication{
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(WebApplication.class, args);
        A a = run.getBean(A.class);
        System.out.println(a.b);
        System.out.println(a.b);
        System.out.println(a.b);
    }
}

研究问题1:为什么打印三次b对象的地址值不一样,从源码角度分析Spring是如何实现的?

在这里插入图片描述

研究问题2:为什么会debug看到的是代理对象,而打印出来的不是代理对象?

2. 注册BeanDefinition

在Spring对@Component扫描的时候,会调用ClassPathBeanDefinitionScanner#doScan生成beandefinition对象,可参考:
SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析

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);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				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);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

此时,Spring会通过AnnotationScopeMetadataResolver#resolveScopeMetadata扫描class上的@Scope注解,并通过candidate.setScope(scopeMetadata.getScopeName())将scope保存到beanDefinition中。

@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
	ScopeMetadata metadata = new ScopeMetadata();
	if (definition instanceof AnnotatedBeanDefinition) {
		AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
				annDef.getMetadata(), this.scopeAnnotationType);
		if (attributes != null) {
			metadata.setScopeName(attributes.getString("value"));
			ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
			if (proxyMode == ScopedProxyMode.DEFAULT) {
				proxyMode = this.defaultProxyMode;
			}
			metadata.setScopedProxyMode(proxyMode);
		}
	}
	return metadata;
}

所以此时的B中的scope为prototype。
在这里插入图片描述
Spring默认为单例,所以此时A中的scope为singleton。

3. 初始化属性

3.1 解决依赖注入

想要更加完善的了解Spring属性值注入,可查看 SpringBoot扩展篇:Spring注入 @Autowired & @Resource

此时,A对象已经创建完毕,当对A对象的B字段赋值时,会调用 DefaultListableBeanFactory#resolveDependency 实现依赖注入。

@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
	if (Optional.class == descriptor.getDependencyType()) {
		return createOptionalDependency(descriptor, requestingBeanName);
	}
	else if (ObjectFactory.class == descriptor.getDependencyType() ||
			ObjectProvider.class == descriptor.getDependencyType()) {
		return new DependencyObjectProvider(descriptor, requestingBeanName);
	}
	else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
		return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
	}
	else {
		Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
				descriptor, requestingBeanName);
		if (result == null) {
			result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
		}
		return result;
	}
}

当getLazyResolutionProxyIfNecessary方法返回有值时,就会返回当前值,而当前值就是解析@Lazy注解,并对返回值进行了代理。

3.2 创建代理 ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary

@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
	return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}

protected boolean isLazy(DependencyDescriptor descriptor) {
	for (Annotation ann : descriptor.getAnnotations()) {
		Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
		if (lazy != null && lazy.value()) {
			return true;
		}
	}
	MethodParameter methodParam = descriptor.getMethodParameter();
	if (methodParam != null) {
		Method method = methodParam.getMethod();
		if (method == null || void.class == method.getReturnType()) {
			Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
			if (lazy != null && lazy.value()) {
				return true;
			}
		}
	}
	return false;
}

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
	Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
			"BeanFactory needs to be a DefaultListableBeanFactory");
	final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
	TargetSource ts = new TargetSource() {
		@Override
		public Class<?> getTargetClass() {
			return descriptor.getDependencyType();
		}
		@Override
		public boolean isStatic() {
			return false;
		}
		@Override
		public Object getTarget() {
			Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
			if (target == null) {
				Class<?> type = getTargetClass();
				if (Map.class == type) {
					return Collections.emptyMap();
				}
				else if (List.class == type) {
					return Collections.emptyList();
				}
				else if (Set.class == type || Collection.class == type) {
					return Collections.emptySet();
				}
				throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
						"Optional dependency not present for lazy injection point");
			}
			return target;
		}
		@Override
		public void releaseTarget(Object target) {
		}
	};
	ProxyFactory pf = new ProxyFactory();
	pf.setTargetSource(ts);
	Class<?> dependencyType = descriptor.getDependencyType();
	if (dependencyType.isInterface()) {
		pf.addInterface(dependencyType);
	}
	return pf.getProxy(beanFactory.getBeanClassLoader());
}

isLazy方法:判断是否需要懒加载。显然,此时A对象的B字段上面有@Lazy注解,返回的是true。
buildLazyResolutionProxy方法:创建ProxyFactory代理对象,并返回该代理对象。当该代理对象调用方法时,会回调getTarget() 方法,从而从beanFactory中获取B对象。但是此时,B对象是prototype类型,不会保存到单例池singletonObjects中,所以每次获取B对象的时候,都是创建,每次都是不同的对象。

3.3 代理拦截处理

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

private final AdvisedSupport advised;

public DynamicAdvisedInterceptor(AdvisedSupport advised) {
	this.advised = advised;
}

@Override
@Nullable
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) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
		// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// Check whether we only have one InvokerInterceptor: that is,
		// no real advice, but just reflective invocation of the target.
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// We can skip creating a MethodInvocation: just invoke the target directly.
			// Note that the final invoker must be an InvokerInterceptor, so we know
			// it does nothing but a reflective operation on the target, and no hot
			// swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			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) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

在代理拦截器中, 回调了target方法。

3.4 单例bean与原型bean创建的区别

AbstractBeanFactory#doGetBean

在这里插入图片描述
可以看到,单例bean创建调用了getSingleton方法,再从中回调createBean创建bean的,而原型模式是直接调用createBean创建bean的。

DefaultSingletonBeanRegistry#getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

在单例bean创建的最后,会调用addSingleton方法将创建好的bean放入到singletonObjects中,而原型模式创建的bean不会!

4. 总结

研究问题1:为什么打印三次b对象的地址值不一样,从源码角度分析Spring是如何实现的?
研究问题2:为什么会debug看到的是代理对象,而打印出来的不是代理对象?

  1. Spring对A对象的B字段赋值的时候,实际上是返回的是一个代理对象。
  2. 而在打印一个对象的时候,会打印这个对象的toString方法。
  3. 此时会进入拦截器。而拦截器中,会回调代理对象的getTarget方法。
  4. getTarget方法中会通过beanFactory获取B,但是B是prototype,不会将创建好的bean保存到singletonObjects中,所以每次都会创建一个新的bean。

http://www.kler.cn/a/533968.html

相关文章:

  • AlwaysOn 可用性组副本所在服务器以及该副本上数据库的各项状态信息
  • 【Linux网络编程】:URL(encode),HTTP协议,telnet工具
  • Got socket exception during request. It might be caused by SSL misconfiguration
  • 【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter3-语言基础
  • 说一下JVM管理的常见参数
  • 探索 paraphrase-MiniLM-L6-v2 模型在自然语言处理中的应用
  • Scala语言的人工智能
  • 搭建集成开发环境PyCharm
  • vue2-为啥data属性是一个函数而不是对象
  • 基于SpringBoot的在线远程考试系统的设计与实现(源码+SQL脚本+LW+部署讲解等)
  • [创业之路-276]:从燃油汽车到智能汽车:工业革命下的价值变迁
  • Nginx高并发性能优化
  • vue2-key的原理与作用
  • 开源安全一站式构建!开启企业开源治理新篇章
  • Java 中 LinkedList 的底层源码
  • 【后端开发】系统设计101——通信协议,数据库与缓存,架构模式,微服务架构,支付系统(36张图详解)
  • 在C#中使用DeepSeek API实现自然语言处理、文本分类、情感分析等
  • HTML语言的软件工程
  • flutter 专题四十七 Flutter 应用启动流程分析
  • 【力扣】240.搜索二维矩阵 II
  • 4 前端前置技术(上):AJAX技术、Axios技术(前端发送请求)
  • idea分析sql性能
  • win32汇编环境,对话框程序中自定义工具栏的使用示例
  • neo4j-在Linux中安装neo4j
  • 【华为OD-E卷 - 115 数组组成的最小数字 100分(python、java、c++、js、c)】
  • C# Action和 Func的用法