OSGi集成iBatis

       要将iBatis集成到OSGi环境中,主要完成的就是各bundle中的sqlmap配置文件的动态加载与移除。而iBatis的api中并没有提供直接移除sqlmap中statement的api,而且在iBatis中也并没有将statement按照namespace来存放,而仅仅是将namespace作为statement的id的一个前缀。所以我们首先要做的就是对iBatis的代码进行改造,以达到能动态添加和移除sqlmap的功能(本文章中所用到的是iBatis 2.3.4)
       此改造是在Spring的 SqlMapClientFactoryBean 的基础上进行,因为 SqlMapClientFactoryBean 已经基本实现了sqlmap文件的动态增加,首先我们编写一个类继承 SqlMapClientFactoryBean,然后在Spring DM的配置文件中将BundleContext注入到这个类中(Spring DM的配置过程可以到百度上面搜索),如下:
   <bean id="sqlMapClient" class="com.yinhai.ibatis.ext.DySqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:sql-map-config.xml" />
        <property name="context" ref="bundleContext"/>
        <property name="dataSource" ref="dataSource" />
    </bean>
在setContext方法中来处理bundle中的配置文件,也可以自己再写一个init方法
 
    public void setContext(BundleContext context) {
        this.context = context;
        Bundle[] bundles = context.getBundles();
        for (Bundle bundle : bundles) {
            if (bundle.getState() ==Bundle.ACTIVE) {
                //SQLMAP_PATTERN为sqlMap的后缀名,此处我用的是"*SqlMap.xml"
                Enumeration<URL> enu = bundle.findEntries("/"SQLMAP_PATTERN,
                        true);
                if (enu != null) {
                    while (enu.hasMoreElements()) {
                        resourceList.add(new UrlResource(enu.nextElement()));
                    }
                }
            }
        }
        //设置已经加载的Bundle中的sqlmap文件
 
        setMappingLocations(resourceList.toArray(new Resource[resourceList.size()]));
        context.addBundleListener(new BundleListener() {
 
            @Override
            public void bundleChanged(BundleEvent event) {
                if (event.getType() == BundleEvent.STARTED) {
                    addSqlmapsFromBundle(event.getBundle());
                } else if (event.getType() == BundleEvent.STOPPED) {
                    removeSqlMapsFromBundle(event.getBundle());
                }
            }
        });
    }
此方法中已经将mappingLocations设置好了,接下来就是将这些sqlmap配置文件加载到iBatis中,需要重写方法
   protected SqlMapClient buildSqlMapClient(Resource[] configLocations,
            Resource[] mappingLocations, Properties properties)
            throws IOException
Spring的实现如下:
    protected SqlMapClient buildSqlMapClient(Resource[] configLocations,
            Resource[] mappingLocations, Properties properties)
            throws IOException {
        if (ObjectUtils.isEmpty(configLocations)) {
            throw new IllegalArgumentException(
                    "At least 1 'configLocation' entry is required");
        }
 
        SqlMapClient client = null;
        SqlMapConfigParser configParser = new SqlMapConfigParser();
        InputStream is;
        for (Resource configLocation : configLocations) {
            is = configLocation.getInputStream();
            try {
                client = configParser.parse(is, properties);
            } catch (RuntimeException ex) {
                throw new NestedIOException("Failed to parse config resource: "
                        + configLocation, ex.getCause());
            }
        }
 
        if (mappingLocations != null) {
            //SqlMapParser对象为局部变量,由于后面需要用到,所以应将它设置成类的成员变量。
            SqlMapParser mapParser = SqlMapParserFactory
                    .createSqlMapParser(configParser);
            for (Resource mappingLocation : mappingLocations) {
                try {
                    mapParser.parse(mappingLocation.getInputStream());
                } catch (NodeletException ex) {
                    throw new NestedIOException(
                            "Failed to parse mapping resource: "
                                    + mappingLocation, ex);
                }
            }
        }
 
        return client;
    }
 
修改后的代码如下:
    private SqlMapParser mapParser = null;
    ....
    
    protected SqlMapClient buildSqlMapClient(Resource[] configLocations,
            Resource[] mappingLocations, Properties properties)
            throws IOException {
        if (ObjectUtils.isEmpty(configLocations)) {
            throw new IllegalArgumentException(
                    "At least 1 'configLocation' entry is required");
        }
        SqlMapClient client = null;
        SqlMapConfigParser configParser = new SqlMapConfigParser();
 
        for (int i = 0; i < configLocations.length; i++) {
            InputStream is = configLocations[i].getInputStream();
            try {
                client = configParser.parse(is, properties);
            } catch (RuntimeException ex) {
                throw new NestedIOException("Failed to parse config resource: "
                        + configLocations[i], ex.getCause());
            }
        }
        mapParser = SqlMapParserFactory.createSqlMapParser(configParser);
        client = new DySqlMapClient(client, mapParser, configParser,
                configLocations);
        if (mappingLocations != null) {
            for (Resource mappingLocation : mappingLocations) {
                try {
                    mapParser.parse(mappingLocation.getInputStream());
                } catch (NodeletException ex) {
                    throw new NestedIOException(
                            "Failed to parse mapping resource: "
                                    + mappingLocation, ex);
                }
            }
        }
        return client;
    }
 
