Spring框架下PropertyPlaceholderConfigurer类

本文介绍Spring框架中的BeanFactoryPostProcessor接口实现——PropertyPlaceholderConfigurer。该类用于将配置信息从XML中分离到.properties文件中,便于管理和调整配置参数。通过具体示例展示了如何使用此配置器加载外部属性文件。

要了解这个类首先要弄清楚一个概念:bean factory post-processor
官方解释是这样的:
A bean factory post-processor is a java class which implements the
org.springframework.beans.factory.config.BeanFactoryPostProcessor interface. It is executed manually (in the case of the BeanFactory) or automatically (in the case of the ApplicationContext) to apply changes of some sort to an entire BeanFactory, after it has been constructed.
我理解的意思是这样的:
1.首先bean factory post-processor实现了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。
2.在BeanFactory的情况下它被手动的执行。
3.在ApplicationContext的条件下它会自动的执行。
4.最关键的一点是,是在一个类的实例被构造出来之后,对整个BeanFactory进行修改。

--------------------------------------------------------------------------------------------------------------------------------


Spring的框架中为您提供了一个 BeanFactoryPostProcessor 的实作类别: org.springframework.beans.factory.config.PropertyPlaceholderConfigurer。藉由这个类别,您可以将一些组态设定,移出至.properties档案中,如此的安排可以让XML定义档负责系统相关设定,而.properties档可以作为客户根据需求,自定义一些相关的参数。
来看一个Bean定义档的实际例子:

•beans-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
      "http://www.springframework.org/dtd/spring-beans.dtd">

<beans> 
        <bean id="configBean"
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location">
                <value>hello.properties</value>
            </property>
        </bean>

        <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">
            <property name="helloWord">
                <value>${onlyfun.caterpillar.helloWord}</value>
            </property>
        </bean>
</beans>
假设在helloBean中有许多依赖注入的属性,这些都是比较不常变动的属性,而其中helloWord会经常变动,可以透过hello.properties来简单的设定,而这个资讯已设定在configBean的location属性中:

•hello.properties
onlyfun.caterpillar.helloWord=Welcome!
在helloBean的helloWord属性中,${onlyfun.caterpillar.helloWord}将会被hello.properties的helloWord所取代。

如果有多个.properties档案,则可以透过locations属性来设定,例如:

•beans-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
      "http://www.springframework.org/dtd/spring-beans.dtd">

<beans> 
        <bean id="configBean"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>hello.properties</value>
                    <value>welcome.properties</value>
                    <value>other.properties</value>
   
                </list>
            </property>    
        </bean>

        <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">
            <property name="helloWord">
                <value>${onlyfun.caterpillar.helloWord}</value>
            </property>
            ...
        </bean>
</beans> ======================================PropertyPlaceholderConfigurer类就是bean factory post-processor的一种,它的作用是一个资源属性的配置器,能够将BeanFactory的里定义的内容放在一个以.propertis后缀的文件中。
例如---spring-context.xml----
 <bean id="propertyConfigurer"     class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
           <property name="locations">
               <list>
                   <value>/WEB-INF/jdbc.properties</value>
               </list>
           </property>
 </bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName"><value>${driver}</value></property>
      <property name="url"><value>jdbc:${dbname}</value></property>
</bean>而实际的jdbc.propertis文件是
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
而jdbc.propertis是怎样引用的呢: 将上边一段配置注册在web.xml中就可以了
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>
当然,不要忘了spring的监听器注册
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
这样,一个简单的数据源就设置完毕了。
实际上,PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义
的工具。

<think>嗯,用户现在遇到了在MFC框架下动态创建CEdit控件时需要自动监测内容修改的问题。ta希望避免手动为每个控件添加ON_EN_CHANGE消息映射函数,这种需求确实很实际,特别是当需要创建大量编辑框时,逐个添加处理函数会非常繁琐。回顾之前的对话,我已经解释了基本实现方法,但用户现在需要更智能的解决方案。用户可能正在开发一个需要动态生成多个输入字段的应用,比如数据录入系统或表单生成器。ta的真实需求应该是:在保持代码简洁性的同时确保功能完善。这个问题有几个关键点:首先,动态创建的控件需要唯一ID;其次,如何统一处理消息而不写重复代码;最后,还要考虑资源管理和线程安全。用户提供的引用材料很有帮助,特别是引用[5]提到的ON_CONTROL_RANGE宏,这正是解决问题的核心。让我思考实现方案。主要有两种技术路线:一是使用控件ID范围的消息映射,通过ON_CONTROL_RANGE宏批量处理所有控件事件;二是通过控件子类化技术(SubclassWindow)让每个控件实例自带消息处理能力。考虑到用户需要"自动监测"的特性,子类化方案可能更优雅,但实现起来稍复杂。第一种方案中,我们可以这样设计:定义连续的ID范围,比如从0x8000到0x8FFF。创建控件时分配这些ID,然后使用ON_CONTROL_RANGE(EN_CHANGE,ID_START,ID_END,OnEditChange)来捕获整个范围内所有控件的修改事件。在处理函数里通过参数区分具体是哪个控件发出的消息。这样只需要一个处理函数就能管理所有动态编辑框。不过用户可能需要更灵活的解决方案,特别是当控件ID不连续时。这就引出了第二个方案:创建CEdit的派生类,重写其虚函数,比如PreTranslateMessage或WindowProc,直接在里面处理EN_CHANGE通知。这样每个动态创建的控件都能自主处理消息,完全不需要在父窗口中添加消息映射。考虑到用户的技术水平,这个方案虽然高效但可能需要更深入的MFC知识。权衡之后,我决定优先介绍第一种方案,因为它更容易理解且代码改动小。但为了完整性,我也会简要提及其他的方案思路,确保用户有选择余地。在文档结构上,我会先展示ID范围管理方案的具体代码实现,从定义ID范围到处理函数的细节处理。然后介绍子类化方案作为备选,最后提及反射消息方案作为补充。特别要注意提醒用户资源释放问题,动态创建的控件需要手动销毁,很多人容易忽略这点。用户可能还会关心性能问题,特别是当有大量编辑框时。需要说明消息机制本身的负荷很小,但获取文本内容这种操作可能成为瓶颈。建议在处理函数中避免立即进行复杂操作,可以考虑延迟处理或缓冲机制。对了,引用[3]提到GetWindowText这个重要方法,必须在使用时特别注意内存分配和线程安全。特别是如果用户在辅助线程中创建这些控件(虽然不推荐),需要强调跨线程调用的限制。</think> 在MFC框架中,动态创建多个`CEdit`控件并自动监测内容修改可通过以下方案实现,避免为每个控件单独添加`ON_EN_CHANGE`消息映射: ### 方法一:使用控件ID范围映射 (推荐) 通过`ON_CONTROL_RANGE`宏批量处理同一类事件[[^5]]: 1. **定义控件ID范围** ```cpp #define IDC_EDIT_START 1000 #define IDC_EDIT_END 1050 // 支持最多50个动态编辑框 ``` 2. **动态创建控件** ```cpp for(int i = 0; i < nEditCount; i++) { CEdit* pEdit = new CEdit; pEdit->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, rect, this, IDC_EDIT_START + i); // 分配连续ID m_edits.Add(pEdit); // 加入控件数组 } ``` 3. **添加统一消息映射** 在消息映射表中添加: ```cpp BEGIN_MESSAGE_MAP(CYourDialog, CDialog) ON_CONTROL_RANGE(EN_CHANGE, IDC_EDIT_START, IDC_EDIT_END, OnEditChanged) END_MESSAGE_MAP() ``` 4. **实现统一处理函数** ```cpp void CYourDialog::OnEditChanged(UINT nID) { int nIndex = nID - IDC_EDIT_START; // 计算控件索引 CEdit* pEdit = (CEdit*)GetDlgItem(nID); CString strText; pEdit->GetWindowText(strText); TRACE(_T("Edit %d modified: %s\n"), nIndex, strText); } ``` ### 方法二:子类化CEdit控件 创建派生类自动处理修改事件: ```cpp class CAutoEdit : public CEdit { public: DECLARE_MESSAGE_MAP() afx_msg void OnEnChange() { TRACE(_T("Edit content changed!\n")); // 添加自定义处理逻辑 } }; BEGIN_MESSAGE_MAP(CAutoEdit, CEdit) ON_EN_CHANGE(10000, OnEnChange) // 10000为伪ID,实际不需真实ID END_MESSAGE_MAP() // 动态创建 CAutoEdit* pEdit = new CAutoEdit; pEdit->Create(WS_VISIBLE | WS_CHILD, rect, this, nAnyID); ``` ### 方法三:使用反射消息(Reflection) 在父窗口处理反射消息: ```cpp BEGIN_MESSAGE_MAP(CYourDialog, CDialog) ON_NOTIFY_REFLECT(EN_CHANGE, OnEditReflected) END_MESSAGE_MAP() void CYourDialog::OnEditReflected(NMHDR* pNMHDR, LRESULT* pResult) { CEdit* pEdit = (CEdit*)FromHandle(pNMHDR->hwndFrom); // 处理修改事件 *pResult = 0; } ``` ### 关键注意事项 1. **内存管理** 动态创建的控件必须手动销毁: ```cpp for(int i = 0; i < m_edits.GetCount(); i++) delete m_edits[i]; ``` 2. **线程安全** 控件操作需在UI线程执行,工作线程中修改文本应使用`PostMessage` 3. **性能优化** 高频修改场景建议: ```cpp // 批量修改前禁用通知 pEdit->SetRedraw(FALSE); // ...批量文本操作... pEdit->SetRedraw(TRUE); ``` > **方案对比** > - ✅ `ON_CONTROL_RANGE`:适合固定数量编辑框 > - ✅ **子类化方案**:最适合动态增删控件的场景 > - ✅ 反射消息:适合需要集中处理的简单场景 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值