[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.mzph.cn/news/174466.shtml

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

相关文章

python回溯求解电话号码组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 输入&#xff1a;digits "23" 输出&#xff1a;["ad&qu…

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

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

【Redis缓存】RedisTemplate如何获取符合要求的key,批量获取key

RedisTemplate如何获取符合要求的key,批量获取key 一、方法/命令二、数据使用 一、方法/命令 如果使用命令的形式&#xff0c;输入以下命令即可 keys *如果使用RedisTemplate&#xff0c;则方法为 redisTemplate.keys()获取所有符合条件的key。 二、数据使用 redis中缓存了…

linux下的工具---vim

一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行…

Android笔记(十四):JetPack Compose中附带效应(一)

在Android应用中可以通过定义可组合函数来搭建应用界面。应用界面的更新往往是与可组合函数内部定义的状态值相关联的。当界面的状态值发生变更&#xff0c;会导致应用界面进行更新。在Android笔记&#xff08;九&#xff09;&#xff1a;Compose组件的状态&#xff0c;对Compo…

【新手解答2】深入探索 C 语言:一些常见概念的解析

C语言的相关问题解答 写在最前面问题1变量名是否有可能与变量重名&#xff1f;变量名和变量的关系变量名与变量是否会"重名"举例说明结论 变量则是一个地址不变&#xff0c;值时刻在变的“具体数字”变量的地址和值变量名与数据类型具体化示例结论 问题2关于你给我的…

priority_queue模拟实现

目录 仿函数 模拟实现 结果 大根堆 小根堆 完整代码 priority_queue.h test.c 仿函数 仿函数的通俗定义&#xff1a;仿函数&#xff08;functor&#xff09;又称为函数对象&#xff08;function object&#xff09;是一个能行使函数功能 的类。仿函数的语法几乎和我们…

ubuntu修改系统语言

修改ubuntu系统语言 操作指令修改系统设置总结 操作 ubuntu系统自带的英文环境&#xff0c;个人觉得用起来不方便。改掉吧。换成中文 指令修改 参考了一些博客的解决方式 ctrlartT 打开终端。 sudo apt-get install language-pack-zh-hans 输入下载汉化包的指令。 但是&…

MATLAB的rvctools工具箱熟悉运动学【机械臂机器人示例】

1、rvctools下载安装 rvctools下载地址&#xff1a;rvctools下载 截图如下&#xff0c;点击红色箭头指示的“Download Shared Folder” 即可下载 下载之后进行解压&#xff0c;解压到D:\MATLAB\toolbox这个工具箱目录&#xff0c;这个安装路径根据自己的情况来选择&#xff0c…

OSG编程指南<三>:利用Cmake+VS2019+OSG3.6.5编译osgVerse

目前osgearth的ralease版本和debugx64都已经编译过了并且自测可用&#xff0c;放到资源里osgearth下载&#xff0c;供需要的朋友下载参考。环境配置&#xff1a;win10VS2017OSG3.6.4OSGEarth2.10.2&#xff0c; 1、配置OSG环境 编译好osg&#xff0c;然后在系统变量中配置如下…

javascript 运算符

javascript 运算符 目录 javascript 运算符 一、算术运算符 1、自增运算符 2、自减运算符 二、比较运算符 三、赋值运算符 四、逻辑运算符 五、条件运算符 疑难解答&#xff1a; 这一节&#xff0c;我们来介绍JavaScript的运算符。运算符是完成一系列操作的符号&…

Rocket架构

NameServer&#xff1a;作为注册中心&#xff0c;提供路由注册、路由踢出、路由发现功能&#xff0c;舍弃强一致&#xff0c;保证高可用&#xff0c;集群中各个节点不会实时通讯&#xff0c;其中一个节点下线之后&#xff0c;会提供另外一个节点保证路由功能。 Rocket mq name…

基于Python获取亚马逊的评论信息的处理

文章目录 一、分析亚马逊的评论请求二、获取亚马逊评论的内容三、亚马逊评论信息的处理四、代码整合4.1 代理设置4.2 while循环翻页 总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具…

力扣hot100 和为 K 的子数组 前缀和

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int subarraySum(int[] nums, int k){int ans 0;int n nums.length;int[] s new int[n 1];// 前缀和s[0] 0;s[1] nums[0];for (int i 2; i < n; i)s[i] s[i - 1] nums[i - 1…

C#-基础及扩展合集(持续更新)

一、基础 Ⅰ 关键字 1、record record&#xff08;记录&#xff09;&#xff0c;编译器会在后台创建一个类。支持类似于结构的值定义&#xff0c;但被实现为一个类&#xff0c;方便创建不可变类型&#xff0c;成员在初始化后不能再被改变 &#xff08;C#9新增&#xff09; …

PostgreSQL + SQL Server = WiltonDB

WiltonDB 是一个基于 PostgreSQL 的开源数据库&#xff0c;通过 Babelfish 插件支持 Microsoft SQL Server 协议以及 T-SQL 语句。 Babelfish 是亚马逊提供的一个开源项目&#xff0c;使得 PostgreSQL 数据库同时具有 Microsoft SQL Server 数据查询和处理的能力。Babelfish 可…

如何提高学生的课堂参与度

提高学生的课堂参与度是让许多教师头疼的问题。不过通过采取一些简单的方法&#xff0c;就可以让学生更积极的参与课堂活动。那么&#xff0c;这些方法是什么呢&#xff1f; 激发学生的兴趣 学生如果对课堂内容感兴趣&#xff0c;他们就会更加积极地参与其中。教师可以通过使用…

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

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

联邦学习Federated Learning(FL)

联邦学习Federated Learning 序言FL流程细节FL代码实现&#xff08;Pytorch&#xff09;Reference 序言 手机的数据涉及到个人隐私和安全&#xff0c;如果将客户端的数据上传到服务端&#xff0c;终究是很容易泄漏出用户的信息&#xff0c;何况 用户也不愿意把自己的数据交给服…

Java基础小结

Java 基础小结 Java 基础概念与常识 什么是 Java ? Java 是 1995 年由 sun 公司推出的一门高级语言。 Java 的四个基本特性是面向对象、平台无关性、安全性和简单性。 具体特点如下: 简单易学。 平台无关性。 面向对象 面向对象是一种程序设计技术&#xff0c;以木匠工…