[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;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;@NullableClass<?> 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 {@EventListenerpublic 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());}@Configurationpublic static class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic 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 AnnotationConfigRegistryAnnotationConfigServletWebServerApplicationContext 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@Beanpublic ServletWebServerFactory servletWebServerFactory () {System.out.println(">>>>>>> tomcat");return new TomcatServletWebServerFactory();}// 路径派发@Beanpublic DispatcherServlet dispatcherServlet() {System.out.println(">>>>>>>> dispatch");return new DispatcherServlet();}// 注册 dispatch 到 tomcat@Beanpublic 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;@Autowiredpublic void setBean2(Bean2 bean2) {log.info("@Autowired 生效: {}", bean2);this.bean2 = bean2;}private Bean3 bean3;@Resourcepublic void setBean3(Bean3 bean3) {log.info("@Resource 生效: {}", bean3);this.bean3 = bean3;}private String home;@Autowiredpublic void setHome(@Value("${JAVA_HOME:hello world'}") String home) {log.info("@Value 生效: {}", home);this.home = home;}@PostConstructpublic void init() {log.info("@PostConstruct 生效");}@PreDestroypublic 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());// 用原始方式注册三个 beancontext.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/**/*.classs = "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/**/*.classs = "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 以及派生注解的 转换成 beanAbstractBeanDefinition 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 {@Overridepublic 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/**/*.classs = "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 以及派生注解的 转换成 beanAbstractBeanDefinition 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 {@Overridepublic 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 {@Overridepublic 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())) {// 是接口且有@MapperAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName())// 按照类型注入 SqlSessionFactory.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();// 生成一个新的 beanDefinitional 生成一个 beanNameAbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String beanName = nameGenerator.generateBeanName(beanDefinition1, beanDefinitionRegistry);beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);}}}catch (Exception e) {throw new RuntimeException(e.getMessage());}}@Overridepublic 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 {@Overridepublic void setBeanName(String s) {System.out.println("setBeanName" + this.getClass().getSimpleName() + " 名字叫:" + s);}@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {System.out.println("setBeanName" + this.getClass().getSimpleName() + " applicationContext 容器是:" + applicationContext);}@Overridepublic 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 {@Overridepublic void setBeanName(String s) {System.out.println("setBeanName" + this.getClass().getSimpleName() + " 名字叫:" + s);}@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {System.out.println("setBeanName" + this.getClass().getSimpleName() + " applicationContext 容器是:" + applicationContext);}@Overridepublic void afterPropertiesSet() {System.out.println("setBeanName" + this.getClass().getSimpleName() + " InitializingBean");}@Autowiredpublic void set(ApplicationContext applicationContext) {System.out.println("setBeanName" + this.getClass().getSimpleName() + " @Autowired 注入的 applicationContext 容器是:" + applicationContext);}@PostConstructpublic 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 {@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.info("注入 ApplicationContext");}@PostConstructpublic void init() {log.info("初始化");}
}

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

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

@Slf4j
@Configuration
public class MyConfig1 {@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.info("注入 ApplicationContext");}@PostConstructpublic void init() {log.info("初始化");}@Beanpublic 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 {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {log.info("注入 ApplicationContext");}@Beanpublic BeanFactoryPostProcessor processor1() {return processor -> log.info("执行 processor1");}@Overridepublic void afterPropertiesSet() throws Exception {log.info("初始化");}
}

image.png

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue day2

1、指令修饰符&#xff1a;.指明一些指令后缀&#xff0c;不同后缀封装不同处理操作 按键修饰符&#xff1a;keyup.enter v-model修饰符&#xff1a; v-model.trim&#xff1a;去首位空格 v-model.number&#xff1a;转数字 事件修饰符&#xff1a; 阻止事件冒泡&#xff1…

【vue2】axios请求与axios拦截器的使用详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;当我们在路由跳转前与后我们可实现触发的操作 【前言】ajax是一种在javaScript代码中发请…

Dockerfile讲解

Dockerfile 1. 构建过程解析2. Dockerfile常用保留字指令3. 案例3.1. 自定义镜像mycentosjava83.2. 虚悬镜像 4. Docker微服务实战 dockerfile是用来构建docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 dockerfile定义了进程需要的一切东西&…

详解Java中的泛型(泛型的语法,擦除机制,泛型的上界)

目录 一.什么是泛型 二.Java中为什么要使用泛型 三.泛型的语法 四.泛型类的使用 五.泛型的编译机制&#xff08;擦除机制&#xff09; 六.泛型的上界 一.什么是泛型 泛型&#xff08;Generics&#xff09;是Java SE 5中引入的一个新特性&#xff0c;可以使Java中的类和方…

【JavaEE】多线程 (1)

目录 1. 认识线程&#xff08;Thread&#xff09; 1) 线程是什么 2) 为啥要有线程 3) 进程和线程的区别 2.第⼀个多线程程序 3.多线程的其他创建方式 方法二:实现 Runnable 接⼝ 方法三:匿名内部类 方法四:实现Runable, 重写run, 匿名内部类 方法五:使用lambda表达式…

【青蛙跳台阶问题 —— (三种算法)】

青蛙跳台阶问题 —— (三种算法&#xff09; 一.题目介绍1.1.题目1.2.图示 二.解题思路三.题解及其相关算法3.1.递归分治法3.2.动态规划算法&#xff08;Dynamic Programming&#xff09;3.3.斐波那契数列法 四.注意细节 一.题目介绍 1.1.题目 一只青蛙一次可以跳上1级台阶&am…

C++ :静态成员

静态成员 静态成员就是在成员变量和成员函数前加上关键字 static &#xff0c;称为静态成员 静态成员分为&#xff1a; 静态成员变量 1.所有对象共享同一份数据 2.在编译阶段分配内存 3.类内声明&#xff0c;类外初始化 静态成员函数 1.所有对象共享同一个函数 2.静态成…

CSGO搬砖还能做吗?CSGO饰品未来走势如何?

steam/csgo搬砖项目真能月入过万吗&#xff1f;到底真的假的&#xff1f; 如何看待CSGO饰品市场的整体走向&#xff1f; 从整体来说&#xff0c;CSGO的饰品市场与规模肯定会持续不断的上升&#xff0c;大盘不会发生特别大的波动&#xff0c;目前处于稳定期&#xff01;&…

安装最新版WebStorm来开发JavaScript应用程序

安装最新版WebStorm来开发JavaScript应用程序 Install the Latest Version of JetBrains WebStorm to Develop JavaScript Applications By JacksonML 2023-11-25 1. 系统要求 WebStorm是个跨平台集成开发环境&#xff08;IDE&#xff09;。按照JetBrains官网对WebStorm软件…

C++——模板(进阶)

目录&#xff1a; 非类型模板参数 模板参数分类&#xff1a;类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函…

单片机学习10——独立按键

独立按键输入检测&#xff1a; #include<reg52.h>sbit LED1P1^0; sbit KEY1P3^4;void main() {KEY11;while(1){if(KEY10) //KEY1按下{LED10; //LED1被点亮}else{LED11;}} } 按键 #include<reg52.h>#define uchar unsigned char #define uint unsigned intsbit …

【Python技巧】快速安装各种常用库pip、whl、tar.gz最新最全安装方法(超时、快速安装))

【Python技巧】安装各种常用库pip、whl、tar.gz最新最全安装方法 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&…