当前位置: 首页 > news >正文

红动中国设计网站官网专业软文发布平台

红动中国设计网站官网,专业软文发布平台,做业务有哪些好的网站,做模具的都有什么网站Spring 源码学习 2:Bean 后处理器 在前篇中介绍了,Spring 提供了一些 bean 的后处理器(实现了 BeanPostProsessor 接口),用于处理 bean 的依赖注入等。实际上,我们可以通过自定义一个实现了相应接口的后处…

Spring 源码学习 2:Bean 后处理器

在前篇中介绍了,Spring 提供了一些 bean 的后处理器(实现了 BeanPostProsessor 接口),用于处理 bean 的依赖注入等。实际上,我们可以通过自定义一个实现了相应接口的后处理器来观察 bean 的生命周期。

Bean 生命周期

创建一个用于打印不同生命周期方法钩子的 bean 后处理器:

static class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessBeforeDestruction() is called.");}@Overridepublic boolean requiresDestruction(Object bean) {return DestructionAwareBeanPostProcessor.super.requiresDestruction(bean);}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessBeforeInstantiation() is called.");return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessAfterInstantiation() is called.");return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessProperties() is called.");return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessBeforeInitialization() is called.");return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName))System.out.println("postProcessAfterInitialization() is called.");return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}

创建一个 Bean,在不同的阶段打印信息:

static class MyBean {private ApplicationContext applicationContext;public MyBean() {System.out.println("MyBean's constructor is called.");}@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {System.out.println("MyBean's dependency injection method is called.");this.applicationContext = applicationContext;}@PostConstructpublic void postConstruct() {System.out.println("MyBean's postConstruct is called.");}@PreDestroypublic void preDestroy() {System.out.println("MyBean's preDestroy is called.");}
}

创建配置类,用 bean 方法添加这两个 bean:

@Configuration
static class Config {@BeanMyBeanPostProcessor myBeanPostProcessor() {return new MyBeanPostProcessor();}@BeanMyBean myBean() {return new MyBean();}
}

这里用测试类通过加载 Spring 上下文的方式进行测试,相关内容可以阅读这里。

输出:

postProcessBeforeInstantiation() is called.
MyBean's constructor is called.
postProcessAfterInstantiation() is called.
postProcessProperties() is called.
MyBean's dependency injection method is called.
postProcessBeforeInitialization() is called.
MyBean's postConstruct is called.
postProcessAfterInitialization() is called.
postProcessBeforeDestruction() is called.
MyBean's preDestroy is called.

可以看到执行顺序如下:

  1. postProcessBeforeInstantiation()钩子被调用。
  2. bean 工厂通过相应 bean 的构造器创建 bean 实例。
  3. postProcessAfterInstantiation()钩子被调用。
  4. postProcessProperties()钩子被调用。
  5. bean 工厂对实例进行依赖注入。
  6. postProcessBeforeInitialization()钩子被调用。
  7. bean 的postConstruct()方法被调用(通常在这里执行用户自定义初始化)。
  8. postProcessAfterInitialization钩子被调用。
  9. postProcessBeforeDestruction钩子被调用。
  10. bean 的preDestroy()方法被调用(通常在这里执行资源释放和环境清理)。

可以看到 bean 的生命周期主要包含这么几部分:

  • 实例化(通过构造器)
  • 依赖注入(属性注入或方法注入)
  • 初始化
  • 销毁

在相应的阶段前后,bean 后处理器中对应的生命周期钩子都会被调用。

模版方法模式

实际上这里应用了设计模式中的模版方法模式。

下面展示如何用模版方法模式实现一个在创建 Bean 时使用生命周期钩子的最简单的 Bean 工厂。

首先定义生命周期钩子接口:

interface LifeCycleHook {void beforeConstruct();<T> void afterConstruct(T instance);
}

定义自定义的 bean 工厂:

static class MyBeanFactory {private final List<LifeCycleHook> lifeCycleHooks = new ArrayList<>();public void add(LifeCycleHook lifeCycleHook) {lifeCycleHooks.add(lifeCycleHook);}public <T> T getBean(Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Constructor<T> constructor = clazz.getConstructor();constructor.setAccessible(true);// 调用实例化前钩子for (LifeCycleHook lifeCycleHook : lifeCycleHooks) {lifeCycleHook.beforeConstruct();}// 实例化T newInstance = constructor.newInstance();// 调用实例化后钩子for (LifeCycleHook lifeCycleHook : lifeCycleHooks) {lifeCycleHook.afterConstruct(newInstance);}return newInstance;}
}

在 bean 工厂中,通过反射的方式获取构造器,并创建实例。为了简单起见,这里默认类型拥有一个无参构造器。

bean 工厂持有一个生命周期钩子列表,在实例化前后会依次执行生命周期钩子中的对应方法。

注解处理和工作原理

除了实现特定接口以介入 Bean 的生命周期外,Bean 的后处理器还能够处理 Bean 中的特殊注解。

虽然在前文提到过 Bean 的后处理器可以处理@Aurowired注解,但在这里还是需要再次说明和演示。

注解处理

有这样的类型:

static class Bean1 {
}static class Bean2 {
}@Getter
@ToString
static class Bean3 {private Bean1 bean1;private Bean2 bean2;private String javaHome;@Autowiredpublic void setBean1(Bean1 bean1) {System.out.println("setBean1() is called.");this.bean1 = bean1;}@Resourcepublic void setBean2(Bean2 bean2) {System.out.println("setBean2() is called.");this.bean2 = bean2;}@Autowiredpublic void setJavaHome(@Value("${JAVA_HOME}") String javaHome) {System.out.println("setJavaHome() is called.");this.javaHome = javaHome;}@PostConstructpublic void postConstruct() {System.out.println("postConstruct() is called.");}@PreDestroypublic void preDestroy() {System.out.println("preDestroy() is called.");}
}

Bean3中分别通过@Autowired@Resource注解用方法注入了两个属性,javaHome属性也是利用@Autowired方法注入,不同的是其来源不是 Bean,而是环境变量字符串,所以使用@Value注解。此外,还用两个方法是通过注解定义的 Bean 的生命周期钩子。

使用容器加载这些 Bean 定义并获取实例:

GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
// 调用工厂的后处理器,添加 bean 的后处理器,实例化 bean 等
context.refresh();
Bean3 bean3 = context.getBean(Bean3.class);
System.out.println(bean3);
// 关闭容器
context.close();

这里使用GenericApplicationContext而不是DefaultListableBeanFactory,原因在于前者会自动调用工厂的后处理器并注册 bean 后处理器。

执行代码可以看到,输出中不包括任何依赖注入方法调用或生命周期钩子调用。这表示容器并没有正确处理@Autowired@Resource注解。

@Autowired

我们已经知道,这些 Bean 的注解是由 Bean 的后处理器处理的,因此这里添加处理@Autowired注解的后处理器:

context.registerBean("bean3", Bean3.class);
// 添加处理 @Autowired 注解的后处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 调用工厂的后处理器,添加 bean 的后处理器,实例化 bean 等
context.refresh();

执行代码会报错,因为这里有一个需要从环境变量解析的依赖注入,因此还需要为容器添加一个解析器:

// 添加处理 @Autowired 注解的后处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 添加处理 @Value 注解的从环境变量、配置中解析字符串的解析器
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

再执行就能观察到 @Autowired@Value注解已经被正确处理。

@Resource

现在还有@Resource注入和生命周期钩子注解没有被处理,同样的,加入相应的 Bean 后处理器:

// 添加处理 @Resource 注解和生命周期钩子注解的后处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class);
// 调用工厂的后处理器,添加 bean 的后处理器,实例化 bean 等
context.refresh();

再执行就能看到所有依赖了注入和生命周期钩子都正常工作了。

@ConfigurationProperties

假设有一个类使用@ConfigurationProperties注解利用配置信息注入属性:

@ConfigurationProperties(prefix = "myapp")
@ToString
@Setter
static class Bean4{private String home;private String version;
}

配置文件:

myapp.home=D://home
myapp.version=21

要让这个注解生效,就需要使用新的后处理器:

// 添加处理 @ConfigurationProperties 注解的后处理器
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

添加方式略有不同,是因为除了添加 Bean 定义,这个注解处理器还需要绑定属性等。

现在执行不会得到想要的效果,这是因为容器并没有读取配置文件并加载配置信息。要手动加载:

// 1. 创建属性源
org.springframework.core.io.Resource resource = new ClassPathResource("application.properties");
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 2. 将属性添加到 Environment
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
propertySources.addFirst(new PropertiesPropertySource("appConfig", properties));
// 调用工厂的后处理器,添加 bean 的后处理器,实例化 bean 等
context.refresh();

工作原理

示例类型如下:

static class Bean1 {
}static class Bean2 {
}@ToString
static class Bean3 {@Autowiredprivate Bean1 bean1;private Bean2 bean2;private String javaHome;@Autowiredpublic void setBean2(Bean2 bean2) {this.bean2 = bean2;}@Autowiredpublic void setJavaHome(@Value("${JAVA_HOME}") String javaHome) {this.javaHome = javaHome;}
}

Bean3中分别通过@Autowired注解进行了属性注入、方法注入和用环境变量方法注入。

下面展示实现这些注入的原理。

首先,看最外层的后处理器调用实现:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean1", new Bean1());
beanFactory.registerSingleton("bean2", new Bean2());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
// 关联 bean 工厂
Bean3 bean3 = new Bean3();
beanPostProcessor.setBeanFactory(beanFactory);
// 模拟 bean 的后处理器调用
beanPostProcessor.postProcessProperties(null, bean3, "bean3");
System.out.println(bean3);

输出结果中的环境变量表达式没有被正确处理(javaHome=${JAVA_HOME}),因此需要添加一个解析器:

beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// 添加对`${...}`表达式的解析器
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

查看源码会发现,postProcessProperties方法中主要调用了一个私有方法:

image-20250620164622998

其,返回类型为InjectionMetadata,这个方法的用途是检索目标 Bean 定义,将查找到的需要注入的属性/方法记录到InjectionMetadata对象中:

image-20250620164810318

可以通过反射模拟该方法调用:

// 模拟 bean 的后处理器调用
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
InjectionMetadata metadata = (InjectionMetadata)findAutowiringMetadata.invoke(beanPostProcessor, "bean3", Bean3.class, null);
metadata.inject(bean3, "bean3", null);
System.out.println(bean3);

现在已经知道哪些地方需要注入了,就可以通过反射调用来模拟注入过程。

模拟字段注入:

// 模拟属性注入
Field bean1Field = Bean3.class.getDeclaredField("bean1");
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean1Field, true);
Bean1 bean1 = (Bean1) beanFactory.doResolveDependency(dependencyDescriptor, null, null, null);
bean1Field.set(bean3, bean1);

模拟方法注入:

// 模拟方法注入
Method setBean2 = Bean3.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Bean2 bean2 = (Bean2) beanFactory.doResolveDependency(dependencyDescriptor, null, null, null);
setBean2.invoke(bean3, bean2);

模拟方法注入环境变量:

// 模拟 方法注入 Value
Method setJavaHome = Bean3.class.getDeclaredMethod("setJavaHome", String.class);
String javaHome = (String) beanFactory.doResolveDependency(new DependencyDescriptor(new MethodParameter(setJavaHome, 0), true), null, null, null);
setJavaHome.invoke(bean3, javaHome);

本文的完整示例代码可以从这里获取。

The End.

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

相关文章:

  • 教育局网站建设方案搜索引擎推广的三种方式
  • 单页面网站制作百度推广代理商名单
  • 电子商务毕业设计设计网站建设关键词排名代做
  • 宠物网站建设策划方案seo学校培训课程
  • 东莞教育网站建设百度应用app
  • 评论网站建设网站注册地址查询
  • 有免费建网站网站免费优化软件
  • 如何从下载的视频查到原网站徐州百度推广总代理
  • 网站建设361网页设计效果图及代码
  • 开一个平台需要多少钱?提升网页优化排名
  • 做网站的公司跑了网站关键词排名手机优化软件
  • 建立自我优化网站技术
  • 徐州制作网站软件免费收录软文网站
  • 张家港快速网站建设线上推广的方法
  • 建设个人网站的要求免费个人自助建站
  • 泊头市网站制作公司搜索引擎技术
  • 网页生成pdf失败百度seo流量
  • 网站主持人制作网站代言人网络推广代理平台
  • 政府门户网站建设的问题与对策上海网站seo诊断
  • 门户网站建设的平台关于手机的软文营销
  • 河北网站建设搭建网页设计个人网站
  • ftp网站建设游戏代理平台有哪些
  • 国外企业建站百度域名收录提交入口
  • 南京h5 网站建设汕头seo推广
  • 企业网站开发需求文档制作网页链接
  • dedecms做网站教程荆州seo推广
  • 亲子游网站建设内容广告投放是做什么的
  • 响应式设计是什么意思如何优化网站推广
  • 教育类网页设计欣赏网站优化外包
  • 上海市建设工程设计文件审查管理事务中心网站友情链接网站大全