动态增加SqlMap配置
上面完成了对已经启动的bundle中sqlmap文件的加载,而在之后启动的bundle则在addSqlmapsFromBundle方法中处理
    private void addSqlmapsFromBundle(Bundle bundle){
        Enumeration<URL> enu = bundle.findEntries("/",
                SQLMAP_PATTERNtrue);
        logger.info("添加sqlMaps配置");
        if (enu != null) {
            while (enu.hasMoreElements()) {
                try {
                    DySqlMapClientFactoryBean.this.mapParser
                            .parse(new UrlResource(enu
                                    .nextElement())
                                    .getInputStream());
                } catch (NodeletException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
这儿就用到了刚才存放在类中的成员变量 mapParser, 此Parser就是正在使用的那一个,所以通过它可以动态向正在运行的iBatis中加入sqlMap配置。
 
动态删除SqlMap配置
    之前说过,由于iBatis的api并没有提供直接的支持,所以只能找到iBatis把statement存放在哪儿,然后通过反射或者修改源代码的方式将api暴露出来。跟踪iBatis的源码,可以发现,所有sqlMap中的statement都存放在 SqlMapExecutorDelegate 类中的 mappedStatements 变量中,此变量为一个HashMap,key为statement的id加上namespace(如果开启了namespace的话,所以我们只要将这个HashMap暴露出来,就一切搞定,所以要先修改这个类,将 mappedStatements 暴露出来,而这个类本身又是存放在 XmlParserState 对象的 config 对象中, XmlParserState 类则是直接和我们上面的 mapParser 关联,但 mapParser 并没有提供取得 XmlParserState 的方法,Spring是直接通过反射得到的,我们可以直接修改源代码,加一个方法, SqlMapParser 代码中增加如下代码:
    
 /**
   * FIXME 增加get方法,取得state,以便动态移除statement。
   * @return
   */
  public XmlParserState getState(){
      return state;
  }
 
SqlMapExecutorDelegate 代码增加
     
    public Map getMappedStatement() {
        return mappedStatements;
    }
 
这时我们就可以在DySqlMapClientFactoryBean中来移除Sqlmap了
    
   /**
     * 移除bundle中sqlMap对应的配置
     * @param bundle
     */
    private void removeSqlMapsFromBundle(Bundle bundle) {
        Enumeration<URL> enu = bundle.findEntries("/",SQLMAP_PATTERNtrue);
        if (enu != null) {
            Map statements = ((DySqlMapExecutorDelegate) mapParser.getState()
                    .getConfig().getDelegate()).getDelegate()
                    .getMappedStatement();
            Map resultMaps = ((DySqlMapExecutorDelegate) mapParser.getState()
                    .getConfig().getDelegate()).getDelegate().getResultMap();
            while (enu.hasMoreElements()) {
                InputStream is = null;
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                try {
                    is = enu.nextElement().openStream();
                    SqlMapConfigUtils.copy(is, bos);
                    // 移除statement
                    List<String> states = SqlMapConfigUtils
                            .readSqlMap(new ByteArrayInputStream(bos
                                    .toByteArray()));
                    for (String id : states) {
                        synchronized (statements) {
                            if (statements.containsKey(id)) {
                                statements.remove(id);
                                logger.info("移除id为" + id + "的statement");
                            }
                        }
                    }
                    // 移除statement
                    List<String> results = SqlMapConfigUtils
                            .readResultMap(new ByteArrayInputStream(bos
                                    .toByteArray()));
                    for (String id : results) {
                        synchronized (resultMaps) {
                            if (resultMaps.containsKey(id)) {
                                resultMaps.remove(id);
                                logger.info("移除id为" + id + "的resultMap");
                            }
                        }
                    }
 
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                } catch (JDOMException e) {
                    logger.error(e.getMessage(), e);
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    bos = null;
                }
            }
        }
    }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot框架中集成OSGi(Open Service Gateway Initiative)可以通过使用Apache Felix或Eclipse Equinox来实现。以下是一般的步骤: 1. 添加OSGi的依赖:在您的Spring Boot项目的`pom.xml`文件中添加OSGi容器(如Apache Felix或Eclipse Equinox)的依赖。 2. 创建OSGi Bundle:将您的Spring Boot应用程序转换为一个OSGi Bundle。这可以通过在`pom.xml`文件中添加适当的插件配置,以及在Spring Boot应用程序的类路径中添加OSGi相关的元数据。 3. 定义OSGi服务:将您的Spring Bean声明为OSGi服务。这可以通过使用适当的注解(如`@Component`和`@Service`)来实现,并确保在OSGi Bundle的元数据中正确地声明这些服务。 4. 配置OSGi容器:根据所选择的OSGi容器,配置相关的配置文件(如`config.ini`或`felix.properties`),以指定Bundle的启动顺序、依赖关系和其他配置信息。 5. 运行OSGi容器:启动OSGi容器并部署您的Spring Boot应用程序的OSGi Bundle。这可以通过在命令行中执行相应的命令,或通过使用相应的库和API来实现。 需要注意的是,在将Spring Boot集成OSGi中时,可能会遇到一些挑战和限制。例如,由于Spring Boot通常使用自动配置和自动装配机制,可能需要进行一些额外的配置和适应,以确保在OSGi环境中正确加载和启动应用程序。 此外,还应考虑到OSGi的动态模块化特性和服务注册/发现机制,以充分利用OSGi的优势。 具体的集成步骤和配置可能会根据所选的OSGi容器和具体的项目需求而有所不同。因此,建议查阅相关的文档和教程,并根据实际情况进行配置集成

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值