[Spring ~必知必会] Bean 基础常识汇总

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: [Spring ~必知必会] Bean 基础常识汇总


环境

  • springboot2.7.18
  • jdk17
    :::

springboot 进入 debug 模式的三种方式

  1. java –jar xxx.jar --debug
  2. application.properties中设置 debug=true
  3. idea 启动上面设置 Run Configurations…VM arguments 中添加 –Ddebug

Bean 相关

到底什么是beanFactory

:::info

它是 ApplicationContext的父接口,是Spring的核心容器。

:::

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    boolean containsBean(String name);
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);
}

beanFactory能干啥

:::info

表面上只有 getBean(), 实际上控制反转,基本的依赖注入以及bean的生命周期等各种功能,都由它的实现类实现。

:::

ApplicationContext是什么

:::info

ApplicationContextbeanFactory的继承与扩展关系。

:::

ApplicationContext的功能比 BeanFactory多了什么

:::info

ApplicationContext 除了继承 BeanFactory 外,还继承了:

  • MessageSource:使其具备处理国际化资源的能力
  • ResourcePatternResolver:使其具备使用通配符进行资源匹配的能力
  • EnvironmentCapable:使其具备读取 Spring 环境信息、配置文件信息的能力
  • ApplicationEventPublisher:使其具备发布事件的能力 (事件的发布是堵塞同步的哟)
    :::
ConfigurableApplicationContext context = SpringApplication.run(ShowBeanApplication.class, args);
// 只加载类路径下面的文件
Resource[] resources = context.getResources("classpath:META-INF/spring.factories");
for (Resource resource : resources) {
    System.out.println(resource);
    System.out.println(resource.getURL());
}
System.out.println("************");
// 包含jar包的文件
Resource[] jarResources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource jarResource : jarResources) {
    System.out.println(jarResource);
}
ConfigurableEnvironment environment = context.getEnvironment();
//  获取系统环境变量
Map<String, Object> systemEnvironment = environment.getSystemEnvironment();
systemEnvironment.forEach((k,v)->{
    System.out.println("env >>>>>>>>>>>>> k="+k + "v="+ v);
});
// 获取配置文件的变量
System.out.println(environment.getProperty("server.port"));
// --- 事件
public class MyEvent extends ApplicationEvent implements Serializable {
    public MyEvent(Object source) {
        super(source);
    }
}
// --- 监听器
@Component
public class EVListener {
    @EventListener
    public void recv(MyEvent e) {
        System.out.println(("接受到事件: source=" + e.getSource() + "time=" + e.getTimestamp()));
    }
}
// --- 发布事件
ConfigurableApplicationContext context = SpringApplication.run(ShowBeanApplication.class, args);
context.publishEvent(new MyEvent(context));

容器的实现

BeanFactory的实现

ApplicationContext的实现

xml 配置

:::info

怎么通过 xml 配置 bean

:::

