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

文章目录

  • Bean 相关
    • 到底什么是`beanFactory`
    • `beanFactory`能干啥
    • `ApplicationContext`是什么
    • `ApplicationContext`的功能比 `BeanFactory`多了什么
  • 容器的实现
    • `BeanFactory`的实现
    • `ApplicationContext`的实现
      • `xml` 配置
      • 配置类配置
  • Bean 的生命周期
    • 3.1 Bean 的常见的后处理器
      • 测试代码
      • 总结
    • 3.2 工厂后处理器的模拟实现
    • 3.3 `Aware` 接口以及 `InitializingBean`
    • 3.4 AutoWired 以及 PostConstruct 失效的情况分析

:::info
环境

  • 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的核心容器。image.png
:::


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多了什么

image.png
:::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是一个干净的容器。
image.png


@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();
    }
}

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

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

ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

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

 context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

:::info
注册 AutowiredAnnotationBeanPostProcessor之后, autowired 生效, 但是值注入失败。
:::
image.png

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

:::info
替换完 setAutowireCandidateResolver之后,值注入成功
:::
image.png

context.registerBean(CommonAnnotationBeanPostProcessor.class);

:::info
注册 CommonAnnotationBeanPostProcessor之后, @Resource @PostConstruct @PreDestroy 生效。
:::
image.png

总结

:::warning
通过前文可知

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

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

:::warning
涉及后处理器 @ComponentScan, @Bean
:::
image.png


@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");
    }
}

运行结果image.png
:::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没有注册,符合预期。
image.png
:::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");
    }
}

运行结果image.png

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");
    }
}

image.png

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

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

比如当 Java 配置类不包括 BeanFactoryPostProcessor 时:
image.png
BeanFactoryPostProcessor 会在 Java 配置类初始化之前执行。
当 Java 配置类中定义了 BeanFactoryPostProcessor 时,如果要创建配置类中的 BeanFactoryPostProcessor 就必须提前创建和初始化 Java 配置类。
在创建和初始化 Java 配置类时,由于 BeanPostProcessor 还未准备好,无法解析配置类中的 @Autowired 等注解,导致 @Autowired 等注解失效:
image.png

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

@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("初始化");
    }
}

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/193455.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构 | TOP-K问题

数据结构 | TOP-K问题 文章目录 数据结构 | TOP-K问题随机生成一些数据&#xff0c;找前k个最大值进行取前k个值建堆找到了前k个结果以及全部代码 TOP-K问题&#xff1a;即求数据结合中前K个最大的元素或者最小的元素&#xff0c;一般情况下数据量都比较大。 就是从N个数里面找…

P14 C++局部静态变量static延长生命周期

目录 01 前言 02 变量的作用域与生命周期 2.1 什么是作用域&#xff1a; 2.2 什么是变量的生命周期&#xff1a; 03 局部静态 3.1非静态变量例子 3.2静态变量例子 04 全局变量 05 后话 01 前言 在前几期里&#xff0c;我们了解了static关键字在特定上下文中的含义。 …

innovus如何在floorplan view显示所有module

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 如题&#xff0c;innovus的图形界面在floorplan view下默认只能显示instance数量超过100个的module&#xff0c;如果要显示更小的module&#xff0c;需要在VIEW-Set Perference…

分享从零开始学习网络设备配置--任务4.3 使用动态路由RIPng实现网络连通

任务描述 某公司使用IPv6技术搭建企业网络&#xff0c;由于静态路由需要管理员手工配置&#xff0c;在网络拓扑发生变化时&#xff0c;也不会自动生成新的路由&#xff0c;因此采用IPv6动态路由协议RIPng实现网络连通&#xff0c;实现任意两个节点之间的通信&#xff0c;并降低…

堆的应用:堆排序

文章目录 前言堆排序的实现&#xff08;升序为例&#xff09;代码 前言 堆排序&#xff0c;顾名思义是一个利用堆来完成排序的一个操作。在之前&#xff0c;小编在[C语言学习系列–&#xff1e;【关于qsort函数的详解以及它的模拟实现】] 谈到冒泡排序&#xff0c;但是冒泡排序…

京东秒杀之秒杀详情

1 编写前端页面&#xff08;商品详情&#xff09; <!DOCTYPE html> <head><title>商品详情</title><meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /><script type"text/javascript" src&…

GPU集群使用Tip:查询端口号占用情况、进程由哪个用户创建、运行时指定某一张显卡

在GPU集群上运行代码&#xff0c;会面临一些问题&#xff1a; &#xff08;1&#xff09;跑着跑着GPU memory分配失败 – 因为有其他人在使用 &#xff08;2&#xff09;运行时显示端口号已被占用&#xff0c;需要你换一个端口。 这个时候一般采取的方法有&#xff1a; &#x…

java springboot测试类Transactional解决 测试过程中在数据库留下测试数据问题

好 目前 我们已经完成了表现层对应的测试了 但这里有个坑 如果我们在执行某个声明周期时 包含了测试的过程 它会在数据库中留下一条数据 但真实企业开发 绝对不允许 过一遍留一组数据的 那么 我们的期望就是 执行测试过程 但不要留下任何数据 这是我们的数据库表 然后 这里…

帆软报表 channel 反序列化漏洞复现

0x01 产品简介 FineReport、FineBI 是帆软软件开发的企业级报表设计和数据分析工具与商业智能平台。 0x02 漏洞概述 帆软FineReport、FineBI 存在反序列化漏洞&#xff0c;攻击者可向 /webroot/decision/remote/design/channel 接口发送精心构造的反序列化数据&#xff0c;在目…

E云管家微信群聊机器人开发

请求URL&#xff1a; http://域名地址/modifyGroupRemark 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId是String登录实例标识chatRo…

【html+css】表单元素

目录 表单元素 展示图 简约写法&#xff1a; 完美写法 表单元素 输入框 单选框 复选框 下拉框 按钮 展示图 简约写法&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><t…

域名邮箱与企业邮箱的区别:功能、应用与优势

根据使用者的不同需求&#xff0c;电子邮件分为域名邮箱和企业邮箱两种类型。那么这两种邮箱之间究竟存在哪些区别呢&#xff1f;本文将从定义、优势和劣势三个方面进行详细解析。 什么是域名邮箱&#xff1f; 域名邮箱&#xff0c;顾名思义是以域名作为后缀的电子邮箱。域名邮…

第二十五章 解析cfg文件及读取获得网络结构

网络结构 以YOLOv3_SPP为例 cfg文件 部分&#xff0c;只是用来展示&#xff0c;全部的代码在文章最后 [net] # Testing # batch1 # subdivisions1 # Training batch64 subdivisions16 width608 height608 channels3 momentum0.9 de…

振南技术干货集:FFT 你知道?那数字相敏检波 DPSD 呢?(2)

注解目录 1 、DPSD 的基础知识 1.1 应用模型 1.2 原理推导 1.3 硬件 PSD &#xff08;相敏检波&#xff0c;就是从繁乱复杂的信号中将我们关心的信号检出来&#xff0c;同时对相位敏感。 数学原理&#xff0c;逃不掉的&#xff0c;硬着头皮看吧。&#xff09; 2 、DPSD …

数据结构(超详细讲解!!)第二十五节 线索二叉树

1.线索二叉树的定义和结构 问题的提出&#xff1a; 通过遍历二叉树可得到结点的一个线性序列&#xff0c;在线性序列中&#xff0c;很容易求得某个结点的直接前驱和后继。但是在二叉树上只能找到结点的左孩子、右孩子&#xff0c;结点的前驱和后继只有在遍历过程中才能得到…

python中的简单线性拟合

简单线性回归可以拟合线性关系的数据&#xff0c;一般使用一次函数或二次函数即可。 import numpy as np import matplotlib.pyplot as pltxnp.array([1,2,3,4,5,6,7,8,9,10]) ynp.array([2.5,4.5,4.8,5.5,6.0,7.0,7.8,8.0,9.0,10.0])#一次拟合函数 slope,interceptnp.polyfit…

TUP通信——与多个客户端同时通信

一&#xff0c;概括&#xff1a;可以通过多线程思想每加一个客户端由线程池中的主线程交给一个子线程管理 二&#xff0c;案例 &#xff08;1&#xff09;&#xff0c;线程池 &#xff08;2&#xff09;&#xff0c;服务端 &#xff08;3&#xff09;&#xff0c;客户端

从零开始学优惠券样式代码编写,让你的网站焕然一新!

样式1&#xff1a; 代码实例&#xff1a; <div class"box"><div class"itemBox"><div class"leftBox">全额抵扣</div><div class"rightBotton"><button>立即使用</button></div><…

FLV 文件格式分析

前言 flv 是 flash video 的缩写&#xff0c;是 Adobe Flash payler 支持的一种流媒体播放格式。flv 是一种层级格式&#xff0c;除了一个 flv header 外&#xff0c;剩下全是由 一个个 tag 组成。tag 是由 tag 头和 tag 数据组成。tag 类型分为音频、视频、脚本&#xff0c;一…

Kafka 如何保证消息消费的全局顺序性

哈喽大家好&#xff0c;我是咸鱼 今天我们继续来讲一讲 Kafka 当有消息被生产出来的时候&#xff0c;如果没有指定分区或者指定 key &#xff0c;那么消费会按照【轮询】的方式均匀地分配到所有可用分区中&#xff0c;但不一定按照分区顺序来分配 我们知道&#xff0c;在 Kaf…