Spring5 底层原理 工厂后处理器模拟实现-@Bean(黑马)

演示3 - 模拟解析 @Bean

其过程就是把 @Bean 标注的方法变为BeanDefinition 最终加入到容器

  • @Configuration 标注的类相当于一个工厂
  • 里面的 @Bean 其实就充当了一个共工厂方法
  • 所以在创建的时候我们用的是工厂方法的模式来创建BeanDeification

步骤如下

1. 拿到我们目标类中的注解信息类信息等

第一二行代码与组件扫描类似不在过多解释,这里我们简单些,把路径路径写死了(注意修改为自己类的路径)
在这里插入图片描述
第三行的意思为获取带有@Bean注解的方法,我们解读一下代码:

reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

  • 根据reader获取与注解相关的元数据也就是 getAnnotationMetadata()
  • 在其基础上在获得被注解标注的方法(我们这里用的是工厂方法创建,所以获取方法详细可以自行查看Config类)
  • 被哪个注解标注呢,getAnnotatedMethods()参数里给出Bean.class.getName(),被@Bean标注的。

实现代码如下:
在这里插入图片描述
打印查看一下结果(发现被@Bean注解标注的方法都被找了出来):
在这里插入图片描述

2.根据找的的方法信息创建BeanDefinition

比先前组件扫描创建BeanDefinition

  • 少了在创建BeanDefinition指定的名字
  • 多了builder.setFactoryMethodOnBean(method.getMethodName(), “config”); 因为要先创建工厂,工厂方法才能调用

我们用的是工厂方法创捷,类名不用指定,因为没用,我们创建的对象与例如工厂方法config的类名无关

对于上图(参数需要自动装配)我们需要稍微特殊处理不然会报错,需要指定自动装配(对于构造方法,工厂方法参数自动装配模式选择 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR
在这里插入图片描述

代码实现如下:
在这里插入图片描述

4.演示一下@Bean属性的解析过程(这里演示设置初始化方法名字)

  • 先拿到@Bean属性

String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get(“initMethod”).toString();

  • 在判断一下哪个@Bean有配置,有的话就把修改初始化方法名字改一下
    在这里插入图片描述
    最后注册到容器中即可:
    在这里插入图片描述
    在这里插入图片描述

代码:

A05Bean

package com.itheima.a05;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;
import java.util.Set;

/*
    BeanFactory 后处理器的作用
 */

// 研究 @Bean 标注后被创建流程

public class A05Bean {
    private static final Logger log = LoggerFactory.getLogger(A05Bean.class);

    public static void main(String[] args) throws IOException {

        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        // BeanFactory 后处理器
//        context.registerBean(ConfigurationClassPostProcessor.class); // 可以解析 @ComponentScan @Bean @Import @ImportResoure
//        context.registerBean(MapperScannerConfigurer.class, (BeanDefinition bd) -> {
//            bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
//        } ); // @MapperScanner 里面也是用这个类

        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
        Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

        for (MethodMetadata method : methods) {
            System.out.println(method);
            // config 中  @Bean(initMethod = "init")
            String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
            // 我们用的是工厂方法创捷,类名不用指定,因为没用,我们创建的对象与例如工厂方法config的类名无关
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            // 定义了一个一些config的工厂方法
            builder.setFactoryMethodOnBean(method.getMethodName(), "config");
            AbstractBeanDefinition bd = builder.getBeanDefinition();
            // 指定自动装配模式,解决sqlSessionFactoryBean出错(可以尝试注释这行代码运行试一试)
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            if (initMethod.length() > 0) {
                builder.setInitMethodName(initMethod);
            }

            context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(), bd);
        }



        // ⬇️初始化容器
        context.refresh();


        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }



        // ⬇️销毁容器
        context.close();

        /*
            学到了什么
                a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
                b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义
         */
    }
}

Config

package com.itheima.a05;

import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.a05.mapper.Mapper1;
import com.itheima.a05.mapper.Mapper2;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @Configuration 标注的类相当于一个工厂
 * 里面的 @Bean 其实就充当了一个共工厂方法
 *  所以在创建的时候我们用的是工厂方法的模式来创建BeanDeification
 */

@Configuration
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }


}

结果与上面相同;

收获

  1. 进一步熟悉注解元数据(AnnotationMetadata)获取方法上注解信息
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aholic 冲冲冲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值