黑马spring高级49讲-第一章 容器与Bean

275 阅读8分钟

黑马spring高级49讲学习笔记

视频教程地址:www.bilibili.com/video/BV1P4…

第一章 容器与Bean

第一讲 容器接口

  • BeanFactory 接口,典型功能有:

    • getBean
  • ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:

    • 国际化
    • 通配符方式获取一组 Resource 资源
    • 整合 Environment 环境(能通过它获取各种来源的配置信息)
    • 事件发布与监听,实现组件之间的解耦

BeanFactoryApplicationContext 的区别

测试代码:

@SpringBootApplication
public class A01Application {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
 
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
        System.out.println(context);
    }
}

1、到底什么是BeanFactory

  • 它是ApplicationContext的父接口

    查看ConfigurableApplicationContext类图,可见BeanFactoryApplicationContext的一个父接口。

    image-20220823082848607.png

  • 它才是 Spring 的核心容器, 主要的ApplicationContext实现都【组合】了它的功能,【组合】是指ApplicationContext的一个重要成员变量就是BeanFactory

    通过打印context,发现返回的是ConfigurableApplicationContext类的具体实现AnnotationConfigServletWebServerApplicationContext,通过类图,找到该类间接继承的类GenericApplicationContext,可以发现beanFactory为该类的成员变量。

    image-20220823091301435.png

BeanFactory功能

BeanFactory接口定义:

image.png

通过debug,可以发现ConfigurableApplicationContextgetBeanFactory方法返回的是DefaultListableBeanFactory,打印该类的类图如下:

image.png

DefaultListableBeanFactory类的具体实现后面再分析,跳转到父类DefaultSingletonBeanRegistry,可以发现私有变量singletonObjects,它是一个Map结构,key为bean的名称,value是bean的实例对象。

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

通过反射输出该map的元素

Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
        .forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));

总结:

  • 表面上只有 getBean
  • 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供

ApplicationContext功能

继续查看ConfigurableApplicationContext类图,可以看到ApplicationContext继承了EnvironmentCapable,MessageSource,ApplicationEventPublisher,ResourcePatternResolver四个接口,它的扩展功能主要体现在这四个接口上。

  • MessageSource: 国际化功能,支持多种语言
  • ResourcePatternResolver: 通配符匹配资源
  • ApplicationEventPublisher: 发布事件对象
  • EnvironmentCapable: 读取系统环境变量,比如*.properties*.application.yml等配置文件中的环境变量

image.png

1、MessageSource

image.png

演示:

resources目录下创建四个文件messages.propertesmessages_en.propertiesmessages_ja.propertiesmessages_zh.properties,在messages.propertes文件写通用的翻译(可不写),在messages_en.propertiesmessages_ja.propertiesmessages_zh.properties文件分别填入hi=hellohi=こんにちはhi=你好

执行代码:

System.out.println(context.getMessage("hi", null, Locale.CHINESE));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.JAPANESE));

打印结果:

你好
hello
こんにちは

2、ResourcePatternResolver

image.png

演示:

获取类路径下的资源文件

Resource[] resources = context.getResources("classpath:application.properties");
for (Resource resource : resources) {
    System.out.println(resource);
}

打印结果:

class path resource [application.properties]

3、EnvironmentCapable

演示:

读取环境变量

System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));

打印结果:

D:\developer\java
8080

4、ApplicationEventPublisher

image.png

演示:

定义事件

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

发布事件

@Autowired
private ApplicationEventPublisher context;

public void register() {
    log.debug("用户注册");
    context.publishEvent(new UserRegisteredEvent(this));
}

定义监听器

@EventListener
public void aaa(UserRegisteredEvent event) {
    log.debug("{}", event);
    log.debug("发送短信");
}

第二讲 容器实现

BeanFactory实现

DefaultListableBeanFactoryBeanFactory重要的实现类,以该类为例进行学习

public class TestBeanFactory {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class, scope, 初始化, 销毁)
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .getBeanDefinition();
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 给 BeanFactory 添加一些常用的后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory 后处理器主要功能,补充一些 bean 定义
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(
                beanFactoryPostProcessor -> {
                    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
                });

        //Bean 后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource ...
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                .sorted(beanFactory.getDependencyComparator())
                .forEach(beanPostProcessor -> {
                    System.out.println(">>>>>>" + beanPostProcessor);
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });

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

        beanFactory.preInstantiateSingletons(); // 预加载所有单例
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//        System.out.println(beanFactory.getBean(Bean1.class).getBean2());
        System.out.println(beanFactory.getBean(Bean1.class).getInter());

        /*
            学到了什么:
            a. beanFactory 不会做的事
                   1. 不会主动调用 BeanFactory 后处理器
                   2. 不会主动添加 Bean 后处理器
                   3. 不会主动初始化单例
                   4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
            b. bean 后处理器会有排序的逻辑
         */


    }

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

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

        @Bean
        public Bean3 bean3() {
            return new Bean3();
        }

        @Bean
        public Bean4 bean4() {
            return new Bean4();
        }
    }

    interface Inter {

    }

    static class Bean3 implements Inter {

    }

    static class Bean4 implements Inter {

    }

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

        public Bean1() {
            log.debug("构造 Bean1()");
        }

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {
            return bean2;
        }

        @Autowired
        @Resource(name = "bean4")
        private Inter bean3;

        public Inter getInter() {
            return bean3;
        }
    }

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

        public Bean2() {
            log.debug("构造 Bean2()");
        }
    }
}

原始的BeanFactory并没有解析@Configuration@Bean等注解的功能,需要添加BeanFactory后处理器并运行postProcessBeanFactory方法来实现扩展功能,以及添加Bean后处理器,来针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource...

总结:

a. beanFactory 不会做的事

1. 不会主动调用 BeanFactory 后处理器

2. 不会主动添加 Bean 后处理器

3. 不会主动初始化单例

4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }

b. bean 后处理器会有排序的逻辑

order越小,处理器优先级越高

ApplicationContext实现

1、ClassPathXmlApplicationContext,基于 classpath 下 xml 格式的配置文件来创建;

// 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("a02.xml");

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

    System.out.println(context.getBean(Bean2.class).getBean1());
}

2、FileSystemXmlApplicationContext,基于磁盘路径下 xml 格式的配置文件来创建;

private static void testFileSystemXmlApplicationContext() {
    FileSystemXmlApplicationContext context =
            new FileSystemXmlApplicationContext(
                    "src\main\resources\a02.xml");
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

    System.out.println(context.getBean(Bean2.class).getBean1());
}

3、AnnotationConfigApplicationContext,基于 java 配置类来创建

// 较为经典的容器, 基于 java 配置类来创建
private static void testAnnotationConfigApplicationContext() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(Config.class);

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

    System.out.println(context.getBean(Bean2.class).getBean1());
}

4、AnnotationConfigServletWebServerApplicationContext,基于 java 配置类来创建, 用于 web 环境

// 较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
    AnnotationConfigServletWebServerApplicationContext context =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

配置类案例:

@Configuration
static class WebConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean("/hello")
    public Controller controller1() {
        return (request, response) -> {
            response.getWriter().print("hello");
            return null;
        };
    }
}

第三讲 Bean的生命周期

Spring bean 生命周期各个阶段

测试类代码:

@Component
public class LifeCycleBean {
    private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);

    public LifeCycleBean() {
        log.debug("构造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}") String home){
        log.debug("依赖注入:{}",home);
    }

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

    @PreDestroy
    public void destory(){
        log.debug("销毁");
    }
}

运行SpringBoot启动类,从打印日志可以得到Spring bean的生命周期:

  • 实例化
  • 依赖注入
  • 初始化
  • 销毁

前面讲到Bean后处理器是针对bean的生命周期的各个阶段提供扩展功能;

测试代码:

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

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

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}

打印结果:

image.png

模板方法

Bean后处理器的功能是通过模板方法模式来对bean的生命周期进行扩展。

这里通过一个简单样例来演示Bean后处理器对依赖注入阶段的扩展:

public static void main(String[] args) {
    MyBeanFactory beanFactory = new MyBeanFactory();
    beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
    beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
    beanFactory.getBean();
}

// 模板方法  Template Method Pattern
static class MyBeanFactory {
    public Object getBean() {
        Object bean = new Object();
        System.out.println("构造 " + bean);
        System.out.println("依赖注入 " + bean); // @Autowired, @Resource
        for (BeanPostProcessor processor : processors) {
            processor.inject(bean);
        }
        System.out.println("初始化 " + bean);
        return bean;
    }

    private List<BeanPostProcessor> processors = new ArrayList<>();

    public void addBeanPostProcessor(BeanPostProcessor processor) {
        processors.add(processor);
    }
}

static interface BeanPostProcessor {    public void inject(Object bean); // 对依赖注入阶段的扩展
}

第四讲 Bean后处理器

  • Bean后处理器的作用:为Bean生命周期各个阶段提供扩展

常见bean后处理器

测试类代码

public static void main(String[] args) {
    // GenericApplicationContext 是一个【干净】的容器
    GenericApplicationContext context = new GenericApplicationContext();

    // 用原始方法注册三个 bean
    context.registerBean("bean1", Bean1.class);
    context.registerBean("bean2", Bean2.class);
    context.registerBean("bean3", Bean3.class);

    // 初始化容器
    context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

    // 销毁容器
    context.close();
}

Bean1:

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

    private Bean2 bean2;

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

//    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
    }

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

    @PreDestroy
    public void destroy() {
        log.debug("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + ''' +
               '}';
    }
}

Bean2和Bean3没有添加任何属性和方法;

启动main方法,控制台没有打印Bean1的日志信息,说明注解没有生效;

1、注册AutowiredAnnotationBeanPostProcessor后处理器

AutowiredAnnotationBeanPostProcessor后处理器用于解析@Autowired@Value注解;

直接启动后会报错,因为默认解析器不能解析值注入,需要设置为ContextAnnotationAutowireCandidateResolver

context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value

2、注册CommonAnnotationBeanPostProcessor后处理器

CommonAnnotationBeanPostProcessor后处理器用于解析@Resource@PostConstruct@PreDestroy注解

context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

3、注册ConfigurationPropertiesBindingPostProcessor后处理器

注册ConfigurationPropertiesBindingPostProcessor后处理器用于解析@ConfigurationProperties注解;

ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + ''' +
               ", version='" + version + ''' +
               '}';
    }
}

AutowiredAnnotationBeanPostProcessor运行分析

测试代码:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);

Bean1 bean1 = new Bean1();
System.out.println(bean1);
processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
System.out.println(bean1);

上述代码使用AutowiredAnnotationBeanPostProcessor后处理器对Bean1中的注解进行解析,完成依赖注入;

postProcessProperties方法:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

通过解读源码发现,在postProcessProperties方法中,先执行了findAutowiringMetadata私有方法,该方法用于获取当前bean内所有加了@Value@Autowired的成员变量以及方法参数信息,返回InjectionMetadata对象。然后调用该类的inject方法来进行依赖注入。

可以通过反射调用查看依赖注入的执行情况:

Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
System.out.println(metadata);

// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);

inject方法内部,是如何按照类型查找对应的值?

下述代码演示了在执行inject方法时,内部根据属性类型和方法参数类型如何获取对应值的方式:

// 3. 如何按类型查找值
Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o);

Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 =
        new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o1);

Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o2);

第五讲 常见工厂后处理器

演示代码:未注册工厂后处理器

public static void main(String[] args) throws IOException {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);

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

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

    // 销毁容器
    context.close();
}

Config类:

@Configuration
@ComponentScan("com.itheima.a05.component")
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/mydb");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

1、ConfigurationClassPostProcessor工厂后处理器

该处理器用于解析@ComponentScan@Bean@Import@ImportResource注解

演示代码:

context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource

2、MapperScannerConfigurer工厂后处理器

该处理器用于Spring整合Mybatis时,解析@MapperScanner注解

context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
    bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
});

下面将分析上述两个Bean工厂后处理器是如何解析注解,生成BeanDefinition,以及注册到BeanFactory的;

1、模拟实现-组件扫描

第一步:根据@ComponentScan扫描路径,查找类文件,找到哪些类加了@Component或派生的注解;

第二步:对注解了@Comonent或其派生注解的类,创建BeanDefinition,并注册到BeanFactory

public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*";
                    System.out.println(path);
                    //根据classpath路径获取资源文件
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
    //                    System.out.println(resource);
                        //分析class文件元数据
                        MetadataReader reader = factory.getMetadataReader(resource);
                        System.out.println("类名:" + reader.getClassMetadata().getClassName());
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        System.out.println("是否加了@Component注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                        System.out.println("是否加了@Component派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));

                        //找到注解了Component或者派生注解的类,创建BeanDefinition
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                                || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            //生成bean name
                            if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
                                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
                                String beanName = generator.generateBeanName(beanDefinition, beanFactory);
                                beanFactory.registerBeanDefinition(beanName, beanDefinition);
                            }
                        }
                    }

                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2、模拟实现—@Bean

第一步:获取类的元数据信息,找到指定类中注解了@Bean的方法;

第二步:使用BeanDefinitionBuilder生成BeanDefinition,设置工厂方法(factoryMethod)和工厂Bean(factoryBean)、自动装载模式;

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            //获取类的元数据信息
            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);
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
                if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
                    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
                    beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、模拟实现—@Mapper

第一步:根据指定资源路径,获取所有Mapper接口;

第二步:遍历Mappper接口,获取接口元数据信息,生成MapperFactoryBeanBeanDefinition,并注册到bean容器;

