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

网站 宽屏窄屏自适应嘉定区整站seo十大排名

网站 宽屏窄屏自适应,嘉定区整站seo十大排名,河南旅游网站建设,网页报价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.dtcms.com/wzjs/413907.html

相关文章:

  • 包头网站设计百度推广公司电话
  • 做教育集团的网站建设网页查询
  • 用java做网站的步骤网站后台管理系统
  • 微信微网站开发百度云seo实战培训班
  • 维基百科建设网站打字赚钱平台 学生一单一结
  • 网站建设与管理感想网店运营
  • 龙华做手机网站建设重庆seo排名外包
  • 想发布oa网站 需要备案吗网站免费推广软件
  • 广州市 住房建设局网站首页精准营销的三要素
  • 浙江省网站建设报价页面优化的方法有哪些
  • 出售网站平台百度官方网站下载安装
  • 南通网站建设方法网站优化建议
  • 外国网站建站百度客服工作内容
  • 做双语网站福州seo代理商
  • 聊城做网站公司信息交换友情链接的网站标准是什么
  • wordpress 适合外贸站cpc广告点击日结联盟
  • 南昌公司做网站山东网站建设
  • 已注册域名怎么做网站呢怎么样在百度上免费推广
  • 建设网站的网站空间客户资源买卖平台
  • 网站如何做监测链接seo站长网怎么下载
  • 怎么在自己网站上做拼图重庆自动seo
  • 副业做网站程序搜狗推广
  • 提供网站制作成功的品牌推广案例分析
  • 网站设计的国际专业流程公司网站设计要多少钱
  • 做爰网站有哪些新闻发稿平台有哪些
  • 上海集团网站建设公司seo站长平台
  • 内蒙建设厅网站优化神马排名软件
  • 嵌入式开发工程师需要学什么找索引擎seo
  • 湛江网站建设公司哪个好如何获取热搜关键词
  • 福州 建站 软件百度搜索引擎关键词