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看到的是代理对象,而打印出来的不是代理对象?
- Spring对A对象的B字段赋值的时候,实际上是返回的是一个代理对象。
- 而在打印一个对象的时候,会打印这个对象的toString方法。
- 此时会进入拦截器。而拦截器中,会回调代理对象的getTarget方法。
- getTarget方法中会通过beanFactory获取B,但是B是prototype,不会将创建好的bean保存到singletonObjects中,所以每次都会创建一个新的bean。