【手撕Spring源码】一些值得注意的Spring底层细节

这篇具有很好参考价值的文章主要介绍了【手撕Spring源码】一些值得注意的Spring底层细节。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

FactoryBean

FactoryBean是一个Factory对象,用于生成其他bean示例。当一个bean实现FactoryBean接口后,Spring容器调用其getObject方法返回该工厂所生成的bean,而不是该FactoryBean本身。但是工厂生成的产品对象只能说是一部分受到Spring容器的管理,我们来看看下面的例子:

我们把一个Bean1通过工厂Bean的方法纳入Spring管理,看看结果:

Bean1:

public class Bean1 implements BeanFactoryAware {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("setBean2({})", bean2);
        this.bean2 = bean2;
    }

    public Bean2 getBean2() {
        return bean2;
    }

    @PostConstruct
    public void init() {
        log.debug("init");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.debug("setBeanFactory({})", beanFactory);
    }
}

Bean1的工厂类:

@Component("bean1")
public class Bean1FactoryBean implements FactoryBean<Bean1> {

    private static final Logger log = LoggerFactory.getLogger(Bean1FactoryBean.class);

    // 决定了根据【类型】获取或依赖注入能否成功
    @Override
    public Class<?> getObjectType() {
        return Bean1.class;
    }

    // 决定了 getObject() 方法被调用一次还是多次
    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public Bean1 getObject() throws Exception {
        Bean1 bean1 = new Bean1();
        log.debug("create bean: {}", bean1);
        return bean1;
    }
}

Bean后处理器:

@Component
public class Bean1PostProcessor implements BeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(Bean1PostProcessor.class);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("bean1") && bean instanceof Bean1) {
            log.debug("before [{}] init", beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("bean1") && bean instanceof Bean1) {
            log.debug("after [{}] init", beanName);
        }
        return bean;
    }
}

测试类:

@ComponentScan
public class A43 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A43.class);

        Bean1 bean1 = (Bean1) context.getBean("bean1");
        Bean1 bean2 = (Bean1) context.getBean("bean1");
        Bean1 bean3 = (Bean1) context.getBean("bean1");
        
        //因为工厂isSingleton方法返回的true,所以是单例模式
        System.out.println(bean1);
        System.out.println(bean2);
        System.out.println(bean3);
		
		//如果想通过类型获取,那么工厂中的getObjectType方法就一定要返回正确的值
        System.out.println(context.getBean(Bean1.class));

		//两种获取工厂bean的方法
        System.out.println(context.getBean(Bean1FactoryBean.class));
        System.out.println(context.getBean("&bean1"));

        context.close();

}

结果:

【手撕Spring源码】一些值得注意的Spring底层细节
【手撕Spring源码】一些值得注意的Spring底层细节

总结💡:

  1. 它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 @Bean 已具备等价功能
  2. 使用上较为古怪, 一不留神就会用错
    1. 被 FactoryBean 创建的产品
      • 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走
      • 唯有后初始化的流程会走, 也就是产品可以被代理增强
      • 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
    2. 按名字去获取时, 拿到的是产品对象, 名字前面加 & 获取的是工厂对象。当然我们也可以通过工厂类型去拿工厂对象。

@Indexed 原理

【手撕Spring源码】一些值得注意的Spring底层细节

💡

  1. 在编译时就根据 @Indexed 生成 META-INF/spring.components 文件
  2. 扫描时(二选一)
    • 如果发现 META-INF/spring.components 存在, 以它为准加载 bean definition
    • 否则, 会遍历包下所有 class 资源 (包括 jar 内的)
  3. 解决的问题:在编译期就找到 @Component 组件,节省运行期间扫描 @Component 的时间

Spring代理的特点

我们看一段代码:

@Component
public class Bean1 {

    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    protected Bean2 bean2;

    protected boolean initialized;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("setBean2(Bean2 bean2)");
        this.bean2 = bean2;
    }

    @PostConstruct
    public void init() {
        log.debug("init");
        initialized = true;
    }

    public Bean2 getBean2() {
        log.debug("getBean2()");
        return bean2;
    }

    public boolean isInitialized() {
        log.debug("isInitialized()");
        return initialized;
    }
}

增强:

@Aspect
@Component
public class MyAspect {

    // 故意对所有方法增强
    @Before("execution(* com.itheima.a45.Bean1.*(..))")
    public void before() {
        System.out.println("before");
    }
}

测试类:
【手撕Spring源码】一些值得注意的Spring底层细节

