Skip to content

Commit bdc0f2e

Browse files
authored
Merge pull request #24 from mercyblitz/dev
Refactor
2 parents 1bcac2f + 6f00213 commit bdc0f2e

File tree

12 files changed

+894
-243
lines changed

12 files changed

+894
-243
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.microsphere.mybatis.plugin;
18+
19+
import org.apache.ibatis.executor.Executor;
20+
import org.apache.ibatis.executor.parameter.ParameterHandler;
21+
import org.apache.ibatis.executor.resultset.ResultSetHandler;
22+
import org.apache.ibatis.executor.statement.StatementHandler;
23+
import org.apache.ibatis.plugin.Interceptor;
24+
import org.apache.ibatis.plugin.Intercepts;
25+
import org.apache.ibatis.plugin.Plugin;
26+
import org.apache.ibatis.plugin.PluginException;
27+
import org.apache.ibatis.plugin.Signature;
28+
29+
import java.lang.reflect.InvocationHandler;
30+
import java.lang.reflect.Method;
31+
import java.lang.reflect.Proxy;
32+
import java.util.HashMap;
33+
import java.util.HashSet;
34+
import java.util.List;
35+
import java.util.Map;
36+
import java.util.Set;
37+
38+
import static io.microsphere.collection.ListUtils.ofList;
39+
import static io.microsphere.text.FormatUtils.format;
40+
import static java.lang.reflect.Proxy.getInvocationHandler;
41+
import static org.apache.ibatis.util.MapUtil.computeIfAbsent;
42+
43+
/**
44+
* The utilities class of {@link Plugin}
45+
*
46+
* @author <a href="mailto:mercyblitz@gmail.com">Mercy<a/>
47+
* @see Plugin
48+
* @see Intercepts
49+
* @see Signature
50+
* @since 1.0.0
51+
*/
52+
public abstract class Plugins {
53+
54+
/**
55+
* The valid target classes for {@link Interceptor}
56+
*/
57+
public static final List<Class<?>> TARGET_CLASSES = ofList(Executor.class, ParameterHandler.class,
58+
ResultSetHandler.class, StatementHandler.class);
59+
60+
/**
61+
* The size of {@link #TARGET_CLASSES}
62+
*/
63+
public static final int TARGET_CLASSES_SIZE = TARGET_CLASSES.size();
64+
65+
/**
66+
* Get the {@link Signature} map from the specified {@link Interceptor} that was annotated {@link Intercepts}
67+
*
68+
* @param interceptor the {@link Interceptor} that was annotated {@link Intercepts}
69+
* @return
70+
* @throws PluginException if No {@link Intercepts @Intercepts} annotation was found in interceptor or
71+
* no {@link Signature @Signature} was found in the {@link Intercepts @Intercepts}
72+
* @see Plugin#getSignatureMap(Interceptor)
73+
*/
74+
public static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) throws PluginException {
75+
return getSignatureMap(interceptor.getClass());
76+
}
77+
78+
/**
79+
* Get the {@link Signature} map from the specified {@link Interceptor} that was annotated {@link Intercepts}
80+
*
81+
* @param interceptorClass the {@link Interceptor} that was annotated {@link Intercepts}
82+
* @return
83+
* @throws PluginException if No {@link Intercepts @Intercepts} annotation was found in interceptorClass or
84+
* no {@link Signature @Signature} was found in the {@link Intercepts @Intercepts}
85+
* @see Plugin#getSignatureMap(Interceptor)
86+
*/
87+
public static Map<Class<?>, Set<Method>> getSignatureMap(Class<? extends Interceptor> interceptorClass) throws PluginException {
88+
Intercepts interceptsAnnotation = interceptorClass.getAnnotation(Intercepts.class);
89+
// issue #251
90+
if (interceptsAnnotation == null) {
91+
throw newPluginException("No @Intercepts annotation was found in interceptorClass : {}", interceptorClass.getName());
92+
}
93+
Signature[] sigs = interceptsAnnotation.value();
94+
// @Intercepts usually specifies one or two targets, so HashMap can be optimized
95+
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>(TARGET_CLASSES_SIZE / 2, 1.0f);
96+
for (Signature sig : sigs) {
97+
Class<?> targetType = sig.type();
98+
if (!TARGET_CLASSES.contains(targetType)) {
99+
throw newPluginException("The @Intercepts#type() = {} must be one of the target classes : {}", targetType.getName(), TARGET_CLASSES);
100+
}
101+
Set<Method> methods = computeIfAbsent(signatureMap, targetType, k -> new HashSet<>());
102+
String methodName = sig.method();
103+
try {
104+
Method method = targetType.getMethod(methodName, sig.args());
105+
methods.add(method);
106+
} catch (NoSuchMethodException e) {
107+
throw new PluginException(format("Could not find method on {} named {}", targetType, methodName), e);
108+
}
109+
}
110+
return signatureMap;
111+
}
112+
113+
/**
114+
* Get the {@link Plugin} from the specified proxy
115+
*
116+
* @param proxy the specified proxy
117+
* @return <code>null</code> if the proxy is not a {@link Plugin}
118+
*/
119+
public static Plugin getPlugin(Object proxy) {
120+
if (proxy instanceof Proxy) {
121+
InvocationHandler invocationHandler = getInvocationHandler(proxy);
122+
if (invocationHandler instanceof Plugin) {
123+
return (Plugin) invocationHandler;
124+
}
125+
}
126+
return null;
127+
}
128+
129+
protected static PluginException newPluginException(String messagePattern, Object... args) {
130+
return new PluginException(format(messagePattern, args));
131+
}
132+
}

microsphere-mybatis-core/src/test/java/io/microsphere/mybatis/plugin/InterceptingExecutorInterceptorTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import io.microsphere.mybatis.executor.LogggingExecutorInterceptor;
2020
import io.microsphere.mybatis.executor.LoggingExecutorFilter;
2121
import io.microsphere.mybatis.executor.TestExecutorFilter;
22-
import io.microsphere.mybatis.test.AbstractMyBatisTest;
22+
import io.microsphere.mybatis.test.DefaultMapperTest;
2323
import org.apache.ibatis.session.Configuration;
2424

2525
import java.util.Properties;
@@ -33,7 +33,7 @@
3333
* @see InterceptingExecutorInterceptor
3434
* @since 1.0.0
3535
*/
36-
public class InterceptingExecutorInterceptorTest extends AbstractMyBatisTest {
36+
public class InterceptingExecutorInterceptorTest extends DefaultMapperTest {
3737

3838
public static final String TEST_PROPERTY_KEY = "test.class";
3939

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.microsphere.mybatis.plugin;
18+
19+
import org.apache.ibatis.plugin.Interceptor;
20+
import org.apache.ibatis.plugin.Invocation;
21+
22+
/**
23+
* No-Operation {@link Interceptor}
24+
*
25+
* @author <a href="mailto:mercyblitz@gmail.com">Mercy<a/>
26+
* @see Interceptor
27+
* @since 1.0.0
28+
*/
29+
public class NoOpInterceptor implements Interceptor {
30+
31+
@Override
32+
public Object intercept(Invocation invocation) throws Throwable {
33+
return null;
34+
}
35+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.microsphere.mybatis.plugin;
18+
19+
import io.microsphere.mybatis.test.executor.LoggingExecutor;
20+
import org.apache.ibatis.executor.Executor;
21+
import org.apache.ibatis.executor.parameter.ParameterHandler;
22+
import org.apache.ibatis.executor.resultset.ResultSetHandler;
23+
import org.apache.ibatis.executor.statement.StatementHandler;
24+
import org.apache.ibatis.plugin.Interceptor;
25+
import org.apache.ibatis.plugin.Intercepts;
26+
import org.apache.ibatis.plugin.Invocation;
27+
import org.apache.ibatis.plugin.Plugin;
28+
import org.apache.ibatis.plugin.PluginException;
29+
import org.apache.ibatis.plugin.Signature;
30+
import org.junit.jupiter.api.Test;
31+
32+
import java.lang.reflect.Method;
33+
import java.sql.SQLException;
34+
import java.util.Map;
35+
import java.util.Set;
36+
37+
import static io.microsphere.mybatis.plugin.Plugins.TARGET_CLASSES;
38+
import static io.microsphere.mybatis.plugin.Plugins.TARGET_CLASSES_SIZE;
39+
import static io.microsphere.mybatis.plugin.Plugins.getPlugin;
40+
import static io.microsphere.mybatis.plugin.Plugins.getSignatureMap;
41+
import static org.apache.ibatis.plugin.Plugin.wrap;
42+
import static org.junit.jupiter.api.Assertions.assertEquals;
43+
import static org.junit.jupiter.api.Assertions.assertFalse;
44+
import static org.junit.jupiter.api.Assertions.assertNotNull;
45+
import static org.junit.jupiter.api.Assertions.assertNull;
46+
import static org.junit.jupiter.api.Assertions.assertSame;
47+
import static org.junit.jupiter.api.Assertions.assertThrows;
48+
import static org.junit.jupiter.api.Assertions.assertTrue;
49+
50+
/**
51+
* {@link Plugins} Test
52+
*
53+
* @author <a href="mailto:mercyblitz@gmail.com">Mercy<a/>
54+
* @see Plugins
55+
* @since 1.0.0
56+
*/
57+
public class PluginsTest {
58+
59+
@Test
60+
public void testTargetClasses() {
61+
assertEquals(4, TARGET_CLASSES.size());
62+
assertEquals(TARGET_CLASSES_SIZE, TARGET_CLASSES.size());
63+
assertTrue(TARGET_CLASSES.contains(Executor.class));
64+
assertTrue(TARGET_CLASSES.contains(ParameterHandler.class));
65+
assertTrue(TARGET_CLASSES.contains(ResultSetHandler.class));
66+
assertTrue(TARGET_CLASSES.contains(StatementHandler.class));
67+
}
68+
69+
@Test
70+
public void testGetSignatureMap() {
71+
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(new TestAnnotatedExecutorInterceptor());
72+
assertEquals(1, signatureMap.size());
73+
assertTrue(signatureMap.containsKey(Executor.class));
74+
assertFalse(signatureMap.containsKey(ParameterHandler.class));
75+
assertFalse(signatureMap.containsKey(ResultSetHandler.class));
76+
assertFalse(signatureMap.containsKey(StatementHandler.class));
77+
Set<Method> methods = signatureMap.get(Executor.class);
78+
assertEquals(3, methods.size());
79+
}
80+
81+
@Test
82+
public void testGetSignatureMapOnFailed() {
83+
assertThrows(PluginException.class, () -> getSignatureMap(new NoOpInterceptor()));
84+
assertThrows(PluginException.class, () -> getSignatureMap(new WrongTargetSignatureInterceptor()));
85+
assertThrows(PluginException.class, () -> getSignatureMap(new WrongMethodSignatureInterceptor()));
86+
}
87+
88+
89+
@Test
90+
public void testGetPlugin() throws SQLException {
91+
Executor executor = newExecutor();
92+
Object proxy = wrap(executor, new TestAnnotatedExecutorInterceptor());
93+
assertTrue(proxy instanceof Executor);
94+
95+
Plugin plugin = getPlugin(proxy);
96+
assertNotNull(plugin);
97+
98+
Executor proxyExecutor = (Executor) proxy;
99+
assertEquals(0, proxyExecutor.update(null, null));
100+
101+
// No Proxy
102+
proxy = wrap(executor, new SignatureInterceptor());
103+
assertSame(executor, proxy);
104+
plugin = getPlugin(proxy);
105+
assertNull(plugin);
106+
}
107+
108+
private Executor newExecutor() {
109+
return new LoggingExecutor();
110+
}
111+
112+
@Intercepts(
113+
value = {}
114+
)
115+
static class SignatureInterceptor implements Interceptor {
116+
@Override
117+
public Object intercept(Invocation invocation) throws Throwable {
118+
return invocation.proceed();
119+
}
120+
}
121+
122+
123+
@Intercepts(
124+
@Signature(
125+
type = Object.class,
126+
method = "equals",
127+
args = {Object.class}
128+
)
129+
)
130+
static class WrongTargetSignatureInterceptor implements Interceptor {
131+
@Override
132+
public Object intercept(Invocation invocation) throws Throwable {
133+
return invocation.proceed();
134+
}
135+
}
136+
137+
@Intercepts(
138+
@Signature(
139+
type = Executor.class,
140+
method = "methodNotFound",
141+
args = {}
142+
)
143+
)
144+
static class WrongMethodSignatureInterceptor implements Interceptor {
145+
@Override
146+
public Object intercept(Invocation invocation) throws Throwable {
147+
return invocation.proceed();
148+
}
149+
}
150+
151+
}

0 commit comments

Comments
 (0)