Spring Boot依赖注入之Lookup注解

当采用@Autowired注解对单例bean注依赖的原型bean时,会由于单例bean只会创建一次,导致依赖的原型bean也只会注入一次,@Lookup注解可以较为优雅的解决此类问题

1、Lookup注解的使用

假如有个原型bean定义如下

@Component
@Scope(DefaultListableBeanFactory.SCOPE_PROTOTYPE)
public class ProtoTypeBean {
}

另一个单例bean的func方法需要使用这个原型bean,如下

@Component
public class SingletonBean {

    public void func() {
        // ProtoTypeBean bean = ...
        // System.out.println(bean);
    }
}

要如何获取bean?肯定不能使用@Autowired注解,因为只会注入一次。SingletonBean可以拿到BeanFactory,直接从BeanFactory中获取,如下

@Component
public class SingletonBean implements BeanFactoryAware {
    
    private BeanFactory beanFactory;
    
    public void func() {
         ProtoTypeBean bean = beanFactory.getBean(ProtoTypeBean.class);
         System.out.println(bean);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

这种从BeanFactory获取bean的公共逻辑,也可以放到工具类中去,通过静态方法getBean获取Bean,如下

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    
    public static <T> T getBean(Class<T> clazz) {
        Assert.state(context != null, "can't invoke getBean before context set");
        return context.getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}

尽管SpringContextUtil是一个Component,完全可以把它当作工具调用静态方法,只要在ApplicationContextAware接口被调用之后就行
这种公共逻辑也可以使用@Lookup注解来实现,如下

@Component
public abstract class SingletonBean {

    public void func() {
         ProtoTypeBean bean = getProtoTypeBean();
         System.out.println(bean);
    }
    
    @Lookup
    public abstract ProtoTypeBean getProtoTypeBean();
}

这里把SingletonBean定义为抽象类并定义了抽象方法getProtoTypeBean,在getProtoTypeBean方法上标注了@Lookup,此时Spring会生成一个SingletonBean的子类,实现getProtoTypeBean方法,在getProtoTypeBean方法内从BeanFactory中取出ProtoTypeBean实例。由于Spring继承SingletonBean实现标注了@Lookup的方法,就要求

  • 类可继承(不能是final)
  • 方法子类可重写(public、protected)

类可以不是抽象的,方法也可以不是抽象方法,子类会重写该方法,这个方法的实现不会起作用。这个注解如下

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {

	String value() default "";
}

其中的value属性,可以指定获取bean的名字

  • 如果没有指定value,按照方法的返回类型获取bean
  • 如果指定value,则按照指定的beanName获取bean

2、Lookup注解实现原理分析

BeanFactory在实例化bean之前,会先解析这个类里面的@Lookup注解,实例化时会根据不同解析结果进行实例化,如果存在@Lookup注解则会通过cglib生成子类来实例化,否则直接调用构造函数进行实例化

a、@Lookup注解的解析

@Lookup注解的解析在AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors方法中,如下

	public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
			throws BeanCreationException {

		// Let's check for lookup methods here...
		if (!this.lookupMethodsChecked.contains(beanName)) {
			if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
				try {
					Class<?> targetClass = beanClass;
					do {
						ReflectionUtils.doWithLocalMethods(targetClass, method -> {
							Lookup lookup = method.getAnnotation(Lookup.class);
							if (lookup != null) {
								Assert.state(this.beanFactory != null, "No BeanFactory available");
								LookupOverride override = new LookupOverride(method, lookup.value());
								try {
									RootBeanDefinition mbd = (RootBeanDefinition)
											this.beanFactory.getMergedBeanDefinition(beanName);
									mbd.getMethodOverrides().addOverride(override);
								}
								catch (NoSuchBeanDefinitionException ex) {
									throw new BeanCreationException(beanName,
											"Cannot apply @Lookup to beans without corresponding bean definition");
								}
							}
						});
						targetClass = targetClass.getSuperclass();
					}
					while (targetClass != null && targetClass != Object.class);

				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
				}
			}
			this.lookupMethodsChecked.add(beanName);
		}
        
        // 其他逻辑忽略
    }

ReflectionUtils.doWithLocalMethods对这个类的每个方法进行处理,如果这个方法上存在@Lookup注解,则实例化一个LookupOverride对象,保存到Beandefinition的methodOverrides里,LookupOverride对象保存了这个Method和@Lookup注解指定的beanName
LookupOverride是MethodOverride的子类,MethodOverride代表被容器重写的方法,有两个子类:LookupOverride和ReplaceOverride。这里就只分析LookupOverride涉及到的逻辑

b、bean实例化

不管是通过无参构造器实例化还是有参构造器实例化,实例化逻辑都会走到这里:

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this)

getInstantiationStrategy获取到实例化策略,实例化策略是一个接口InstantiationStrategy,有两个实现SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。SimpleInstantiationStrategy简单通过构造器来实例化,CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy类,除了能够通过构造器实例化,还能够生成子类来实例化。一般情况下实例化策略就是CglibSubclassingInstantiationStrategy实例
instantiate方法判断是否有方法重写,有的话生成cglib子类重写方法,否则直接实例化

	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

instantiateWithMethodInjection方法通过CglibSubclassCreator来进行实例化,实际就是

	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		return instantiateWithMethodInjection(bd, beanName, owner, null);
	}

	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Constructor<?> ctor, Object... args) {

		// Must generate CGLIB subclass...
		return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
	}

简单分析下后续逻辑。CglibSubclassCreator#instantiate做了什么呢?

		public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
			Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
			Object instance;
			if (ctor == null) {
				instance = BeanUtils.instantiateClass(subclass);
			}
			else {
				try {
					Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
					instance = enhancedSubclassConstructor.newInstance(args);
				}
				catch (Exception ex) {
					throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
							"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
				}
			}
			// SPR-10785: set callbacks directly on the instance instead of in the
			// enhanced class (via the Enhancer) in order to avoid memory leaks.
			Factory factory = (Factory) instance;
			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
			return instance;
		}

instantiate就是创建了一个子类subclass,通过子类来实例化对象。具体看下createEnhancedSubclass的实现

		private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(beanDefinition.getBeanClass());
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			if (this.owner instanceof ConfigurableBeanFactory) {
				ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
				enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
			}
			enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
			enhancer.setCallbackTypes(CALLBACK_TYPES);
			return enhancer.createClass();
		}

对cglib的Enhance设置了一个MethodOverrideCallbackFilter跟CALLBACK_TYPES,cglib代理可以根据filter返回的index从CallbackTypes中选一个实现类。CALLBACK_TYPES如下

private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
		{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};

MethodOverrideCallbackFilter#accept会返回index,如下

		public int accept(Method method) {
			MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
			if (logger.isTraceEnabled()) {
				logger.trace("MethodOverride for " + method + ": " + methodOverride);
			}
			if (methodOverride == null) {
				return PASSTHROUGH;
			}
			else if (methodOverride instanceof LookupOverride) {
				return LOOKUP_OVERRIDE;
			}
			else if (methodOverride instanceof ReplaceOverride) {
				return METHOD_REPLACER;
			}
			throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
					methodOverride.getClass().getName());
		}

MethodOverrideCallbackFilter#accept根据Method查询一下是否有MethodOverride,没有的话返回PASSTHROUGH,即0,对应NoOp.class;如果是LookupOverride,返回LOOKUP_OVERRIDE,即1,对应LookupOverrideMethodInterceptor.class;如果是ReplaceOverride,返回METHOD_REPLACER,即2,对应ReplaceOverrideMethodInterceptor.class
看一下LookupOverrideMethodInterceptor#intercept方法的实现

		public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
			// Cast is safe, as CallbackFilter filters are used selectively.
			LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
			Assert.state(lo != null, "LookupOverride not found");
			Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
			if (StringUtils.hasText(lo.getBeanName())) {
				Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
						this.owner.getBean(lo.getBeanName()));
				// Detect package-protected NullBean instance through equals(null) check
				return (bean.equals(null) ? null : bean);
			}
			else {
				// Find target bean matching the (potentially generic) method return type
				ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);
				return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :
						this.owner.getBeanProvider(genericReturnType).getObject());
			}
		}

这里逻辑就很清晰了,根据method获取LookupOverride实例,看@Lookup注解是否配置类beanName

  • 如果配置了beanName:则通过beanName从beanFactory获取bean
  • 如果没有配置beanName,则解析方法的返回类型,根据返回类型获取bean
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值