【手撕Spring源码】一些值得注意的Spring底层细节

【手撕Spring源码】一些值得注意的Spring底层细节

从这里我们可以看出来依赖注入和初始化影响的是原始对象

我们再来看看代理与目标的成员变量情况:

    public static void showProxyAndTarget(Bean1 proxy) throws Exception {
        System.out.println(">>>>> 代理中的成员变量");
        System.out.println("\tinitialized=" + proxy.initialized);
        System.out.println("\tbean2=" + proxy.bean2);

        if (proxy instanceof Advised advised) {
            System.out.println(">>>>> 目标中的成员变量");
            Bean1 target = (Bean1) advised.getTargetSource().getTarget();
            System.out.println("\tinitialized=" + target.initialized);
            System.out.println("\tbean2=" + target.bean2);
        }
    }

【手撕Spring源码】一些值得注意的Spring底层细节

所以说代理与目标是两个对象,二者成员变量并不共用数据。我们平时是使用的方法去获取成员变量,方法虽然被增强,但其中还是会调用原方法,所以获取的就是目标对象中的成员变量。这也给我们一种提示我们想要走代理增强就必须走方法,不能走成员。

  1. spring 代理的设计特点

    • 依赖注入和初始化影响的是原始对象

      • 因此 cglib 不能用 MethodProxy.invokeSuper()
    • 代理与目标是两个对象,二者成员变量并不共用数据

  2. static 方法、final 方法、private 方法均无法增强

    • 进一步理解代理增强基于方法重写,这三种情况是无法被增强的

@Value 装配底层

  1. 查看需要的类型是否为 Optional,是,则进行封装(非延迟),否则向下走
  2. 查看需要的类型是否为 ObjectFactory 或 ObjectProvider,是,则进行封装(延迟),否则向下走
  3. 查看需要的类型(成员或参数)上是否用 @Lazy 修饰,是,则返回代理,否则向下走
  4. 解析 @Value 的值
    1. 如果需要的值是字符串,先解析 ${ },再解析 #{ }
    2. 不是字符串,需要用 TypeConverter 转换
  5. 看需要的类型是否为 Stream、Array、Collection、Map,是,则按集合处理,否则向下走
  6. 在 BeanFactory 的 resolvableDependencies 中找有没有类型合适的对象注入,没有向下走
  7. 在 BeanFactory 及父工厂中找类型匹配的 bean 进行筛选,筛选时会考虑 @Qualifier 及泛型
  8. 结果个数为 0 抛出 NoSuchBeanDefinitionException 异常
  9. 如果结果 > 1,再根据 @Primary 进行筛选
  10. 如果结果仍 > 1,再根据成员名或变量名进行筛选
  11. 结果仍 > 1,抛出 NoUniqueBeanDefinitionException 异常

示例代码:

@Configuration
@SuppressWarnings("all")
public class A46 {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);

//        test1(context, resolver, Bean1.class.getDeclaredField("home"));
//        test2(context, resolver, Bean1.class.getDeclaredField("age"));
//        test3(context, resolver, Bean2.class.getDeclaredField("bean3"));
        test3(context, resolver, Bean4.class.getDeclaredField("value"));
    }

    private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
        System.out.println(value.getClass());

        // 解析 #{} @bean3
        Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));

        // 类型转换
        Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, dd1.getDependencyType());
        System.out.println(result);
    }

    private static void test2(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
        System.out.println(value.getClass());
        Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dd1.getDependencyType());
        System.out.println(age.getClass());
    }

    private static void test1(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
    }

    public class Bean1 {
        @Value("${JAVA_HOME}")
        private String home;
        @Value("18")
        private int age;
    }

    public class Bean2 {
        @Value("#{@bean3}") // SpringEL       #{SpEL}
        private Bean3 bean3;
    }

    @Component("bean3")
    public class Bean3 {
    }

    static class Bean4 {
        @Value("#{'hello, ' + '${JAVA_HOME}'}")
        private String value;
    }
}

重点:

  • ${ }对应的解析方法:context.getEnvironment().resolvePlaceholders
  • #{ } 对应的解析方法:context.getBeanFactory().getBeanExpressionResolver().evaluate
  • ContextAnnotationAutowireCandidateResolver 作用之一,获取 @Value 的值

@Autowired 装配底层

收获💡

  1. @Autowired 本质上是根据成员变量或方法参数的类型进行装配
  2. 如果待装配类型是 Optional,需要根据 Optional 泛型找到 bean,再封装为 Optional 对象装配
  3. 如果待装配的类型是 ObjectFactory,需要根据 ObjectFactory 泛型创建 ObjectFactory 对象装配
    • 此方法可以延迟 bean 的获取
  4. 如果待装配的成员变量或方法参数上用 @Lazy 标注,会创建代理对象装配
    • 此方法可以延迟真实 bean 的获取
    • 被装配的代理不作为 bean
  5. 如果待装配类型是数组,需要获取数组元素类型,根据此类型找到多个 bean 进行装配
  6. 如果待装配类型是 Collection 或其子接口,需要获取 Collection 泛型,根据此类型找到多个 bean
  7. 如果待装配类型是 ApplicationContext 等特殊类型
    • 会在 BeanFactory 的 resolvableDependencies 成员按类型查找装配
    • resolvableDependencies 是 map 集合,key 是特殊类型,value 是其对应对象
    • 不能直接根据 key 进行查找,而是用 isAssignableFrom 逐一尝试右边类型是否可以被赋值给左边的 key 类型
  8. 如果待装配类型有泛型参数
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按泛型参数类型筛选
  9. 如果待装配类型有 @Qualifier
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按注解提供的 bean 名称筛选
  10. 有 @Primary 标注的 @Component 或 @Bean 的处理
  11. 与成员变量名或方法参数名同名 bean 的处理

事件监听器

事件监听器的两种方式

  1. 实现 ApplicationListener 接口
    • 根据接口泛型确定事件类型
  2. @EventListener 标注监听方法
    • 根据监听器方法参数确定事件类型
    • 解析时机:在 SmartInitializingSingleton(所有单例初始化完成后),解析每个单例 bean

示例代码:

实现 ApplicationListener 接口:

// 事件解耦例子
@Configuration
public class A48_1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_1.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher; // applicationContext
        public void doBusiness() {
            log.debug("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(SmsApplicationListener.class);
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(EmailApplicationListener.class);
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送邮件");
        }
    }

}

使用@EventListener:

@Configuration
public class A48_2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_2.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher; // applicationContext
        public void doBusiness() {
            log.debug("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Component
    static class SmsService {
        private static final Logger log = LoggerFactory.getLogger(SmsService.class);
        @EventListener
        public void listener(MyEvent myEvent) {
            log.debug("发送短信");
        }
    }

    @Component
    static class EmailService {
        private static final Logger log = LoggerFactory.getLogger(EmailService.class);
        @EventListener
        public void listener(MyEvent myEvent) {
            log.debug("发送邮件");
        }
    }
	
	//模拟多线程	

    @Bean
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        return executor;
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(executor);
        return multicaster;
    }

}

模拟事件发布器

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context, ThreadPoolTaskExecutor executor) {
        return new AbstractApplicationEventMulticaster() {
            private List<GenericApplicationListener> listeners = new ArrayList<>();

            // 收集监听器
            public void addApplicationListenerBean(String name) {
                ApplicationListener listener = context.getBean(name, ApplicationListener.class);
                System.out.println(listener);
                // 获取该监听器支持的事件类型
                ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();
                System.out.println(type);

                // 将原始的 listener 封装为支持事件类型检查的 listener
                GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                    // 是否支持某事件类型                真实的事件类型
                    public boolean supportsEventType(ResolvableType eventType) {
                        return type.isAssignableFrom(eventType);
                    }

                    public void onApplicationEvent(ApplicationEvent event) {
                        executor.submit(() -> listener.onApplicationEvent(event));
                    }
                };

                listeners.add(genericApplicationListener);
            }

            // 发布事件
            public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
                for (GenericApplicationListener listener : listeners) {
                    if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                        listener.onApplicationEvent(event);
                    }
                }
            }
        };
    }

为了只实现两个核心方法,我们创建了一个AbstractApplicationEventMulticaster对ApplicationEventMulticaster接口进行了空实现,并定义为抽象类:
【手撕Spring源码】一些值得注意的Spring底层细节

事件发布器模拟实现文章来源地址https://www.toymoban.com/news/detail-487808.html

  1. addApplicationListenerBean 负责收集容器中的监听器
    • 监听器会统一转换为 GenericApplicationListener 对象,以支持判断事件类型
  2. multicastEvent 遍历监听器集合,发布事件
    • 发布前先通过 GenericApplicationListener.supportsEventType 判断支持该事件类型才发事件
    • 可以利用线程池进行异步发事件优化
  3. 如果发送的事件对象不是 ApplicationEvent 类型,Spring 会把它包装为 PayloadApplicationEvent 并用泛型技术解析事件对象的原始类型