注:FactoryBean是一种特殊的bean,它是个工厂bean,通过实现getObject方法可以创建自定义的bean实例,而Mapper接口生成的bean需要通过MapperFactoryBean工厂bean去创建;

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:com/itheima/a05/mapper/**/*.class");
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            for (Resource resource : resources) {
                AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                if (classMetadata.isInterface()) {
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName())
                            .getBeanDefinition();
                    String beanName = generator.generateBeanName(beanDefinition2, registry);
                    registry.registerBeanDefinition(beanName, beanDefinition);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

第六讲 Aware接口

Aware接口和InitializationBean接口

Aware接口的作用:

用于注入一些与容器相关的信息,例如

  1. BeanNameAware 注入bean的名字
  2. BeanFactoryAware 注入BeanFactory容器
  3. ApplicationContextAware 注入ApplicationContext容器
  4. EmbeddedValueResolverAware ${}

InitializationBean接口的作用:

提供接口方法afterPropertiesSet,在bean初始化时执行;

演示代码:

public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {

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

    @Override
    public void setBeanName(String name) {
        log.debug("当前bean " + this + " 名字叫:" + name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.debug("当前bean " + this + " 容器是:" + applicationContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("当前bean " + this + " 初始化");
    }

    @Autowired
    public void aaa(ApplicationContext applicationContext) {
        log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
    }

    @PostConstruct
    public void init() {
        log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
    }
}

问题:为什么可以使用@Autowired注解和@PostContruct注解,还要使用Aware接口?

a. @Autowired注解的解析需要用到bean后处理器, 属于扩展功能

b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别

某些情况下, 扩展功能会失效, 而内置功能不会失效

@Autowire失效分析

Java配置类在添加了bean工厂后处理器后,你会发现用传统接口方式的注入和初始化仍然成功, 而@Autowired@PostConstruct的注入和初始化失败;

演示代码:

@Configuration
public class MyConfig1 {

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

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.debug("注入 ApplicationContext");
    }

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

    @Bean //  beanFactory 后处理器
    public BeanFactoryPostProcessor processor1() {
        return beanFactory -> {
            log.debug("执行 processor1");
        };
    }
}

运行后可以看到,@Autowired@PostConstruct注解的方法没有执行;

原因分析:

通过阅读分析ApplicationContext的refresh方法,其主要流程为:

sequenceDiagram 
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor
ac ->> +config : 3. 创建和初始化
bpp ->> config : 3.1 依赖注入扩展(如 @Value 和 @Autowired)
bpp ->> config : 3.2 初始化扩展(如 @PostConstruct)
ac ->> config : 3.3 执行 Aware 及 InitializingBean
config -->> -ac : 3.4 创建成功

当Java配置类包含BeanFactoryPostProcessor的时候,要创建其中的BeanFactoryPostProcessor必须提前创建Java配置类,而此时的BeanPostProcessor还未准备好,导致@Autowired等注解失效;

sequenceDiagram 
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> +config : 3. 创建和初始化
ac ->> config : 3.1 执行 Aware 及 InitializingBean
config -->> -ac : 3.2 创建成功

ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor

解决方法:

  • 用内置依赖注入和初始化取代扩展依赖注入和初始化
  • 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建

总结:

a. Aware接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory,ApplicationContext

b. InitializingBean 接口提供了一种【内置】的初始化手段

c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们

第七讲 初始化与销毁

Spring框架提供了多种初始化和销毁手段

初始化方式:(执行顺序1>2>3)

  1. 使用@PostConstruct注解方法;
  2. 继承InitializingBean接口,实现afterPropertiesSet方法;
  3. 使用@Bean(initMethod) 指定的初始化方法

销毁方式:(执行顺序1>2>3)

  1. 使用@PreDestroy注解方法;
  2. 继承DisposableBean接口,实现destory方法;
  3. @Bean(destroyMethod)指定的销毁方法;

第八讲 Scope

在当前版本的 Spring 和 Spring Boot 程序中,支持五种Scope

  • singleton,容器启动时创建(未设置延迟),容器关闭时销毁
  • prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
  • request,每次请求用到此 bean 时创建,请求结束时销毁
  • session,每个会话用到此 bean 时创建,会话结束时销毁
  • application,web 容器用到此 bean 时创建,容器停止时销毁

Scope失效解决 —— singleton注入其它scope失效

问题演示:singleton单例bean注入prototype原型bean

单例bean:E

@Component
public class E {
    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}

原型bean:F1

@Scope("prototype")
@Component
public class F1 {
}

测试:

AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(A08_1.class);

E e = context.getBean(E.class);
log.debug("{}", e.getF1().getClass());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());

输出:

- class com.itheima.a08.sub.F1 
- com.itheima.a08.sub.F1@2898ac89 
- com.itheima.a08.sub.F1@2898ac89

通过打印的f1对象发现,返回的是同一个对象,@Scope("prototype")没有生效;

原因分析:

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

graph LR

e1(e 创建)
e2(e set 注入 f)

f1(f 创建)

e1-->f1-->e2

解决方式:

1、使用@Lazy注解生成代理

@Lazy
@Autowired
private F1 f1;

输出日志:

- class com.itheima.a08.sub.F1$$EnhancerBySpringCGLIB$$21b324fd
- com.itheima.a08.sub.F1@7748410a 
- com.itheima.a08.sub.F1@37f1104d 

从输出日志可以看到,使用@Lazy后生成的是代理对象,代理对象虽然还是同一个,但当每次使用代理对象的任意方法时(如本例隐含调用了toString方法),才会从容器中查找 f 对象

graph LR

e1(e 创建)
e2(e set 注入 f代理)

f1(f 创建)
f2(f 创建)
f3(f 创建)

e1-->e2
e2--使用f方法-->f1
e2--使用f方法-->f2
e2--使用f方法-->f3

2、使用@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

输出日志:

- class com.itheima.a08.sub.F2$$EnhancerBySpringCGLIB$$a7e65ca8 
- com.itheima.a08.sub.F2@574b560f 
- com.itheima.a08.sub.F2@ba54932 

由输出结果可知,同样是使用代理方式解决

3、使用ObjectFactory

@Autowired
private ObjectFactory<F3> f3;

4、使用ApplicationContext

@Autowired
private ApplicationContext context;

public F4 getF4() {
    return context.getBean(F4.class);
}