控制台顺利地打印出了 我得到了一个苹果 ,说明我们的猜测是正确的。同理,我们通过配置@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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/650c3d5d2b57f43d95e123dbd621f53f.jpeg)
最后
分布式技术专题+面试解析+相关的手写和学习的笔记pdf
还有更多Java笔记分享如下:
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
s/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />
最后
分布式技术专题+面试解析+相关的手写和学习的笔记pdf
还有更多Java笔记分享如下:
[外链图片转存中…(img-PggaAgjn-1712514731642)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!