public class ApplicationContextTest {
    public static void main(String[] args) {
       testClassPathXmlApplicationContext();
       testFileSystemXmlApplicationContext();
    }
    /**
     * 最为经典 基于 classpath 下的xml配置文件来创建
     */
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    /**
     * 基于磁盘路径的 xml 配置文件来创建
     */
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("//Users/csjerry/project/java/easy_spring_mvc/learn_spring/show_bean/src/test/resources/b01.xml");
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    public static class Bean1 {
        Bean1() {
            System.out.println(">>>>>>>>>> 1");
        }
    }
    public static class Bean2 {
        private Bean1 bean1;
        public Bean2() {
            System.out.println(">>>>>>>>>>>> 2.");
        }
        public void setBean1(Bean1 bean1) {
            this.bean1 = bean1;
        }
        public Bean1 getBean1() {
            return bean1;
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="bean1" class="com.example.show_bean.ApplicationContextTest.Bean1"/>
  <bean id="bean2" class="com.example.show_bean.ApplicationContextTest.Bean2">
    <property name="bean1" ref="bean1"/>
  </bean>
</beans>

xml 创建 bean 的原理

通过XmlBeanDefinitionReaderloadBeanDefinitions实现。

public static void main(String[] args) {
        // xml 读取的原理
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println(">>>>>>>> 读取钱前");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(">>>>>>> 读取后");
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        int i = reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
//        int i = reader.loadBeanDefinitions(new FileSystemResource("//Users/csjerry/project/java/easy_spring_mvc/learn_spring/show_bean/src/test/resources/b01.xml"));
        System.out.println(">>>>>>>>>> 读取" + i + "bean");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

配置类配置

普通 bean

public class ApplicationContextTest {
        testAnnotationConfigApplicationContext();
    }
    /**
     * java 配置类来创建
     */
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    @Configuration
    public static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }
    public static class Bean1 {
        Bean1() {
            System.out.println(">>>>>>>>>> 1");
        }
    }
    public static class Bean2 {
        private Bean1 bean1;
        public Bean2() {
            System.out.println(">>>>>>>>>>>> 2.");
        }
        public void setBean1(Bean1 bean1) {
            this.bean1 = bean1;
        }
        public Bean1 getBean1() {
            return bean1;
        }
    }
}

用于 web 环境 (用错类debug了半天,差点怀疑人生)

public class AnnotationConfigServletWebContext {
    public static void main(String[] args) {
//        public class AnnotationConfigServletWebServerApplicationContext
//              extends ServletWebServerApplicationContext
//                  implements AnnotationConfigRegistry
        AnnotationConfigServletWebServerApplicationContext context =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        // AnnotationConfigServletWebApplicationContext
        //          extends GenericWebApplicationContext
        //              implements AnnotationConfigRegistry
        // 注意这个类不会启动 tomcat
//        AnnotationConfigServletWebApplicationContext context = new AnnotationConfigServletWebApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>>>name="+name);
        }
//
    }
    static class WebConfig {
        // 内嵌 tomcat
        @Bean
        public ServletWebServerFactory servletWebServerFactory () {
            System.out.println(">>>>>>> tomcat");
            return new TomcatServletWebServerFactory();
        }
        // 路径派发
        @Bean
        public DispatcherServlet dispatcherServlet() {
            System.out.println(">>>>>>>> dispatch");
            return new DispatcherServlet();
        }
        // 注册 dispatch 到 tomcat
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            System.out.println(">>>>>>>>registrationBean");
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        // 使用的是 org.springframework.web.servlet.mvc.Controller
        @Bean("/hello")
        public Controller controller1() {
            System.out.println(">>>>>>>> controller");
            return ((request, response) -> {
                response.getWriter().println("hello");
                return null;
            });
        }
    }
}

Bean 的生命周期

实例化 -> 依赖注入 -> 销毁

用到模版方法 的设计模式

3.1 Bean 的常见的后处理器

测试代码

GenericApplicationContext直接继承 AbstractApplicationContext是一个干净的容器。

@Slf4j
@ToString
public class Bean1 {
    private Bean2 bean2;
    @Autowired
    public void setBean2(Bean2 bean2) {
        log.info("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }
    private Bean3 bean3;
    @Resource
    public void setBean3(Bean3 bean3) {
        log.info("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }
    private String home;
    @Autowired
    public void setHome(@Value("${JAVA_HOME:hello world'}") String home) {
        log.info("@Value 生效: {}", home);
        this.home = home;
    }
    @PostConstruct
    public void init() {
        log.info("@PostConstruct 生效");
    }
    @PreDestroy
    public void destroy() {
        log.info("@PreDestroy 生效");
    }
}
//-
package com.example.show_bean.a04;
public class Bean2 {
}
//- 
package com.example.show_bean.a04;
public class Bean3 {
}
//-
package com.example.show_bean.a04;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ToString
@ConfigurationProperties(prefix = "java")
public class Bean4 {
    private String home;
    private String version;
}
package com.example.show_bean.a04;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
public class A04Application {
    public static void main(String[] args) {
        // GenericApplicationContext 是一个干净的容器
        GenericApplicationContext context = new GenericApplicationContext();
//        // 解析值注入内容
//        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
//
//        // @Autowired @Value
//        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
//
//        // @Resource @PostConstruct @PreDestroy
//        context.registerBean(CommonAnnotationBeanPostProcessor.class);
//
//        // @ConfigurationProperties  获取环境变量信息
//        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
        // 用原始方式注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);
        // 初始化容器。执行 beanFactory 后置处理器,添加 bean 后置处理器,初始化所有单例
        context.refresh();
        System.out.println(context.getBean(Bean4.class));
        // 销毁容器
        context.close();
    }
}

此时,启动类运行,只会打印基础的信息

接下来,一步一步地打开后处理器的注释,查看处理器的作用

ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

:::info

注册 getDefaultListableBeanFactory处理器后,可以拿到系统的环境变量信息。

:::

context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

:::info

注册 AutowiredAnnotationBeanPostProcessor之后, autowired 生效, 但是值注入失败。

:::

context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

:::info

替换完 setAutowireCandidateResolver之后,值注入成功

:::

context.registerBean(CommonAnnotationBeanPostProcessor.class);

:::info

注册 CommonAnnotationBeanPostProcessor之后, @Resource @PostConstruct @PreDestroy 生效。

:::

总结

:::warning

通过前文可知

  • AutowiredAnnotationBeanPostProcessor 用于解析 @Autowired 和 @Value 注解。
  • CommonAnnotationBeanPostProcessor 解析 @Resource @PostConstruct @PreDestroy
  • getDefaultListableBeanFactory获取系统环境变量
  • setAutowireCandidateResolver配合 AutowiredAnnotationBeanPostProcessor 进行值注入。
    :::

3.2 工厂后处理器的模拟实现

:::warning

涉及后处理器 @ComponentScan, @Bean

:::

@Slf4j
public class A05Application {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        ComponentScan scan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
        if(scan != null) {
            String[] strings = scan.basePackages();
            for (String s : strings) {
                System.out.println(">>>>> prev->: " + s);
                //-> classpath*:com/example/show_bean/**/*.class
                s = "classpath*:"+s.replace(".", "/") + "/**/*.class";
                System.out.println(">>>>> post->: "  + s);
                Resource[] resources = context.getResources(s);
                for (Resource resource : resources) {
                    MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    System.out.println("类名:" + className);
                    boolean b = metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
                    System.out.println("是否加了 @Component: " + b);
                    boolean b1 = metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
                    System.out.println("是否加了 @Component 派生注解: " + b1);
                }
            }
        }
        context.refresh();
    }
}
//- Config
@Component
@ComponentScan(basePackages = "com.example.show_bean.a05.component")
public class Config {
    public Config() {
        System.out.println(">>>>>>>> config init");
    }
}
//- bean2
@Component
public class bean2 {
    public bean2(){
        System.out.println(">>>>>>>>>" + bean2.class.getSimpleName() + "spring init");
    }
}
//- bean3
@Controller
public class bean3 {
    public bean3(){
        System.out.println(">>>>>>>>>" + bean3.class.getSimpleName() + "spring inti ");
    }
}
//- bean4
public class bean4 {
    public bean4(){
        System.out.println(">>>>>>>>>" + bean4.class.getSimpleName() + "init");
    }
}

运行结果

:::warning

修改 Application 代码使之扫描组件注册成 bean

:::

package com.example.show_bean.a05;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
public class A05Application {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        ComponentScan scan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
        // 根据注解生成 bean 名字
        AnnotationBeanNameGenerator annotationBeanNameGenerator = new AnnotationBeanNameGenerator();
        DefaultListableBeanFactory defaultListableBeanFactory = context.getDefaultListableBeanFactory();
        if(scan != null) {
            String[] strings = scan.basePackages();
            for (String s : strings) {
                System.out.println(">>>>> prev->: " + s);
                //-> classpath*:com/example/show_bean/**/*.class
                s = "classpath*:"+s.replace(".", "/") + "/**/*.class";
                System.out.println(">>>>> post->: "  + s);
                Resource[] resources = context.getResources(s);
                for (Resource resource : resources) {
                    MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
//                    System.out.println("类名:" + className);
                    boolean b = metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
//                    System.out.println("是否加了 @Component: " + b);
                    boolean b1 = metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
//                    System.out.println("是否加了 @Component 派生注解: " + b1);
                    if(b || b1) {
                        // 加了 @Component 以及派生注解的 转换成 bean
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(className)
                            .getBeanDefinition();
                        String beanName = annotationBeanNameGenerator.generateBeanName(beanDefinition, defaultListableBeanFactory);
                        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                    }
                }
                for (String name : context.getBeanDefinitionNames()) {
                    System.out.println(">>>>>>>>>beanName="+name);
                }
            }
        }
        context.refresh();
    }
}

运行 bean2, bean3 注册成功, bean4没有注册,符合预期。

:::warning

bean 注册部分代码,抽取成一个后处理器, 修改启动类以及新添加后处理器类。

后处理器需要实现 BeanFactoryPostProcessor, 他会在 调用 refresh 时执行。

:::

@Slf4j
public class A05Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        // 注册后处理器
        context.registerBean(ComponentScanPostProcessor.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>beanName="+name);
        }
    }
}
//- 
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            ComponentScan scan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
            // 根据注解生成 bean 名字
            AnnotationBeanNameGenerator annotationBeanNameGenerator = new AnnotationBeanNameGenerator();
            // 获取资源文件
            PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
            if (scan != null) {
                String[] strings = scan.basePackages();
                for (String s : strings) {
                    System.out.println(">>>>> prev->: " + s);
                    //-> classpath*:com/example/show_bean/**/*.class
                    s = "classpath*:" + s.replace(".", "/") + "/**/*.class";
                    System.out.println(">>>>> post->: " + s);
                    Resource[] resources = patternResolver.getResources(s);
                    for (Resource resource : resources) {
                        MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
                        String className = metadataReader.getClassMetadata().getClassName();
//                    System.out.println("类名:" + className);
                        boolean b = metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
//                    System.out.println("是否加了 @Component: " + b);
                        boolean b1 = metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
//                    System.out.println("是否加了 @Component 派生注解: " + b1);
                        if (b || b1) {
                            // 加了 @Component 以及派生注解的 转换成 bean
                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(className)
                                .getBeanDefinition();
                            if (configurableListableBeanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory) {
                                String beanName = annotationBeanNameGenerator.generateBeanName(beanDefinition, defaultListableBeanFactory);
                                defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("注册 bean 失败" + e.getMessage());
        }
    }
}

:::warning

@Bean

:::

package com.example.show_bean.a05;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
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 org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.Set;
public class BeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
            MetadataReader reader = cachingMetadataReaderFactory.getMetadataReader(new ClassPathResource("com/example/show_bean/a05/Config.class"));
            Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : annotatedMethods) {
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
                definitionBuilder.setFactoryMethodOnBean(method.getMethodName(), "config");
                // 设置注入模式
                definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if(StringUtils.hasLength(initMethod)) {
                    definitionBuilder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = definitionBuilder.getBeanDefinition();
                if(configurableListableBeanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory) {
                    defaultListableBeanFactory.registerBeanDefinition(method.getMethodName(), bd);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

:::warning

@Mapper

:::

@Mapper
public interface Mapper1 {
}
//-
@Mapper
public interface Mapper2 {
}
//- MapperFactoryBean 注册
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
    MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
    factoryBean.setSqlSessionFactory(sqlSessionFactory);
    return factoryBean;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
    MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);
    factoryBean.setSqlSessionFactory(sqlSessionFactory);
    return factoryBean;
}

MapperFactoryBean 只能注册一个 bean, 抽象成一个后处理器进行扫描 批量 注册

package com.example.show_bean.a05;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.lang.NonNull;
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(@NonNull BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:com/example/show_bean/a05/mapper/**/*.class");
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            AnnotationBeanNameGenerator nameGenerator = new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                if (classMetadata.isInterface() && reader.getAnnotationMetadata().hasAnnotation(Mapper.class.getName())) {
                    // 是接口且有@Mapper
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                        .addConstructorArgValue(classMetadata.getClassName())
                        // 按照类型注入 SqlSessionFactory
                        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                        .getBeanDefinition();
                    // 生成一个新的 beanDefinitional 生成一个 beanName
                    AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder
                        .genericBeanDefinition(classMetadata.getClassName())
                        .getBeanDefinition();
                    String beanName = nameGenerator.generateBeanName(beanDefinition1, beanDefinitionRegistry);
                    beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
                }
            }
        }catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
    @Override
    public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
}

3.3 Aware 接口以及 InitializingBean

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