到了这里,关于【手撕Spring源码】一些值得注意的Spring底层细节的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring源码篇(你值得拥有)

    下面是本文章关于Spring底层原理的章节 类-》推断构造方法-》根据构造方法创建普通对象-》依赖注入(@Autowired等进行属性注入)-》初始化前(@PostConstruct)-初始化(InitializingBean)-》初始化后(AOP)-》代理对象(没有开启AOP就会把普通对象放入单例池)-》放入单例池-》Bean对象

    2024年02月02日
    浏览(38)
  • Spring手写模拟源码篇(你值得拥有)

    下面是本文章关于Spring底层原理的章节 类-》推断构造方法-》根据构造方法创建普通对象-》依赖注入(@Autowired等进行属性注入)-》初始化前(@PostConstruct)-初始化(InitializingBean)-》初始化后(AOP)-》代理对象(没有开启AOP就会把普通对象放入单例池)-》放入单例池-》Bean对象

    2024年02月01日
    浏览(38)
  • Spring Data Elasticsearch 一些异常报错、注意事项(1)

    记录一:批量更新数据saveAll 引入maven依赖  saveAll批量新增,如果数据存在则会更新数据 记录二:批量更新数据Script脚本更新字段 参考:Script query | Elasticsearch Guide [8.5] | Elastic 记录三:空字段查询处理 如果查询字段createTime在ES数据中不存在,直接用.must(QueryBuilders.rangeQuery(

    2024年02月11日
    浏览(48)
  • 【spring源码系列-01】spring底层源码整体概述

    Spring源码系列整体栏目 内容 链接地址 【一】spring源码整体概述 https://blog.csdn.net/zhenghuishengq/article/details/130940885 【二】通过refresh方法剖析IOC的整体流程 https://blog.csdn.net/zhenghuishengq/article/details/131003428 【三】xml配置文件启动spring时refresh的前置工作 https://blog.csdn.net/zhenghuishen

    2024年02月07日
    浏览(43)
  • Spring源码系列:初探底层,手写Spring

    在学习Spring框架源码时,记住一句话:源码并不难,只需要给你各种业务场景或者项目经理,你也能实现自己的Spring。虽然你的实现可能无法与开源团队相媲美,但是你肯定可以实现一个0.0.1版本。因此,初次阅读源码时,不要陷入太深的细节中。先了解大体逻辑,再仔细研

    2023年04月12日
    浏览(44)
  • 【Spring】Spring之事务底层源码解析

    能使用spring事务解决开发需求 了解spring事务是如何被spring管理的 了解spring事务底层原理实现,比如代理、事务传播机制等 配置数据源及事务管理器: 业务逻辑代码: 调用: 结果: 如上,就是Spring中事务的简单应用,接下来分析Spring事务的实现原理。 开启Spring事务本质上

    2024年02月14日
    浏览(40)
  • 【Spring】Spring之AOP底层源码解析

    理解Spring中AOP的实现原理 Spring中有关AOP功能的使用 什么是动态代理: 为 其他对象 提供一种 代理 以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。 如下一个service类,如果想不改变test方法代码的情况下,增加特定的逻辑,该怎么做呢? 此时就可以用

    2024年02月14日
    浏览(41)
  • Spring源码(二)Spring底层架构核心概念解析

    BeanDefinition表示 Bean定义 ,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如: class,表示Bean类型 scope,表示Bean作用域,单例或原型等 lazyInit:表示Bean是否是懒加载 initMethodName:表示Bean初始化时要执行的方法 destroyMethodName:表示Bean销毁时要执行的方法 在Spring中,我

    2024年02月15日
    浏览(35)
  • 【手撕数据结构】二分查找(好多细节)

    🌈键盘敲烂,年薪30万🌈 目录 普通版本的二分查找: right只负责控制边界(少了两次比较): 时间复杂度更稳定的版本: BSLeftmost: BSRightmost:   🏸细节1:循环判定条件是left = right ⭐细节2:mid = (left + right ) 1 原因见代码注释 改动1:while条件是left right 改动2:right = nums.len

    2024年02月05日
    浏览(48)
  • 深入分析Spring的IoC容器:从底层源码探索

    前言: 博主在最近的几次面试中,大中小厂都问到了Spring的ioc容器相关问题,这块知识确实是面试中的重点内容,因此结合所看的书籍,在这篇文章中总结下。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读,感谢大佬的关注。 如果文章有什么需要改进的地方欢迎

    2024年02月12日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包