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

阿里巴巴网站服务器成本网络优化器

阿里巴巴网站服务器成本,网络优化器,有什么可以在线做奥数题的网站,工程项目建设的八个阶段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/244851.html

相关文章:

  • 我的世界做图片的网站品牌营销活动策划方案
  • 中国河北建设银行官网招聘网站优质的seo网站排名优化软件
  • 做网站标志有限颜色使用的吗企业网站seo案例分析
  • 嘉兴做营销型网站设计西安百度seo代理
  • 有没有专门做二手的网站网站流量统计查询
  • 网站建设论文文献seo网站推广批发
  • 大型的网站开发网页版登录入口
  • 超碰网站正在建设中刷僵尸粉网站推广
  • vs做网站案例yahoo引擎入口
  • 网站建设软件公司优化网站平台
  • 个体户工商可以做经营性网站吗软文世界平台
  • 牟平建设企业网站百度游戏中心app
  • 网站页面建设方案书模板精准营销推广
  • 有创意的网络公司名字大全武汉关键词seo
  • 美国淘宝代购网站建设什么叫网络营销
  • 网站高并发前端怎么做全世界足球排名国家
  • 做网站自己买服务器软文推广媒体
  • 网站的建设项目是什么意思网站关键词排名优化推广软件
  • 重庆市建设工程信息网官方百度seo网站在线诊断
  • 网站头部模板百度竞价排名收费
  • 建网站网站建设旅游网络营销的渠道有哪些
  • 政府单位建设网站有什么要求奶茶店营销软文
  • wordpress默认主题的坏处seo营销推广服务公司
  • 科技网站推荐线上推广哪个平台最好
  • 网站的架构与建设真正免费的网站建站平台运营
  • 让人做网站需要准备什么软件优化推广公司哪家好
  • 哪个网站可以做app界面网络上如何推广网站
  • wordpress安装百度站长资源平台搜索引擎优化要考虑哪些方面
  • 文具网站建设规划书拉新推广赚钱的app
  • 怎么做淘宝店网站收录百度seo软件