Jrebel 在重新加载 Mapper XML 文件时清除 Mybatis 拦截器

Jrebel clear Mybatis interceptors when reload Mapper XML files

我正在使用 Jrebel 6.3.0 为我的 Spring 引导 Web 应用程序热重载 Mybatis 的映射器 XML。并且我使用了Java配置来配置Mybatis。

@Configuration
@ConditionalOnClass({ PageInterceptor.class })
@EnableConfigurationProperties(MybatisPageProperties.class)
@AutoConfigureBefore(MybatisAutoConfiguration.class)
public class MyBatisPageAutoConfiguration implements ApplicationContextAware {
    private static final Logger LOG = LoggerFactory.getLogger(MyBatisPageAutoConfiguration.class);

    @Autowired
    private MybatisPageProperties properties;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanUtil.setApplicationContext(applicationContext);
    }

    /**
     * 
     * 
     * @return
     */
    @Bean
    public PageInterceptor pageInterceptor() {
        LOG.info("========PageInterceptor========");
        PageInterceptor pageInterceptor = new PageInterceptor();

        Properties p = new Properties();
        p.setProperty("dialect", properties.getDialect());
        p.setProperty("sqlIdRegex", properties.getSqlIdRegex());
        pageInterceptor.setProperties(p);

        return pageInterceptor;
    }

我发现 Jrebel 在重新加载映射器 xml 文件时清除了拦截器。

org.zeroturnaround.jrebel.mybatis.SqlMapReloader

  private void reconfigure() {
    log.infoEcho("Reloading SQL maps");

    this.conf.reinit(); // Clear loadedResources and interceptors

    reloadedResources.set(Collections.synchronizedSet(new HashSet()));
    enterReloading();
    try {
      if (this.confBuilder != null)
        this.confBuilder.reinit();
      reconfigureAdditionalMappings();


      exitReloading();
      reloadedResources.remove();
    }
    finally
    {
      exitReloading();
      reloadedResources.remove();
    }
  }

  .........

  private void reconfigureAdditionalMappings() {
    for (ResourceDesc rd : (ResourceDesc[])this.additionalMappings.toArray(new ResourceDesc[0])) {
      reconfigureMapping(rd);
    }
  }

  private void reconfigureMapping(ResourceDesc rd) {
    org.apache.ibatis.session.Configuration c = (org.apache.ibatis.session.Configuration)this.conf;
    try { // Only reload loadedResources from Mapper XML files.
      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(ResourceUtil.asInputStream(rd.url), c, rd.path, c.getSqlFragments());

      xmlMapperBuilder.parse();
    }
    catch (Exception e) {
      if ((e.getCause() instanceof java.io.FileNotFoundException)) removeMappingForDeletedResource(rd); else {
        throw new RuntimeException("Failed to parse mapping resource: '" + rd.url + "'", e);
      }
    } finally {
      ErrorContext.instance().reset();
    }
  }

org.zeroturnaround.jrebel.mybatis.cbp.ConfigurationCBP

public class ConfigurationCBP
  extends JavassistClassBytecodeProcessor
{
  public void process(ClassPool cp, ClassLoader cl, CtClass ctClass)
    throws Exception
  {
    ctClass.addInterface(cp.get(JrConfiguration.class.getName()));

    ctClass.addField(new CtField(cp.get(SqlMapReloader.class.getName()), "reloader", ctClass));

    CtConstructor[] constructors = ctClass.getConstructors();
    for (int i = 0; i < constructors.length; i++) {
      CtConstructor constructor = constructors[i];
      if (constructor.callsSuper()) {
        constructor.insertAfter("reloader = new " + SqlMapReloader.class.getName() + "([=13=]);");
      }
    }

    ctClass.addMethod(CtNewMethod.make("public " + SqlMapReloader.class
      .getName() + " getReloader() {" + "  return reloader;" + "}", ctClass));




    ctClass.addMethod(CtNewMethod.make("public void reinit() {  loadedResources.clear();  ((" + JrInterceptorChain.class


      .getName() + ") interceptorChain).jrClear();" + "}", ctClass));



    ctClass.getDeclaredMethod("isResourceLoaded").insertAfter("if (reloader.doReload()) {  loadedResources.remove();  $_ = false;}");
  }
}

有没有办法让 Jrebel 保留拦截器?

我的项目是 web app,我们不需要在 prod-env 中进行此更改,我们只是在 dev-env 中使用 jrebel,所以只需修改从源 jar 中获取的源文件,并将其放在 src/test/java,它会被构建到classes目录,并替换原来jar中的class。不要将其放入 src/main/java 文件夹。

org.apache.ibatis.builder.xml.XMLMapperBuilder

...
public class XMLMapperBuilder extends BaseBuilder {
    private static List<Interceptor> interceptors = new ArrayList<Interceptor>(); // Added by yuanjinyong
...
    public void parse() {
...
        parsePendingStatements();

        // Added by yuanjinyong
        if (interceptors.size() == 0) {
            interceptors.addAll(configuration.getInterceptors());
        }
        if (configuration.getInterceptors().size() == 0) {
            for (Interceptor interceptor : interceptors) {
                configuration.addInterceptor(interceptor);
            }
        }
    }
...
}