FlexyPool如何同时支持连接代理和装饰器
代理人
FlexyPool监视连接池使用情况,因此需要拦截连接关闭方法调用。
为了简单起见,第一个版本为此目的依赖动态代理:
private static class ConnectionInvocationHandler implements InvocationHandler { public static final String CLOSE_METHOD_NAME = "close"; private final Connection target; private final ConnectionCallback callback; public ConnectionInvocationHandler( Connection target, ConnectionCallback callback) { this.target = target; this.callback = callback; } @Override public Object invoke( Object proxy, Method method, Object[] args) throws Throwable { if (CLOSE_METHOD_NAME.equals(method.getName())) { callback.close(); } return method.invoke(target, args); } }
代理调用的速度可能比装饰器慢,装饰器使用直接调用来调用目标方法。
由于所有连接池仍然使用代理,因此添加另一个代理层只会增加更多的呼叫时间开销,因此现在FlexyPool也支持连接装饰器。
装饰工
ConnectionDecorator包装基础数据库连接,将所有调用委派给实际的对象实例。 就像它的代理服务器代理一样,只有close方法可以执行任何额外的逻辑:
public class ConnectionDecorator implements Connection { private final Connection target; private final ConnectionCallback callback; public ConnectionDecorator( Connection target, ConnectionCallback callback) { this.target = target; this.callback = callback; } public Connection getTarget() { return target; } public ConnectionCallback getCallback() { return callback; } @Override public Statement createStatement() throws SQLException { return target.createStatement(); } @Override public void close() throws SQLException { callback.close(); target.close(); } /** * More methods omitted for brevity sake */ public void setSchema(String schema) throws SQLException { ReflectionUtils.invoke( target, ReflectionUtils.getMethod( target, "setSchema", String.class ), schema ); } public String getSchema() throws SQLException { return ReflectionUtils.invoke( target, ReflectionUtils.getMethod( target, "getSchema" ) ); } public void abort(Executor executor) throws SQLException { ReflectionUtils.invoke( target, ReflectionUtils.getMethod( target, "abort", Executor.class ), executor ); } public void setNetworkTimeout( Executor executor, int milliseconds) throws SQLException { ReflectionUtils.invoke( target, ReflectionUtils.getMethod( target, "setNetworkTimeout", Executor.class, int.class ), executor, milliseconds ); } public int getNetworkTimeout() throws SQLException { return (Integer) ReflectionUtils.invoke( target, ReflectionUtils.getMethod( target, "getNetworkTimeout" ) ); } }
您可能已经注意到,某些方法使用Java Reflection而不是直接方法调用:
这些方法已添加到Java 1.7中,使用Java 1.6编译项目时,直接调用将失败。 因为Java 1.6是大多数FlexyPool模块的最低要求,所以这些方法通过Java反射调用转发传入的方法调用。 省略这些方法不是可选的,因为在1.7 JVM上, Connection装饰器将没有这些方法,并且将引发类装入错误。
在至少使用Java 1.7的项目中,FlexyPool还提供了Java7ConnectionDecorator :
public class Java7ConnectionDecorator extends ConnectionDecorator { public Java7ConnectionDecorator( Connection target, ConnectionCallback callback) { super(target, callback); } @Override public void setSchema(String schema) throws SQLException { getTarget().setSchema(schema); } @Override public String getSchema() throws SQLException { return getTarget().getSchema(); } @Override public void abort(Executor executor) throws SQLException { getTarget().abort(executor); } @Override public void setNetworkTimeout( Executor executor, int milliseconds) throws SQLException { getTarget().setNetworkTimeout(executor, milliseconds); } @Override public int getNetworkTimeout() throws SQLException { return getTarget().getNetworkTimeout(); } }
此类不是核心库的一部分,包含在单独的Java 1.7兼容模块中。 要使用它,您需要添加以下Maven依赖项:
<dependency> <groupId>com.vladmihalcea.flexy-pool</groupId> <artifactId>flexy-pool-core-java7</artifactId> <version>${flexy-pool.version}</version> </dependency>
服务发现机制
从一开始,FlexyPool就为配置ConnectionProxyFactory实例提供了支持,因此切换到装饰器不需要任何繁琐的代码重构。
在1.2.4发行版之前,默认的连接提供程序是JdkConnectionProxyFactory ,它使用动态代理。
从1.2.4开始,FlexyPool使用连接装饰器作为默认的连接拦截机制。
实际的装饰器版本在运行时解析,并且加载机制由以下组件构成:
实际的连接装饰器工厂通过以下方法解决:
public ConnectionDecoratorFactory resolve() { int loadingIndex = Integer.MIN_VALUE; ConnectionDecoratorFactory connectionDecoratorFactory = null; Iterator<ConnectionDecoratorFactoryService> connectionDecoratorFactoryServiceIterator = serviceLoader.iterator(); while (connectionDecoratorFactoryServiceIterator.hasNext()) { try { ConnectionDecoratorFactoryService connectionDecoratorFactoryService = connectionDecoratorFactoryServiceIterator.next(); int currentLoadingIndex = connectionDecoratorFactoryService.loadingIndex(); if (currentLoadingIndex > loadingIndex) { ConnectionDecoratorFactory currentConnectionDecoratorFactory = connectionDecoratorFactoryService.load(); if (currentConnectionDecoratorFactory != null) { connectionDecoratorFactory = currentConnectionDecoratorFactory; loadingIndex = currentLoadingIndex; } } } catch (LinkageError e) { LOGGER.info("Couldn't load ConnectionDecoratorFactoryService on the current JVM", e); } } if (connectionDecoratorFactory != null) { return connectionDecoratorFactory; } throw new IllegalStateException("No ConnectionDecoratorFactory could be loaded!"); }
就像MetricsFactory一样 ,每个连接装饰器工厂都有一个关联的服务提供者 。 可以在运行时加载多个此类服务提供程序(默认的Java 1.6连接修饰器服务或Java 1.7之一)。 根据索引(最新的Java版本优先)和当前的JVM JDBC版本支持(在Java 1.6运行时环境中无法解析Java 1.7连接装饰器)进行选择。
结论
装饰器比代理器承担更多的配置开销,但是如果您想减少最后的性能下降,则值得考虑直接方法调用的优势。