It is recommended to add XmlConfigBuilder
to support Configuration
construction parameters
https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java#L85,L92
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
Comment From: brucelwl
@harawata
Comment From: harawata
Hello @brucelwl ,
What are you trying to achieve?
Can't you perform additional configuration on the Configuration
instance returned from XMLConfigBuilder#parse()
?
Configuration must be loaded in a particular order, so passing a partially loaded Configuration
instance might not work as you expect.
Comment From: brucelwl
@harawata
I wrote a subclass of configuration
so that we can use decorator mode to implement interceptors and improve code performance, but I found that Configuration
in XMLConfigBuilder
is fixed, At present, my approach is to modify the Configuration
object by reflection
my code:
public class InterceptorChain {
private final List<InterceptedMarker> interceptors = new ArrayList<>();
public InterceptorChain() {
}
public boolean addInterceptor(Interceptor interceptor) {
if (interceptor instanceof InterceptedMarker) {
interceptors.add((InterceptedMarker) interceptor);
return true;
}
return false;
}
/**
* target must be an implementation of T or its subclass
*/
@SuppressWarnings("unchecked")
public <T> T buildChain(T target, Class<T> tClass) {
List<InterceptedMarker> executorInterceptors = interceptors.stream()
.filter(item -> tClass.isAssignableFrom(item.getClass())).collect(Collectors.toList());
if (executorInterceptors.size() == 0) {
return target;
}
InterceptedMarker first = executorInterceptors.get(0);
InterceptedMarker next = first;
for (int i = 1; i < executorInterceptors.size(); i++) {
InterceptedMarker interceptorMarker = executorInterceptors.get(i);
next.setNext(interceptorMarker);
next = interceptorMarker;
}
next.setNext(target);
return (T) first;
}
}
public class OptimizedConfiguration extends Configuration {
protected InterceptorChain interceptorChain = new InterceptorChain();
@Override
public void addInterceptor(Interceptor interceptor) {
if (!interceptorChain.addInterceptor(interceptor)) {
super.addInterceptor(interceptor);
}
}
@Override
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = super.newParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = interceptorChain.buildChain(parameterHandler, ParameterHandler.class);
return parameterHandler;
}
@Override
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = super.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
resultSetHandler = interceptorChain.buildChain(resultSetHandler, ResultSetHandler.class);
return resultSetHandler;
}
@Override
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = super.newStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = interceptorChain.buildChain(statementHandler, StatementHandler.class);
return statementHandler;
}
@Override
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
Executor executor = super.newExecutor(transaction, executorType);
executor = interceptorChain.buildChain(executor, Executor.class);
return executor;
}
}
public class OptimizedSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
@Override
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
replaceConfiguration(parser, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
@Override
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
replaceConfiguration(parser, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
// Modify Configuration to OptimizedConfiguration by reflection
private void replaceConfiguration(XMLConfigBuilder parser, Properties properties) {
OptimizedConfiguration optimizedConfiguration = new OptimizedConfiguration();
optimizedConfiguration.setVariables(properties);
Field configurationField = ReflectionUtils.findField(XMLConfigBuilder.class, "configuration");
ReflectionUtils.makeAccessible(configurationField);
ReflectionUtils.setField(configurationField, parser, optimizedConfiguration);
Field typeAliasRegistryField = ReflectionUtils.findField(XMLConfigBuilder.class, "typeAliasRegistry");
ReflectionUtils.makeAccessible(typeAliasRegistryField);
ReflectionUtils.setField(typeAliasRegistryField, parser, optimizedConfiguration.getTypeAliasRegistry());
Field typeHandlerRegistryField = ReflectionUtils.findField(XMLConfigBuilder.class, "typeHandlerRegistry");
ReflectionUtils.makeAccessible(typeHandlerRegistryField);
ReflectionUtils.setField(typeHandlerRegistryField, parser, optimizedConfiguration.getTypeHandlerRegistry());
}
}
Comment From: harawata
@brucelwl ,
That usage is out of scope, I'm afraid. We might reconsider in the future, but please continue using reflection for now if the hack is absolutely necessary.