新增aop相关封装、测试方法
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.proxy.Proxy;
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyFactory;
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyStrategy;
|
||||
import fun.asgc.neutrino.core.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class Aop {
|
||||
private static final ProxyStrategy proxyStrategy = ProxyStrategy.SUB_CLASS_PROXY;
|
||||
private static final ProxyFactory proxyFactory = Proxy.getProxyFactory(proxyStrategy);
|
||||
|
||||
public static <T> T get(Class<T> clazz) {
|
||||
Assert.notNull(proxyFactory, "代理工厂初始化异常!");
|
||||
return proxyFactory.get(clazz);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface Intercept {
|
||||
Class<? extends Interceptor>[] value();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public interface Interceptor {
|
||||
/**
|
||||
* 拦截方法
|
||||
* @param inv
|
||||
*/
|
||||
void intercept(Invocation inv);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.proxy.ProxyCache;
|
||||
import fun.asgc.neutrino.core.util.ArrayUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class Invocation {
|
||||
private Class<?> targetClass;
|
||||
private Method targetMethod;
|
||||
private Object proxy;
|
||||
private Supplier callback;
|
||||
private Object[] args;
|
||||
private Interceptor[] interceptors;
|
||||
private volatile int index = 0;
|
||||
private Object returnValue;
|
||||
|
||||
public Invocation(Long methodId, Object proxy, Supplier callback, Object... args) {
|
||||
this.targetMethod = ProxyCache.getMethod(methodId);
|
||||
this.targetClass = this.targetMethod.getDeclaringClass();
|
||||
this.proxy = proxy;
|
||||
this.callback = callback;
|
||||
this.targetClass = proxy.getClass().getSuperclass();
|
||||
this.args = args;
|
||||
this.interceptors = getInterceptors(this.targetMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 先简单实现为 newInstance
|
||||
* @param targetMethod
|
||||
* @return
|
||||
*/
|
||||
public static Interceptor[] getInterceptors(Method targetMethod) {
|
||||
if (null == targetMethod) {
|
||||
return null;
|
||||
}
|
||||
Intercept intercept = targetMethod.getAnnotation(Intercept.class);
|
||||
if (null == intercept) {
|
||||
intercept = targetMethod.getDeclaringClass().getAnnotation(Intercept.class);
|
||||
}
|
||||
if (null == intercept) {
|
||||
return null;
|
||||
}
|
||||
Class<? extends Interceptor>[] classes = intercept.value();
|
||||
if (ArrayUtil.isEmpty(classes)) {
|
||||
return null;
|
||||
}
|
||||
Interceptor[] interceptors = new Interceptor[classes.length];
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
try {
|
||||
interceptors[i] = classes[i].newInstance();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return interceptors;
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
if (ArrayUtil.notEmpty(this.interceptors) && index < this.interceptors.length) {
|
||||
this.interceptors[index++].intercept(this);
|
||||
} else {
|
||||
returnValue = callback.get();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getReturnValue() {
|
||||
return (T)returnValue;
|
||||
}
|
||||
|
||||
|
||||
public Class<?> getTargetClass() {
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
public Method getTargetMethod() {
|
||||
return targetMethod;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class Proxy {
|
||||
private static final ProxyFactory subClassProxyFactory = new SubClassProxyFactory();
|
||||
|
||||
public static ProxyFactory getProxyFactory(ProxyStrategy strategy) {
|
||||
switch (strategy) {
|
||||
case SUB_CLASS_PROXY: return subClassProxyFactory;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 代理缓存
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class ProxyCache {
|
||||
private static final AtomicLong methodId = new AtomicLong();
|
||||
private static final Map<Long, Method> methodCache = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public static Long generateMethodId() {
|
||||
return methodId.incrementAndGet();
|
||||
}
|
||||
|
||||
public static Long setMethod(Method method) {
|
||||
Long id = generateMethodId();
|
||||
methodCache.put(id, method);
|
||||
return id;
|
||||
}
|
||||
|
||||
public static Method getMethod(Long id) {
|
||||
return methodCache.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: 代理类
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
@Data
|
||||
public class ProxyClass {
|
||||
/**
|
||||
* 被代理的目标
|
||||
*/
|
||||
private Class<?> target;
|
||||
/**
|
||||
* 包名
|
||||
*/
|
||||
private String pkg;
|
||||
/**
|
||||
* 类名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 源代码
|
||||
*/
|
||||
private String sourceCode;
|
||||
/**
|
||||
* 字节码
|
||||
*/
|
||||
private Map<String, byte[]> byteCode;
|
||||
/**
|
||||
* 字节码被加载后的代理类
|
||||
*/
|
||||
private Class<?> clazz;
|
||||
|
||||
public ProxyClass(Class<?> target) {
|
||||
this.target = target;
|
||||
this.pkg = target.getPackage().getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 代理类加载器
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class ProxyClassLoader extends ClassLoader {
|
||||
|
||||
protected Map<String, byte[]> byteCodeMap = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public ProxyClassLoader() {
|
||||
super(getParentClassLoader());
|
||||
}
|
||||
|
||||
protected static ClassLoader getParentClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
return ret != null ? ret : ProxyClassLoader.class.getClassLoader();
|
||||
}
|
||||
|
||||
public Class<?> loadProxyClass(ProxyClass proxyClass) {
|
||||
for (Map.Entry<String, byte[]> e : proxyClass.getByteCode().entrySet()) {
|
||||
byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
try {
|
||||
return loadClass(proxyClass.getPkg() + "." + proxyClass.getName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] bytes = byteCodeMap.get(name);
|
||||
if (bytes != null) {
|
||||
Class<?> ret = defineClass(name, bytes, 0, bytes.length);
|
||||
byteCodeMap.remove(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return super.findClass(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class ProxyCompiler {
|
||||
protected volatile List<String> options = null;
|
||||
|
||||
protected List<String> getOptions() {
|
||||
if (options != null) {
|
||||
return options;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (options != null) {
|
||||
return options;
|
||||
}
|
||||
|
||||
List<String> ret = new ArrayList<>();
|
||||
ret.add("-target");
|
||||
ret.add("1.8");
|
||||
|
||||
String cp = getClassPath();
|
||||
if (cp != null && cp.trim().length() != 0) {
|
||||
ret.add("-classpath");
|
||||
ret.add(cp);
|
||||
}
|
||||
|
||||
options = ret;
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 tomcat 丢失 class path,否则无法编译
|
||||
*/
|
||||
protected String getClassPath() {
|
||||
URLClassLoader classLoader = getURLClassLoader();
|
||||
if (classLoader == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
boolean isWindows = isWindows();
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for (URL url : classLoader.getURLs()) {
|
||||
if (index++ > 0) {
|
||||
ret.append(File.pathSeparator);
|
||||
}
|
||||
|
||||
String path = url.getFile();
|
||||
|
||||
// 如果是 windows 系统,去除前缀字符 '/'
|
||||
if (isWindows && path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
// 去除后缀字符 '/'
|
||||
if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
ret.append(path);
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
protected boolean isWindows() {
|
||||
String osName = System.getProperty("os.name", "unknown");
|
||||
return osName.toLowerCase().indexOf("windows") != -1;
|
||||
}
|
||||
|
||||
protected URLClassLoader getURLClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
if (ret == null) {
|
||||
ret = ProxyCompiler.class.getClassLoader();
|
||||
}
|
||||
return (ret instanceof URLClassLoader) ? (URLClassLoader)ret : null;
|
||||
}
|
||||
|
||||
public void compile(ProxyClass proxyClass) {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
if (compiler == null) {
|
||||
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \n" +
|
||||
"Visit https://jfinal.com/doc/4-8 for details \n");
|
||||
}
|
||||
|
||||
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
|
||||
try (MyJavaFileManager javaFileManager = new MyJavaFileManager(compiler.getStandardFileManager(collector, null, null))) {
|
||||
|
||||
MyJavaFileObject javaFileObject = new MyJavaFileObject(proxyClass.getName(), proxyClass.getSourceCode());
|
||||
Boolean result = compiler.getTask(null, javaFileManager, collector, getOptions(), null, Arrays.asList(javaFileObject)).call();
|
||||
outputCompileError(result, collector);
|
||||
|
||||
Map<String, byte[]> ret = new HashMap<>();
|
||||
for (Map.Entry<String, MyJavaFileObject> e : javaFileManager.fileObjects.entrySet()) {
|
||||
ret.put(e.getKey(), e.getValue().getByteCode());
|
||||
}
|
||||
|
||||
proxyClass.setByteCode(ret);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void outputCompileError(Boolean result, DiagnosticCollector<JavaFileObject> collector) {
|
||||
if (! result) {
|
||||
// collector.getDiagnostics().forEach(item -> log.error(item.toString()));
|
||||
collector.getDiagnostics().forEach(item -> System.out.println(item.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyCompiler setCompileOptions(List<String> options) {
|
||||
Objects.requireNonNull(options, "options can not be null");
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProxyCompiler addCompileOption(String option) {
|
||||
Objects.requireNonNull(option, "option can not be null");
|
||||
options.add(option);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class MyJavaFileObject extends SimpleJavaFileObject {
|
||||
|
||||
private String source;
|
||||
private ByteArrayOutputStream outPutStream;
|
||||
|
||||
public MyJavaFileObject(String name, String source) {
|
||||
super(URI.create("String:///" + name + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public MyJavaFileObject(String name, JavaFileObject.Kind kind) {
|
||||
super(URI.create("String:///" + name + kind.extension), kind);
|
||||
source = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
if (source == null) {
|
||||
throw new IllegalStateException("source field can not be null");
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
outPutStream = new ByteArrayOutputStream();
|
||||
return outPutStream;
|
||||
}
|
||||
|
||||
public byte[] getByteCode() {
|
||||
return outPutStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
|
||||
public Map<String, MyJavaFileObject> fileObjects = new HashMap<>();
|
||||
|
||||
public MyJavaFileManager(JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
MyJavaFileObject javaFileObject = new MyJavaFileObject(qualifiedClassName, kind);
|
||||
fileObjects.put(qualifiedClassName, javaFileObject);
|
||||
return javaFileObject;
|
||||
}
|
||||
|
||||
// 是否在编译时依赖另一个类的情况下用到本方法 ?
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException {
|
||||
JavaFileObject javaFileObject = fileObjects.get(className);
|
||||
if (javaFileObject == null) {
|
||||
javaFileObject = super.getJavaFileForInput(location, className, kind);
|
||||
}
|
||||
return javaFileObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public interface ProxyFactory {
|
||||
|
||||
/**
|
||||
* 获取一个类的代理实例
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> T get(Class<T> clazz);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
/**
|
||||
* 代理策略
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProxyStrategy {
|
||||
JDK_DYNAMIC_PROXY(1, "JDK动态代理"),
|
||||
SUB_CLASS_PROXY(2, "子类代理");
|
||||
|
||||
private Integer strategy;
|
||||
private String desc;
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.proxy;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.Invocation;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class SubClassProxyFactory implements ProxyFactory {
|
||||
private static String classNameTemplate = "%sSubClassProxy$$%s";
|
||||
private static AtomicLong proxyClassCounter = new AtomicLong();
|
||||
private ProxyCompiler compiler = new ProxyCompiler();
|
||||
private ProxyClassLoader classLoader = new ProxyClassLoader();
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> clazz) {
|
||||
try {
|
||||
return doGet(clazz);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T doGet(Class<T> clazz) throws ReflectiveOperationException {
|
||||
ProxyClass proxyClass = new ProxyClass(clazz);
|
||||
proxyClass.setName(generateClassName(clazz));
|
||||
String sourceCode = generateProxyClassSourceCode(clazz, proxyClass.getName());
|
||||
proxyClass.setSourceCode(sourceCode);
|
||||
log.debug("类:{} 的代理类源码:\n{}", clazz.getName(), sourceCode);
|
||||
|
||||
compiler.compile(proxyClass);
|
||||
Class<T> retClass = (Class<T>)classLoader.loadProxyClass(proxyClass);
|
||||
T obj = retClass.newInstance();
|
||||
return obj;
|
||||
}
|
||||
|
||||
private String generateClassName(Class<?> clazz) {
|
||||
return String.format(classNameTemplate, clazz.getSimpleName(), proxyClassCounter.incrementAndGet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代理类源代码 - 继承方式
|
||||
* 1、类不能有final修饰符
|
||||
* 2、被代理方法不能有final修饰符
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
private String generateProxyClassSourceCode(Class<?> clazz, String proxyClassName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("package " + clazz.getPackage().getName() + ";").append("\n");
|
||||
sb.append("import ").append(Invocation.class.getName()).append(";\n");
|
||||
sb.append("public class ").append(proxyClassName).append(" extends ").append(clazz.getSimpleName()).append("{\n");
|
||||
Method[] methods = clazz.getMethods();
|
||||
if (methods != null && methods.length > 0) {
|
||||
for (Method method : methods) {
|
||||
if (Modifier.isFinal(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Long methodId = ProxyCache.setMethod(method);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean isVoid = returnType == void.class;
|
||||
String parametersString = buildParametersString(method.getParameters());
|
||||
String parameterNamesString = buildParameterNamesString(method.getParameters());
|
||||
|
||||
sb.append("\t").append("public").append(" ").append(returnType.getName()).append(" ").append(method.getName()).append("(").append(parametersString).append(") {").append("\n")
|
||||
.append("\t\tInvocation inv = new Invocation(").append(methodId + "L,").append("this,").append("() -> {").append("\n")
|
||||
.append("\t\t\t").append(isVoid ? "" : "return ").append("super.").append(method.getName()).append("(").append(parameterNamesString).append(");").append("\n")
|
||||
.append(isVoid ? "\t\t\treturn null;\n" : "")
|
||||
.append("\t\t").append("}").append(StringUtil.isEmpty(parameterNamesString) ? "" : "," + parameterNamesString).append(");").append("\n")
|
||||
.append("\t\t").append("inv.invoke();").append("\n")
|
||||
.append("\t\t").append(isVoid ? "" : "return inv.getReturnValue();").append("\n")
|
||||
.append("\t").append("}").append("\n");
|
||||
}
|
||||
}
|
||||
sb.append("}").append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildParametersString(Parameter[] parameters) {
|
||||
if (null == parameters || parameters.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return Stream.of(parameters).map(item -> item.getType().getName() + " " + item.getName()).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
private String buildParameterNamesString(Parameter[] parameters) {
|
||||
if (null == parameters || parameters.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return Stream.of(parameters).map(Parameter::getName).collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (C) 2018-2022 Zeyi information technology (Shanghai) Co., Ltd.
|
||||
* <p>
|
||||
* All right reserved.
|
||||
* <p>
|
||||
* This software is the confidential and proprietary
|
||||
* information of Zeyi Company of China.
|
||||
* ("Confidential Information"). You shall not disclose
|
||||
* such Confidential Information and shall use it only
|
||||
* in accordance with the terms of the contract agreement
|
||||
* you entered into with Zeyi inc.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: wen.y
|
||||
* @date: 2022/6/23
|
||||
*/
|
||||
@Intercept({DogInterceptor.class})
|
||||
public class Dog {
|
||||
public Dog() {
|
||||
System.out.println("狗出生");
|
||||
}
|
||||
public void call() {
|
||||
System.out.println("汪汪汪");
|
||||
}
|
||||
|
||||
public String say(String msg) {
|
||||
return "狗说:" + msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (C) 2018-2022 Zeyi information technology (Shanghai) Co., Ltd.
|
||||
* <p>
|
||||
* All right reserved.
|
||||
* <p>
|
||||
* This software is the confidential and proprietary
|
||||
* information of Zeyi Company of China.
|
||||
* ("Confidential Information"). You shall not disclose
|
||||
* such Confidential Information and shall use it only
|
||||
* in accordance with the terms of the contract agreement
|
||||
* you entered into with Zeyi inc.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: wen.y
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class DogInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public void intercept(Invocation inv) {
|
||||
try {
|
||||
log.info("拦截器 class:{} method:{} args:{} before", inv.getTargetClass().getName(), inv.getTargetMethod().getName(), inv.getArgs());
|
||||
inv.invoke();
|
||||
log.info("拦截器 class:{} method:{} args:{} after", inv.getTargetClass().getName(), inv.getTargetMethod().getName(), inv.getArgs());
|
||||
} catch (Exception e) {
|
||||
log.info("拦截器 class:{} method:{} args:{} error", inv.getTargetClass().getName(), inv.getTargetMethod().getName(), inv.getArgs());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (C) 2018-2022 Zeyi information technology (Shanghai) Co., Ltd.
|
||||
* <p>
|
||||
* All right reserved.
|
||||
* <p>
|
||||
* This software is the confidential and proprietary
|
||||
* information of Zeyi Company of China.
|
||||
* ("Confidential Information"). You shall not disclose
|
||||
* such Confidential Information and shall use it only
|
||||
* in accordance with the terms of the contract agreement
|
||||
* you entered into with Zeyi inc.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: wen.y
|
||||
* @date: 2022/6/24
|
||||
*/
|
||||
public class Test1 {
|
||||
|
||||
@Test
|
||||
public void dogCall() {
|
||||
Dog dog = Aop.get(Dog.class);
|
||||
dog.call();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dogSay() {
|
||||
Dog dog = Aop.get(Dog.class);
|
||||
System.out.println(dog.say("hello"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user