新增aop相关封装、测试方法

This commit is contained in:
aoshiguchen
2022-06-24 13:37:44 +08:00
parent dc270d35c4
commit 5830325717
16 changed files with 977 additions and 0 deletions
@@ -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;
}
@@ -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"));
}
}