diff --git a/src/main/java/org/apache/ibatis/plugin/Invocation.java b/src/main/java/org/apache/ibatis/plugin/Invocation.java index 14735ff9391..888eea56c8e 100644 --- a/src/main/java/org/apache/ibatis/plugin/Invocation.java +++ b/src/main/java/org/apache/ibatis/plugin/Invocation.java @@ -35,14 +35,16 @@ public class Invocation { private final Object target; private final Method method; private final Object[] args; + private final List interceptors; - public Invocation(Object target, Method method, Object[] args) { + public Invocation(Object target, Method method, Object[] args, List interceptors) { if (!targetClasses.contains(method.getDeclaringClass())) { throw new IllegalArgumentException("Method '" + method + "' is not supported as a plugin target."); } this.target = target; this.method = method; this.args = args; + this.interceptors = interceptors; } public Object getTarget() { @@ -57,8 +59,14 @@ public Object[] getArgs() { return args; } - public Object proceed() throws InvocationTargetException, IllegalAccessException { - return method.invoke(target, args); + public Object proceed() throws Throwable { + int intSize = interceptors.size(); + if ((intSize --)== 0) { + return method.invoke(target, args); + } + Interceptor interceptor = interceptors.get(intSize); + return interceptor.intercept(this); + } } diff --git a/src/main/java/org/apache/ibatis/plugin/Plugin.java b/src/main/java/org/apache/ibatis/plugin/Plugin.java index e4f829221ed..ef894fa5d0c 100644 --- a/src/main/java/org/apache/ibatis/plugin/Plugin.java +++ b/src/main/java/org/apache/ibatis/plugin/Plugin.java @@ -18,13 +18,14 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; - import org.apache.ibatis.reflection.ExceptionUtil; -import org.apache.ibatis.util.MapUtil; + /** * @author Clinton Begin @@ -32,13 +33,15 @@ public class Plugin implements InvocationHandler { private final Object target; - private final Interceptor interceptor; - private final Map, Set> signatureMap; + private final Map> interceptorMap; - private Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) { + private Plugin(Object target, Map> interceptorMap) { this.target = target; - this.interceptor = interceptor; - this.signatureMap = signatureMap; + this.interceptorMap = interceptorMap; + } + + public Map> getInterceptorMap() { + return interceptorMap; } public static Object wrap(Object target, Interceptor interceptor) { @@ -46,24 +49,41 @@ public static Object wrap(Object target, Interceptor interceptor) { Class type = target.getClass(); Class[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { - return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); - } + if (Proxy.isProxyClass(target.getClass())) { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(target); + if (invocationHandler instanceof Plugin) { + Map> interceptorMap = ((Plugin) invocationHandler).getInterceptorMap(); + mapping(interceptor, signatureMap, interceptorMap); + return target; + } + } + + Map> interceptorMap = new HashMap<>(); + mapping(interceptor, signatureMap, interceptorMap); + return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptorMap));} return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { - Set methods = signatureMap.get(method.getDeclaringClass()); - if (methods != null && methods.contains(method)) { - return interceptor.intercept(new Invocation(target, method, args)); - } + List interceptors = interceptorMap.get(method); + if (interceptors != null) { + return new Invocation(target, method, args, interceptors).proceed();} return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } + private static void mapping(Interceptor interceptor, Map, Set> signatureMap, Map> interceptorMap) { + for (Set methods : signatureMap.values()) { + for (Method method : methods) { + interceptorMap.computeIfAbsent(method, (key) -> new ArrayList<>()).add(interceptor); + } + } + } + private static Map, Set> getSignatureMap(Interceptor interceptor) { Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); // issue #251 @@ -74,7 +94,7 @@ private static Map, Set> getSignatureMap(Interceptor intercepto Signature[] sigs = interceptsAnnotation.value(); Map, Set> signatureMap = new HashMap<>(); for (Signature sig : sigs) { - Set methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>()); + Set methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>()); try { Method method = sig.type().getMethod(sig.method(), sig.args()); methods.add(method); diff --git a/src/test/java/org/apache/ibatis/submitted/plugin_invocation/BaseTest.java b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/BaseTest.java new file mode 100644 index 00000000000..0d06eb2a7c0 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/BaseTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2009-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.plugin_invocation; + +import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.Reader; + +class BaseTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + // create an SqlSessionFactory + try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/plugin_invocation/mybatis-config.xml")) { + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + } + + // populate in-memory database + BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), + "org/apache/ibatis/submitted/plugin_invocation/CreateDB.sql"); + } + + @Test + void shouldGetAUser() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.getUser(1); + Assertions.assertEquals("User1", user.getName()); + } + } + + @Test + void shouldInsertAUser() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = new User(); + user.setId(2); + user.setName("User2"); + mapper.insertUser(user); + } + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/plugin_invocation/Mapper.java b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/Mapper.java new file mode 100644 index 00000000000..9d0b55feb5a --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/Mapper.java @@ -0,0 +1,24 @@ +/* + * Copyright 2009-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.plugin_invocation; + +public interface Mapper { + + User getUser(Integer id); + + void insertUser(User user); + +} diff --git a/src/test/java/org/apache/ibatis/submitted/plugin_invocation/User.java b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/User.java new file mode 100644 index 00000000000..6973e5b98c3 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/plugin_invocation/User.java @@ -0,0 +1,38 @@ +/* + * Copyright 2009-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.plugin_invocation; + +public class User { + + private Integer id; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/CreateDB.sql b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/CreateDB.sql new file mode 100644 index 00000000000..e8768ba28b7 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/CreateDB.sql @@ -0,0 +1,24 @@ +-- +-- Copyright 2009-2022 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +drop table users if exists; + +create table users ( + id int, + name varchar(20) +); + +insert into users (id, name) values(1, 'User1'); diff --git a/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/Mapper.xml b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/Mapper.xml new file mode 100644 index 00000000000..5ad3da89c55 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/Mapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + insert into users values(#{id}, #{name}) + + + diff --git a/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/mybatis-config.xml b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/mybatis-config.xml new file mode 100644 index 00000000000..c3696662900 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/plugin_invocation/mybatis-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + +