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

news2024/11/26 4:26:51

文章目录

  • 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.coloradmin.cn/o/1260124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【中间件】配置中心中间件intro

中间件middleware 内容管理 why use 配置中心配置中心feature配置中心develop主流配置中心Apollo浅谈 本文从理论上介绍一下服务化中服务治理系统中的配置中心的理论并浅析Apllo 配置在生产中是很关键的一个部分&#xff0c;cfeng在work中遇到几次配置问题引发的问题&#xff0…

为何要隐藏IP地址?网络上哪些行为需要隐藏IP和更换IP?

网络已经成为现代人生活的重要组成部分&#xff0c;人们在网络上交流、学习、娱乐、购物等。但是&#xff0c;在享受网络带来的便利时&#xff0c;我们也需要时刻保护自己的隐私和安全。其中&#xff0c;IP地址作为网络通信中的重要标识&#xff0c;如何隐藏以及在哪些情况下需…

电商数据采集中如何采集1688平台商品详情SKU数据

一、背景介绍 1688.com是阿里旗下国内最大的B2B批发采购平台&#xff0c;1688分销客是依托此平台的官方营销平台&#xff0c;通过此平台API接口的接入推广平台商家的商品&#xff0c;按照商品成交金额的一定比例获得佣金。可以调用1688平台上的商品详情&#xff0c;SKU数据&…

西南科技大学电路分析基础实验A1(一阶电路的设计)

目录 一、实验目的 二、实验设备 三、预习内容(如:基本原理、电路图、计算值等) 四、实验数据及结果分析(预习写必要实验步骤和表格) 1. 观测一阶电

万字解析设计模式之观察者模式、中介者模式、访问者模式

一、观察者模式 1.1概述 观察者模式是一种行为型设计模式&#xff0c;它允许一个对象&#xff08;称为主题或可观察者&#xff09;在其状态发生改变时&#xff0c;通知它的所有依赖对象&#xff08;称为观察者&#xff09;并自动更新它们。这种模式提供了一种松耦合的方式&…

什么是高性能计算岗位

最近有小伙伴咨询什么是高性能计算岗位。 1、什么是高性能计算 高性能计算&#xff0c;在很多招聘信息中也会被标注为 HPC&#xff0c;是 High Performance Computing 的缩写。 目前很多 AI 公司或者从事 AI 的部门招聘都有这个岗位需求&#xff0c;我从某聘上截取了几个有代…

VBA高级应用30例:Ribbon(功能区)的介绍

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

【Proteus仿真】【STM32单片机】感应水龙头设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1602液晶模块、HCSR04超声波等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示超声波模块检测的距离&#xff0c;若检测距离小…

二十九、微服务案例完善(数据聚合、自动补全、数据同步)

目录 一、定义 二、分类 1、桶(Bucket)聚合: 2、度量(Metric&#xff09;聚合: 3、管道聚合&#xff08;Pipeline Aggregation&#xff09;&#xff1a; 4、注意&#xff1a; 参与聚合的字段类型必须是: 三、使用DSL实现聚合 聚合所必须的三要素&#xff1a; 聚合可配…

数字系列——数字经济

数字经济是全球经济未来发展方向&#xff0c;正在成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。都知道数字经确实很重要&#xff0c;但有些人还傻傻搞不懂数字经济到底是什么&#xff1f;小编今天就给大家捋一捋。 什么是数字经济&#xff1f; 数字经…

tabs切换,组件库framework7

IOS和安卓兼容的背景下&#xff0c; 可以使用&#xff1a;framework7.io文档 效果展示&#xff1a; 代码&#xff1a; <!-- Top Tabs --> <div class"tabs tabs-top"><div class"tab tab1 active">...</div><div class"…

高级/进阶”算法和数据结构书籍推荐

“高级/进阶”算法和数据结构书籍推荐《高级算法和数据结构》 高级算法和数据结构 为什么要选择本书 谈及为什么需要花时间学算法&#xff0c;我至少可以列举出三个很好的理由。 (1)性能&#xff1a;选择正确的算法可以显著提升应用程序的速度。仅就搜索来说&#xff0c;用二…

jQuery_07 函数的使用

在jQuery中&#xff0c;如何使用函数呢&#xff1f; 1.基本函数 函数(常用的) 其实有很多函数&#xff0c;但是我们只需要掌握常用的函数即可 1.val 操作dom对象的value val() 没有参数 获取dom数组中第一个dom对象的value值 val(value) 有参数 设置dom数组中所有dom对象的…

毫米波雷达DOA角度计算-----DBF算法

DBF算法实现程序如下&#xff1a; 输入&#xff1a; parameter 是 毫米波雷达的参数设置。 antVec 是 目标点的8个虚拟天线的非相参积累数据。 function [angle,doa_abs] dbfMethod(parameter,antVec)txAntenna parameter.txAntenna; % 发射天线 [1 1]rxAntenna para…

使用功能点估算法进行估算,5大注意事项

功能点估算法在软件项目管理中起着重要的作用&#xff0c;其有助于项目的早期估算&#xff0c;更准确地预测项目成本和进度&#xff0c;有助于更好地理解项目规模&#xff0c;并做出相应的资源分配和进度安排。如果不使用此估算方法&#xff0c;可能会导致项目范围不清晰&#…

R语言期末考试复习二

上篇文章的后续&#xff01;&#xff01;&#xff01;&#xff01; http://t.csdnimg.cn/sqvYD 1.给向量vec1设置名为"A","B","C","D","E","F","G"。 2.将矩阵mat1的行名设置为"Row1"&#…

Unreal Engine 学习笔记 (4)—— 多方向动画

1.创建混合空间 1.设置水平方向命名为Direction表示行进方向 -45,300表示向左前方45度方向行走-90,300表示向正左方90度方向行走-135,300表示向左后方45度方向行走-180,300表示向正后方行走右侧方向动画与上述左侧使用同样方法设置Run动画与Walk动画使用同样方法设置 2. 设置…

【网安AIGC专题】46篇前沿代码大模型论文、24篇论文阅读笔记汇总

网安AIGC专题 写在最前面一些碎碎念课程简介 0、课程导论1、应用 - 代码生成2、应用 - 漏洞检测3、应用 - 程序修复4、应用 - 生成测试5、应用 - 其他6、模型介绍7、模型增强8、数据集9、模型安全 写在最前面 本文为邹德清教授的《网络安全专题》课堂笔记系列的文章&#xff0c…

基于注解配置的AOP

注解方式AOP基本使用 Spring的AOP也提供了注解方式配置&#xff0c;使用相应的注解代替之前的xml配置。 xml配置&#xff1a; <aop:config> <!-- 配置切入点 目的是指定哪些方法增强--><aop:pointcut id"myPointCut1" expression"execu…

智能优化算法应用:基于多元宇宙算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于多元宇宙算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于多元宇宙算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.多元宇宙算法4.实验参数设定5.算法结果6.参考…