From 8ccf945272866c2330693d7c774cd80e47fe11b5 Mon Sep 17 00:00:00 2001 From: FuriousPws002 <1938485828@qq.com> Date: Thu, 18 Apr 2024 22:24:58 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=8F=92=E4=BB=B6Pointcut=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/ibatis/plugin/Invocation.java | 35 +++++++++++++++ .../ibatis/plugin/PointcutDefinition.java | 44 +++++++++++++++++++ .../ibatis/plugin/PointcutRegistry.java | 44 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 src/main/java/org/apache/ibatis/plugin/Invocation.java create mode 100644 src/main/java/org/apache/ibatis/plugin/PointcutDefinition.java create mode 100644 src/main/java/org/apache/ibatis/plugin/PointcutRegistry.java diff --git a/src/main/java/org/apache/ibatis/plugin/Invocation.java b/src/main/java/org/apache/ibatis/plugin/Invocation.java new file mode 100644 index 0000000..a543847 --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/Invocation.java @@ -0,0 +1,35 @@ +package org.apache.ibatis.plugin; + +import java.lang.reflect.Method; + +/** + * @author furious 2024/4/18 + */ +public class Invocation { + + private final Object target; + private final Method method; + private final Object[] args; + + public Invocation(Object target, Method method, Object[] args) { + this.target = target; + this.method = method; + this.args = args; + } + + public T getTarget() { + return (T)target; + } + + public Method getMethod() { + return method; + } + + public Object[] getArgs() { + return args; + } + + public T proceed() throws Exception { + return (T)method.invoke(target, args); + } +} diff --git a/src/main/java/org/apache/ibatis/plugin/PointcutDefinition.java b/src/main/java/org/apache/ibatis/plugin/PointcutDefinition.java new file mode 100644 index 0000000..93ef4f7 --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/PointcutDefinition.java @@ -0,0 +1,44 @@ +package org.apache.ibatis.plugin; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * @author furious 2024/4/18 + */ +public class PointcutDefinition { + + private final Class type; + private final Method method; + private final Class[] args; + private final String id; + + public PointcutDefinition(Method method, PointcutRegistry registry) { + this.type = method.getDeclaringClass(); + this.method = method; + this.args = method.getParameterTypes(); + this.id = id(method); + registry.getDefinitions().put(id, this); + } + + public static String id(Method method) { + return String.join("_", method.getDeclaringClass().getSimpleName(), method.getName(), Arrays.stream(method.getParameterTypes()).map(Class::getSimpleName).collect(Collectors.joining("_"))); + } + + public Class getType() { + return type; + } + + public Method getMethod() { + return method; + } + + public Class[] getArgs() { + return args; + } + + public String getId() { + return id; + } +} diff --git a/src/main/java/org/apache/ibatis/plugin/PointcutRegistry.java b/src/main/java/org/apache/ibatis/plugin/PointcutRegistry.java new file mode 100644 index 0000000..53cf926 --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/PointcutRegistry.java @@ -0,0 +1,44 @@ +package org.apache.ibatis.plugin; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.executor.resultset.ResultSetHandler; +import org.apache.ibatis.executor.statement.StatementHandler; + +/** + * @author furious 2024/4/18 + */ +public class PointcutRegistry { + + public static final String Executor_update_MappedStatement_Object = "Executor_update_MappedStatement_Object"; + public static final String Executor_query_MappedStatement_Object = "Executor_query_MappedStatement_Object"; + public static final String ParameterHandler_getParameterObject_ = "ParameterHandler_getParameterObject_"; + public static final String ParameterHandler_setParameters_PreparedStatement = "ParameterHandler_setParameters_PreparedStatement"; + public static final String ResultSetHandler_handleResultSets_Statement = "ResultSetHandler_handleResultSets_Statement"; + public static final String StatementHandler_parameterize_Statement = "StatementHandler_parameterize_Statement"; + public static final String StatementHandler_update_Statement = "StatementHandler_update_Statement"; + public static final String StatementHandler_prepare_Connection_Integer = "StatementHandler_prepare_Connection_Integer"; + public static final String StatementHandler_query_Statement = "StatementHandler_query_Statement"; + public static final String StatementHandler_getBoundSql_ = "StatementHandler_getBoundSql_"; + + private final Map definitions = new LinkedHashMap<>(); + + public PointcutRegistry() { + Arrays.stream(Executor.class.getMethods()).forEach(m -> new PointcutDefinition(m, this)); + Arrays.stream(ParameterHandler.class.getMethods()).forEach(m -> new PointcutDefinition(m, this)); + Arrays.stream(ResultSetHandler.class.getMethods()).forEach(m -> new PointcutDefinition(m, this)); + Arrays.stream(StatementHandler.class.getMethods()).forEach(m -> new PointcutDefinition(m, this)); + } + + public Map getDefinitions() { + return definitions; + } + + public PointcutDefinition getDefinition(String key) { + return definitions.get(key); + } +} From be1213dc383210ca0b181f19529fbbf220e7cd8f Mon Sep 17 00:00:00 2001 From: FuriousPws002 <1938485828@qq.com> Date: Fri, 19 Apr 2024 22:54:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + .../statement/PrepareStatementHandler.java | 5 +++ .../executor/statement/StatementHandler.java | 4 ++ .../org/apache/ibatis/plugin/Interceptor.java | 17 +++++++ .../ibatis/plugin/InterceptorChain.java | 30 +++++++++++++ .../java/org/apache/ibatis/plugin/Plugin.java | 44 +++++++++++++++++++ .../apache/ibatis/session/Configuration.java | 18 +++++++- .../apache/ibatis/plugin/PageInterceptor.java | 28 ++++++++++++ .../apache/ibatis/session/SqlSessionTest.java | 18 ++++++++ 9 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/apache/ibatis/plugin/Interceptor.java create mode 100644 src/main/java/org/apache/ibatis/plugin/InterceptorChain.java create mode 100644 src/main/java/org/apache/ibatis/plugin/Plugin.java create mode 100644 src/test/java/org/apache/ibatis/plugin/PageInterceptor.java diff --git a/README.md b/README.md index 0912090..41222a8 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,4 @@ [5.resultType结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/5.resultType%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown")
[6.resultMap结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/6.resultMap%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown")
[7.动态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/7.%E5%8A%A8%E6%80%81sql "Markdown")
+[8.插件支持](https://github.com/FuriousPws002/mini-mybatis/wiki/8.%E6%8F%92%E4%BB%B6%E6%94%AF%E6%8C%81 "Markdown")
diff --git a/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java b/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java index 129b1aa..6e6f88f 100644 --- a/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java +++ b/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java @@ -61,4 +61,9 @@ public List query(Statement statement) throws SQLException { ps.execute(); return resultSetHandler.handleResultSets(ps); } + + @Override + public BoundSql getBoundSql() { + return boundSql; + } } diff --git a/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java b/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java index f50fefb..5de9a1e 100644 --- a/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java +++ b/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java @@ -5,6 +5,8 @@ import java.sql.Statement; import java.util.List; +import org.apache.ibatis.mapping.BoundSql; + /** * @author furious 2024/4/8 */ @@ -17,4 +19,6 @@ public interface StatementHandler { void parameterize(Statement statement) throws SQLException; List query(Statement statement) throws SQLException; + + BoundSql getBoundSql(); } diff --git a/src/main/java/org/apache/ibatis/plugin/Interceptor.java b/src/main/java/org/apache/ibatis/plugin/Interceptor.java new file mode 100644 index 0000000..1316d5c --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/Interceptor.java @@ -0,0 +1,17 @@ +package org.apache.ibatis.plugin; + +import org.apache.ibatis.session.Configuration; + +/** + * @author furious 2024/4/19 + */ +public interface Interceptor { + + String pointcut(); + + Object intercept(Invocation invocation) throws Throwable; + + default Object plugin(Object target, Configuration configuration) { + return Plugin.wrap(target, configuration, this); + } +} diff --git a/src/main/java/org/apache/ibatis/plugin/InterceptorChain.java b/src/main/java/org/apache/ibatis/plugin/InterceptorChain.java new file mode 100644 index 0000000..7a9eea8 --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/InterceptorChain.java @@ -0,0 +1,30 @@ +package org.apache.ibatis.plugin; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.ibatis.session.Configuration; + +/** + * @author furious 2024/4/19 + */ +public class InterceptorChain { + + private final Configuration configuration; + private final List interceptors = new ArrayList<>(); + + public InterceptorChain(Configuration configuration) { + this.configuration = configuration; + } + + public T pluginAll(Object target) { + for (Interceptor interceptor : interceptors) { + target = interceptor.plugin(target, configuration); + } + return (T) target; + } + + public void addInterceptor(Interceptor interceptor) { + interceptors.add(interceptor); + } +} diff --git a/src/main/java/org/apache/ibatis/plugin/Plugin.java b/src/main/java/org/apache/ibatis/plugin/Plugin.java new file mode 100644 index 0000000..cf2eeb2 --- /dev/null +++ b/src/main/java/org/apache/ibatis/plugin/Plugin.java @@ -0,0 +1,44 @@ +package org.apache.ibatis.plugin; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Objects; + +import org.apache.ibatis.session.Configuration; + +/** + * @author furious 2024/4/19 + */ +public class Plugin implements InvocationHandler { + + private final Object target; + private final Configuration configuration; + private final Interceptor interceptor; + + public Plugin(Object target, Configuration configuration, Interceptor interceptor) { + this.target = target; + this.configuration = configuration; + this.interceptor = interceptor; + } + + public static Object wrap(Object target, Configuration configuration, Interceptor interceptor) { + String pointcut = interceptor.pointcut(); + PointcutDefinition definition = configuration.getPointcutRegistry().getDefinition(pointcut); + //不是当前接口类型不代理 + if (!definition.getType().isAssignableFrom(target.getClass())) { + return target; + } + return Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[]{definition.getType()}, new Plugin(target, configuration, interceptor)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + PointcutDefinition definition = configuration.getPointcutRegistry().getDefinition(interceptor.pointcut()); + if (!Objects.equals(definition.getId(), PointcutDefinition.id(method))) { + //没有代理该方法,直接返回 + return method.invoke(target, args); + } + return interceptor.intercept(new Invocation(target, method, args)); + } +} diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index c00a47e..ea4c3e9 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -18,6 +18,10 @@ import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.InterceptorChain; +import org.apache.ibatis.plugin.Plugin; +import org.apache.ibatis.plugin.PointcutRegistry; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; import org.apache.ibatis.type.TypeHandlerRegistry; @@ -37,6 +41,8 @@ public class Configuration { private final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(); private final LanguageDriver languageDriver = new XMLLanguageDriver(); private final Map resultMaps = new HashMap<>(); + private final PointcutRegistry pointcutRegistry = new PointcutRegistry(); + private final InterceptorChain interceptorChain = new InterceptorChain(this); public Configuration() { } @@ -82,7 +88,9 @@ public void setDataSource(DataSource dataSource) { } public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { - return new PrepareStatementHandler(executor, mappedStatement, parameterObject, boundSql); + StatementHandler statementHandler = new PrepareStatementHandler(executor, mappedStatement, parameterObject, boundSql); + statementHandler = interceptorChain.pluginAll(statementHandler); + return statementHandler; } public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { @@ -108,4 +116,12 @@ public void addResultMap(ResultMap resultMap) { public ResultMap getResultMap(String id) { return resultMaps.get(id); } + + public PointcutRegistry getPointcutRegistry() { + return pointcutRegistry; + } + + public void addInterceptor(Interceptor interceptor) { + interceptorChain.addInterceptor(interceptor); + } } diff --git a/src/test/java/org/apache/ibatis/plugin/PageInterceptor.java b/src/test/java/org/apache/ibatis/plugin/PageInterceptor.java new file mode 100644 index 0000000..82d79d5 --- /dev/null +++ b/src/test/java/org/apache/ibatis/plugin/PageInterceptor.java @@ -0,0 +1,28 @@ +package org.apache.ibatis.plugin; + +import java.sql.Connection; + +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; + +/** + * @author furious 2024/4/19 + */ +public class PageInterceptor implements Interceptor { + @Override + public String pointcut() { + return PointcutRegistry.StatementHandler_prepare_Connection_Integer; + } + + @Override + public Object intercept(Invocation invocation) throws Throwable { + StatementHandler statementHandler = invocation.getTarget(); + BoundSql original = statementHandler.getBoundSql(); + Object[] args = invocation.getArgs(); + Connection connection = (Connection) args[0]; + //不判读是否为select语句,limit仅为示范参数 + String sql = original.getSql(); + sql += " LIMIT 1,1"; + return connection.prepareStatement(sql); + } +} diff --git a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java index d14dd7d..a8d31d8 100644 --- a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java +++ b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java @@ -7,6 +7,7 @@ import org.apache.ibatis.dao.UserMapper; import org.apache.ibatis.entity.UserDO; import org.apache.ibatis.entity.UserDTO; +import org.apache.ibatis.plugin.PageInterceptor; import org.apache.ibatis.session.defaults.DefaultSqlSession; import org.junit.Assert; import org.junit.Test; @@ -158,6 +159,23 @@ public void queryForeachTag() { Assert.assertNotNull(list); } + @Test + public void queryPointcut() { + Configuration configuration = new Configuration(); + configuration.addInterceptor(new PageInterceptor()); + configuration.setDataSource(DataSourceBuilderTest.build()); + configuration.addMapper(UserMapper.class); + SqlSession sqlSession = new DefaultSqlSession(configuration); + UserMapper userMapper = sqlSession.getMapper(UserMapper.class); + List idList = new ArrayList<>(); + idList.add(1L); + idList.add(2L); + List list = userMapper.listForeach(idList); + Assert.assertNotNull(list); + Assert.assertEquals(list.size(), 1); + } + + private UserMapper getUserMapper() { Configuration configuration = new Configuration(); configuration.setDataSource(DataSourceBuilderTest.build());