谈谈Spring中Lookup配置的使用与原理

控制台顺利地打印出了 我得到了一个苹果 ,说明我们的猜测是正确的。同理,我们通过配置@Lookup注解来验证下。

@Lookup注解

我们把上面的所有Bean加上@Component注解,最重要的是要在Fruit类的getFruit方法上加上@Lookup注解:

public abstract class Fruit {

// 抽象方法获取水果

@Lookup

protected abstract Fruit getFruit(String fruitName);

}

之后我们通过 AnnotationConfigApplicationContext 类来启动Spring容器验证下,不出意外也会打印出上面的一行中文 我得到了一个苹果 。那么Spring是如何成功获取到这个抽象类并把抽象类的方法替换掉的呢?答案就是应用cglib提供的强大功能。

SimpleInstantiationStrategy

在第一种Xml配置的方式下Spring首先解析Xml配置后会把lookup-method放入对应beanDefinition的 methodOverrides 成员变量中,随后在实例化这个beanDefinition的时候,它会判断是否有methodOverrides ,如果有,那么它就会调用 instantiateWithMethodInjection 方法,我们拿源码作证:

//在SimpleInstantiationStrategy类下

@Override

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {

// Don’t override the class with CGLIB if no overrides.

if (!bd.hasMethodOverrides()) {

//省略部分代码

}

else {

//todo 像lookup replace等 需要用cglib 代理来实例化

// Must generate CGLIB subclass.

return instantiateWithMethodInjection(bd, beanName, owner);

}

}

接下来就会通过CglibSubclassCreator来创建抽象类的子类了:

public Object instantiate(@Nullable Constructor<?> ctor, Object… args) {

//todo 根据beanDefinition类来创建子类 2021-1-14

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;

}

我们只要看里面最重要的一句代码就是:

factory.setCallbacks(new Callback[] {NoOp.INSTANCE,

new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),

new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});

它会加入 LookupOverrideMethodInterceptor 这个拦截器,拦截器必然会有最重要的一个拦截方法:

//todo 调用方法的时候才会进入这个intercept 2020-1-11

@Override

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())) {

return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :

this.owner.getBean(lo.getBeanName()));

}

else {

//如果没有beanName 那就根据方法返回类型来查找Bean

return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :

this.owner.getBean(method.getReturnType()));

}

}

到这里我们应该会豁然开朗,Spring在我们调用getFruit方法的时候实际上会拦截,随后就进入这个方法中执行相应的逻辑,最终就是通过beanFactory的getBean方法返回我们在Xml配置的bean。

AutowiredAnnotationBeanPostProcessor

那么@Lookup注解是什么时候把方法放入beanDefinition的methodOverrides中的呢?答案就是AutowiredAnnotationBeanPostProcessor 的 推断候选的构造函数阶段 。

Spring在第一次通过构造函数创建Bean实例的时候会去推断是否有合适的构造函数,如下代码所示:

@Override

@Nullable

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

throws BeanCreationException {

// Let’s check for lookup methods here…

if (!this.lookupMethodsChecked.contains(beanName)) {

try {

ReflectionUtils.doWithMethods(beanClass, 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 {

///todo 把找到的方法加入 MethodOverrides

RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);

mbd.getMethodOverrides().addOverride(override);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

分布式技术专题+面试解析+相关的手写和学习的笔记pdf

还有更多Java笔记分享如下:

image

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
s/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

最后

分布式技术专题+面试解析+相关的手写和学习的笔记pdf

还有更多Java笔记分享如下:

[外链图片转存中…(img-PggaAgjn-1712514731642)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值