  • BeanNameAware注入 Bean的名字
  • BeanFactoryAware注入 BeanFactory容器
  • ApplicationContextAware注入 ApplicationContext容器
  • EmbeddedResolverAware注入 ${}
public class A06Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean", MyBean.class);
        context.refresh();
        context.close();
    }
}
//- 
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
    @Override
    public void setBeanName(String s) {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " 名字叫:" + s);
    }
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " applicationContext 容器是:" + applicationContext);
    }
    @Override
    public void afterPropertiesSet() {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " InitializingBean");
    }
}

运行结果

BeanFactoryAware注入 BeanFactory容器、ApplicationContextAware注入 ApplicationContext容器、EmbeddedResolverAware注入 ${}使用 @Autowired就能实现,为什么还要用 Aware接口呢?

@Autowire的解析需要 bean 后处理器,属于扩展功能,而 Aware接口属于内置功能,不需要任何扩展, Spring就能识别。

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

public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
    @Override
    public void setBeanName(String s) {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " 名字叫:" + s);
    }
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " applicationContext 容器是:" + applicationContext);
    }
    @Override
    public void afterPropertiesSet() {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " InitializingBean");
    }
    @Autowired
    public void set(ApplicationContext applicationContext) {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " @Autowired 注入的 applicationContext 容器是:" + applicationContext);
    }
    @PostConstruct
    public void init() {
        System.out.println("setBeanName" + this.getClass().getSimpleName() + " @PostConstruct InitializingBean");
    }
}

