• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
isuning
博客园    首页    新随笔    联系   管理    订阅  订阅
容器扩展点-6

目录

        • 自定义 BeanPostProcessor
        • 自定义配置元数据`BeanFactoryPostProcessor`
        • 自定义实例化逻辑`FactoryBean`

通常,应用程序开发人员不需要子类化 ApplicationContext 实现类。相反,可以通过插入特殊集成接口的实现来扩展 Spring IoC 容器。接下来的几节描述了这些集成接口。

自定义 BeanPostProcessor

该BeanPostProcessor接口定义了您可以实现的回调方法,以提供您自己的(或覆盖容器的默认)实例化逻辑、依赖关系解析逻辑等。如果你想在 Spring 容器完成实例化、配置和初始化 bean 之后实现一些自定义逻辑,你可以插入一个或多个自定义BeanPostProcessor实现。

您可以配置多个实例,并且可以通过设置属性BeanPostProcessor来控制这些BeanPostProcessor实例的运行顺序。order仅当BeanPostProcessor实现Ordered 接口时才能设置此属性。如果你自己写BeanPostProcessor,你也应该考虑实现Ordered接口。

通过拦截方法,将返回值转换为大写

public class IService  {

    public String hello() {
        return "Hello World"; 
    }

}
package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
         // 为当前 bean 对象注册监控代理对象,负责增强 bean 对象方法的能力
        Class beanClass = bean.getClass();
        if (beanClass == IService.class) {
            Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                       
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          
                            String result = (String) method.invoke(bean, args);
                            return result.toUpperCase();
                        }
                    });
            return proxy;
        }
        return bean;
    }
}

以下beans元素使用InstantiationTracingBeanPostProcessor:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        https://www.springframework.org/schema/lang/spring-lang.xsd">
 
    <bean id="IService" class="scripting.IService"/>
   
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

请注意 是如何InstantiationTracingBeanPostProcessor定义的。它甚至没有名字,而且,因为它是一个 bean,它可以像任何其他 bean 一样被依赖注入。

以下 Java 应用程序运行上述代码和配置:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
       BaseService serviceObj = (BaseService) factory.getBean("iService");
	   System.out.println(serviceObj.hello());
    }

}

自定义配置元数据BeanFactoryPostProcessor

我们要看的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor. 此接口的语义与 的语义相似,但BeanPostProcessor有一个主要区别:BeanFactoryPostProcessor对 bean 配置元数据进行操作。也就是说,Spring IoC 容器允许BeanFactoryPostProcessor读取配置元数据并可能在容器实例化除实例之外的任何 bean之前BeanFactoryPostProcessor更改它。

您可以使用标准 Java格式PropertySourcesPlaceholderConfigurer将 bean 定义中的属性值外部化到单独的文件中。Properties这样做使部署应用程序的人员能够自定义特定于环境的属性,例如数据库 URL 和密码,而无需修改容器的主要 XML 定义文件或文件的复杂性或风险。

考虑以下基于 XML 的配置元数据片段,其中DataSource 定义了带有占位符值的 :

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

该示例显示了从外部Properties文件配置的属性。在运行时,将 PropertySourcesPlaceholderConfigurer应用于替换 DataSource 的某些属性的元数据。要替换的值被指定为表单的占位符${property-name},它遵循 Ant 和 log4j 以及 JSP EL 样式。

自定义实例化逻辑FactoryBean

您可以org.springframework.beans.factory.FactoryBean为本身是工厂的对象实现接口。

该FactoryBean接口是 Spring IoC 容器的实例化逻辑的可插入点。如果您有复杂的初始化代码,用 Java 更好地表达而不是(可能)冗长的 XML,您可以创建自己的 FactoryBean,在该类中编写复杂的初始化,然后将您的自定义FactoryBean插入容器中。

该FactoryBean<T>接口提供了三种方法:

  • T getObject():返回此工厂创建的对象的实例。该实例可能会被共享,具体取决于该工厂是返回单例还是原型。
  • boolean isSingleton():true如果FactoryBean返回单例或 false其他,则返回。此方法的默认实现返回true.
  • Class<?> getObjectType():返回getObject()方法返回的对象类型,或者null如果事先不知道类型。

FactoryBeanSpring 框架中的许多地方都使用了概念和接口。

@Component
public class HelloFactoryBean implements FactoryBean<String> {

    String s = "hello";

    @Override
    public ZoneId getObject() throws Exception {
        return s;
    }

    @Override
    public Class<?> getObjectType() {
        return String.class;
    }
}

当一个Bean实现了FactoryBean接口后,Spring会先实例化这个工厂,然后调用getObject()创建真正的Bean。getObjectType()可以指定创建的Bean的类型,因为指定类型不一定与实际类型一致,可以是接口或抽象类。

因此,如果定义了一个FactoryBean,要注意Spring创建的Bean实际上是这个FactoryBean的getObject()方法返回的Bean。为了和普通Bean区分,我们通常都以XxxFactoryBean命名。

posted on 2022-05-01 22:18  吕布辕门  阅读(34)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3