如果只是简单地添加 Autowired,@PostConstruct 在,空容器环境下并不会执行。需要添加相应的后处理器。

public class A06Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean", MyBean.class);
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.refresh();
        context.close();
    }
}

Autowired,@PostConstruct 正常执行以及注入。

3.4 AutoWired 以及 PostConstruct 失效的情况分析

public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myconfig", MyConfig1.class);
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();
        context.close();
    }
//-
@Slf4j
@Configuration
public class MyConfig1 {
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.info("注入 ApplicationContext");
    }
    @PostConstruct
    public void init() {
        log.info("初始化");
    }
}

运行之后正常打印。但是加入以下代码之后运行 Autowired , PostConstruct失效。

在 Config1 中添加了一个被 @Bean 注解标记的 processor1() 方法,用于向容器中添加 BeanFactoryPostProcessor。processor1() 方法成功生效,但 @Autowired 和 @PostConstruct 注解的解析失败了。

@Slf4j
@Configuration
public class MyConfig1 {
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.info("注入 ApplicationContext");
    }
    @PostConstruct
    public void init() {
        log.info("初始化");
    }
    @Bean
    public BeanFactoryPostProcessor processor1() {
        return processor -> log.info("执行 processor1");
    }
}

对于 context.refresh() 方法来说,它主要按照以下顺序干了三件事:

  1. 执行 BeanFactory 后置处理器;
  2. 添加 Bean 后置处理器;
  3. 创建和初始化单例对象。

比如当 Java 配置类不包括 BeanFactoryPostProcessor 时:

BeanFactoryPostProcessor 会在 Java 配置类初始化之前执行。

当 Java 配置类中定义了 BeanFactoryPostProcessor 时,如果要创建配置类中的 BeanFactoryPostProcessor 就必须提前创建和初始化 Java 配置类。

在创建和初始化 Java 配置类时,由于 BeanPostProcessor 还未准备好,无法解析配置类中的 @Autowired 等注解,导致 @Autowired 等注解失效:

如果是实现接口,则正常打印

@Slf4j
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.info("注入 ApplicationContext");
    }
    @Bean
    public BeanFactoryPostProcessor processor1() {
        return processor -> log.info("执行 processor1");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("初始化");
    }
}

相关文章
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
27天前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
58 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
160 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
17天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
189 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
27天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
61 1
|
29天前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
23 1
|
30天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
71 1
|
2月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean