diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..45d582a7c4341d8e469e1e881059579ff78b3f6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Build and Release Folders +bin-debug/ +bin-release/ +[Oo]bj/ +[Bb]in/ + +# Other files and folders +.settings/ + +# Executables +*.swf +*.air +*.ipa +*.apk +.idea/* +target/* +/src/main/java/org/needcoke/aop/test/* +# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` +# should NOT be excluded as they contain compiler settings and other important +# information for Eclipse / Flash Builder. diff --git a/README.md b/README.md index a0f8ee77fabc5203a0b8ac4f8609731c156dafa3..e33e92c7ec11c4921206e116c72a31f4d8c92481 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ #### 介绍 needcoke-aop是基于needcoke-ioc基础上实现AOP功能的一个轻量级框架 +基于needcoke-ioc VER-0.2版本 + #### 软件架构 软件架构说明 diff --git a/lib/needcoke-ioc.jar b/lib/needcoke-ioc.jar new file mode 100644 index 0000000000000000000000000000000000000000..65810419395d907e622382024e65b1d3f3871b58 Binary files /dev/null and b/lib/needcoke-ioc.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c85e45868b970161937a2cdaf46bc75fa243a03 --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + org.needcoke + needcoke-aop + VER-0.0.3 + 基于needcokeVER-0.3 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 6 + 6 + + + + + + + + + cglib + cglib + 3.3.0 + + + + \ No newline at end of file diff --git a/src/main/java/org/needcoke/aop/annotation/Aspect.java b/src/main/java/org/needcoke/aop/annotation/Aspect.java new file mode 100644 index 0000000000000000000000000000000000000000..dc3b25147473f1197bd6423a70669b760b62aa74 --- /dev/null +++ b/src/main/java/org/needcoke/aop/annotation/Aspect.java @@ -0,0 +1,39 @@ +package org.needcoke.aop.annotation; + +import org.needcoke.ioc.annotation.Note; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Note("一旦被该注解标记则说明需要被AOP切的类") +public @interface Aspect { + + @Note("切什么主体") + CutObject target() default CutObject.DEFAULT; + + @Note("被切主体的值") + String value() default ""; + + @Note("CutObject中code为3,4,5都需要入的参数") + String methodName() default ""; + + /* 类的全路径.methodName */ + @Note("在执行前") + String before() default ""; + + /* 类的全路径.methodName */ + @Note("不管是否正常执行完毕都会调用") + String afterReturning() default ""; + + /* 类的全路径.methodName */ + @Note("遭遇异常执行") + String afterException() default ""; + + /* 类的全路径.methodName */ + @Note("正常执行结束调用") + String afterOK() default ""; + + String around() default ""; +} diff --git a/src/main/java/org/needcoke/aop/annotation/CutObject.java b/src/main/java/org/needcoke/aop/annotation/CutObject.java new file mode 100644 index 0000000000000000000000000000000000000000..5d7f411ec7fd4f97e49e6265a0a92c1a73343a15 --- /dev/null +++ b/src/main/java/org/needcoke/aop/annotation/CutObject.java @@ -0,0 +1,27 @@ +package org.needcoke.aop.annotation; + +public enum CutObject { + /* 仅切一个方法 */ + METHOD(1), + + /* 仅切类的所有非静态方法 */ + CLASS_METHOD_NOT_STATIC(2), + + /* 实现了某个接口的类 */ + CLASS_IMPLEMENT(3), + + /* 继承了某个类的类 */ + CLASS_EXTENDS(4), + + /* 包路径下的所有类 */ + PACKAGE(5), + + /* 切个屁 */ + DEFAULT(0); + + private final int code; + + CutObject(int code){ + this.code = code; + } +} diff --git a/src/main/java/org/needcoke/aop/config/AOPConfig.java b/src/main/java/org/needcoke/aop/config/AOPConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..dd56f0ac044c81f3398c396160e66fb312c16535 --- /dev/null +++ b/src/main/java/org/needcoke/aop/config/AOPConfig.java @@ -0,0 +1,6 @@ +package org.needcoke.aop.config; + +public class AOPConfig { + + public final static String cutAllMethodNotStaticName = "###切所有非静态方法###"; +} diff --git a/src/main/java/org/needcoke/aop/handle/AOPHandle.java b/src/main/java/org/needcoke/aop/handle/AOPHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..f890932e6f105a51d757f5c2260fa5dfa59e95e0 --- /dev/null +++ b/src/main/java/org/needcoke/aop/handle/AOPHandle.java @@ -0,0 +1,209 @@ +package org.needcoke.aop.handle; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import net.sf.cglib.proxy.Enhancer; +import org.needcoke.aop.annotation.Aspect; +import org.needcoke.aop.annotation.CutObject; +import org.needcoke.aop.config.AOPConfig; +import org.needcoke.aop.interceptor.NeedCokeAOPInterceptor; +import org.needcoke.aop.interceptor.TargetMethod; +import org.needcoke.ioc.core.ContextBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.handle.Handle; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; + +public class AOPHandle implements Handle { + private ContextHolder holder; + + @Override + public int handle(ContextHolder holder, Object... params) { + this.holder = holder; + for (Class aClass : holder.componentMap.keySet()) { + Aspect annotationAspect = aClass.getAnnotation(Aspect.class); + if (null != annotationAspect) { + aspect(annotationAspect, holder.componentMap.get(aClass), aClass); + } + } + + + /* 使用AOP对象替换 */ + return 0; + } + + private void aspect(Aspect annotationAspect, Object component, Class clz) { + /* 切什么 */ + CutObject target = annotationAspect.target(); + String value = annotationAspect.value(); + String methodName = annotationAspect.methodName(); + TargetMethod around = fullLocationToTargetMethod(clz, annotationAspect.around()); + TargetMethod before = fullLocationToTargetMethod(clz, annotationAspect.before()); + TargetMethod afterOK = fullLocationToTargetMethod(clz, annotationAspect.afterOK()); + TargetMethod afterException = fullLocationToTargetMethod(clz, annotationAspect.afterException()); + TargetMethod afterFinally = fullLocationToTargetMethod(clz, annotationAspect.afterReturning()); + + if (target == CutObject.METHOD) { + TargetMethod targetMethod = fullLocationToTargetMethod(clz, value); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(targetMethod.getComponent().getClass()); + enhancer.setCallback(new NeedCokeAOPInterceptor(targetMethod.getMethod().getName(), + before, around, afterOK, afterException, afterFinally)); + Object o = enhancer.create(); + + ContextBean bean = new ContextBean() + .setProxy(true) + .setValue(o) + .setClz(targetMethod.getComponent().getClass()) + .setName(targetMethod.getComponent().getClass().getName() + "#needcoke代理对象"); + holder.proxyComponentMap.put(targetMethod.getComponent().getClass(), bean); + } + else if (target == CutObject.CLASS_METHOD_NOT_STATIC) { + TargetMethod targetMethod = fullLocationToTargetClass(clz, value); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(targetMethod.getComponent().getClass()); + + enhancer.setCallback(new NeedCokeAOPInterceptor(AOPConfig.cutAllMethodNotStaticName, + before, around, afterOK, afterException, afterFinally)); + Object o = enhancer.create(); + + ContextBean bean = new ContextBean() + .setProxy(true) + .setValue(o) + .setClz(targetMethod.getComponent().getClass()) + .setName(targetMethod.getComponent().getClass().getName() + "#needcoke代理对象"); + holder.proxyComponentMap.put(targetMethod.getComponent().getClass(), bean); + } + else if(target == CutObject.CLASS_IMPLEMENT || target == CutObject.CLASS_EXTENDS){ + try { + Class interfaceClass = Class.forName(value); + Collection values = holder.idBeanMap.values(); + for (ContextBean contextBean : values) { + if(interfaceClass.isInstance(contextBean.value)){ + TargetMethod targetMethod = fullLocationToTargetClass(contextBean.clz, contextBean.getClz().getName()); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(targetMethod.getComponent().getClass()); + if(StrUtil.isBlank(methodName)){ + methodName = AOPConfig.cutAllMethodNotStaticName; + } + enhancer.setCallback(new NeedCokeAOPInterceptor(methodName, + before, around, afterOK, afterException, afterFinally)); + Object o = enhancer.create(); + + ContextBean bean = new ContextBean() + .setProxy(true) + .setValue(o) + .setClz(targetMethod.getComponent().getClass()) + .setName(targetMethod.getComponent().getClass().getName() + "#needcoke代理对象"); + holder.proxyComponentMap.put(targetMethod.getComponent().getClass(), bean); + } + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + }else if(target == CutObject.PACKAGE){ + Collection values = holder.idBeanMap.values(); + for (ContextBean contextBean : values) { + if(contextBean.clz.getName().startsWith(value.replace("*",""))){ + TargetMethod targetMethod = fullLocationToTargetClass(contextBean.clz, contextBean.getClz().getName()); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(targetMethod.getComponent().getClass()); + if(StrUtil.isBlank(methodName)){ + methodName = AOPConfig.cutAllMethodNotStaticName; + } + enhancer.setCallback(new NeedCokeAOPInterceptor(methodName, + before, around, afterOK, afterException, afterFinally)); + Object o = enhancer.create(); + + ContextBean bean = new ContextBean() + .setProxy(true) + .setValue(o) + .setClz(targetMethod.getComponent().getClass()) + .setName(targetMethod.getComponent().getClass().getName() + "#needcoke代理对象"); + holder.proxyComponentMap.put(targetMethod.getComponent().getClass(), bean); + } + } + } + } + + + /** + * @param clz Aspect注解标注的类 + * @param fullLocation 方法的全路径 org.needcoke.aop.handle.AOPHandle#fullLocationToTargetMethod + * @return {@link TargetMethod} + * @author Gilgamesh + * @date 2021/9/16 9:16 + **/ + private TargetMethod fullLocationToTargetMethod(Class clz, String fullLocation) { + if (StrUtil.isBlank(fullLocation)) { + return new TargetMethod(); + } + String classFullName = ""; + String methodName = ""; + if (fullLocation.contains(".")) { + StringBuilder clzFullName = new StringBuilder(); + String[] split = fullLocation.split("\\."); + for (int i = 0; i < split.length - 1; i++) { + clzFullName.append(split[i] + "."); + } + classFullName = clzFullName.substring(0, clzFullName.length() - 1); + methodName = split[split.length - 1]; + } else { + /* fullLocation不是全路径只有方法名,则代表methodName是当前注解标注类中的方法 */ + methodName = fullLocation; + classFullName = clz.getName(); + } + TargetMethod targetMethod = new TargetMethod(); + List contextBeans = holder.fullNameBeanContext.get(classFullName); + ContextBean contextBean = null; + if (CollUtil.isNotEmpty(contextBeans)) { + contextBean = contextBeans.get(0); + } else { + try { + Class aClass = Class.forName(classFullName); + Object aspectO = aClass.newInstance(); + contextBean = new ContextBean(); + contextBean.setClz(aClass); + contextBean.setValue(aspectO); + contextBean.setName(aClass.getName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + targetMethod.setComponent(contextBean.value); + Class aspectClz = contextBean.clz; + Method method = null; + try { + method = aspectClz.getMethod(methodName); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + targetMethod.setMethod(method); + return targetMethod; + } + + private TargetMethod fullLocationToTargetClass(Class clz, String fullLocation) { + TargetMethod targetMethod = new TargetMethod(); + if (StrUtil.isBlank(fullLocation)) { + List bean = holder.getBean(clz); + if (CollUtil.isNotEmpty(bean)) { + targetMethod.setComponent(bean.get(0)); + } + } else { + List contextBeans = holder.fullNameBeanContext.get(fullLocation); + if (CollUtil.isNotEmpty(contextBeans)) { + ContextBean contextBean = contextBeans.get(0); + targetMethod.setComponent(contextBean.value); + } + } + if(targetMethod.getComponent() == null){ + throw new RuntimeException("AOP value 的值错误,应该是类的全路径!"); + } + Method[] methods = clz.getMethods(); + if (methods.length > 0) { + targetMethod.setMethod(clz.getMethods()[0]); + } + return targetMethod; + } +} diff --git a/src/main/java/org/needcoke/aop/interceptor/NeedCokeAOPInterceptor.java b/src/main/java/org/needcoke/aop/interceptor/NeedCokeAOPInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..b14aa9a5827cb9ac936e1a5ea342d4bd65fdb37f --- /dev/null +++ b/src/main/java/org/needcoke/aop/interceptor/NeedCokeAOPInterceptor.java @@ -0,0 +1,52 @@ +package org.needcoke.aop.interceptor; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import org.needcoke.aop.config.AOPConfig; + +import java.lang.reflect.Method; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class NeedCokeAOPInterceptor implements MethodInterceptor { + + private String methodName = ""; + + private TargetMethod before = new TargetMethod(); + + private TargetMethod around = new TargetMethod(); + + private TargetMethod afterOK = new TargetMethod(); + + private TargetMethod afterException = new TargetMethod(); + + private TargetMethod afterReturning = new TargetMethod(); + + @Override + public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { + if (!method.getName().equals(methodName) && !methodName.equals(AOPConfig.cutAllMethodNotStaticName)) { + return methodProxy.invokeSuper(o, objects); + } + around.invoke(); + before.invoke(); + try { + Object invoke = methodProxy.invokeSuper(o, objects); + around.invoke(); + afterOK.invoke(); + return invoke; + } catch (Throwable e) { + e.printStackTrace(); + afterException.invoke(); + throw new RuntimeException(o.getClass().getName() + ", method= " + method.getName() + " ,AOP失败!"); + } finally { + afterReturning.invoke(); + } + + } +} diff --git a/src/main/java/org/needcoke/aop/interceptor/TargetMethod.java b/src/main/java/org/needcoke/aop/interceptor/TargetMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..e14cd2d8f6610f00649c217bf5a15ee477979b81 --- /dev/null +++ b/src/main/java/org/needcoke/aop/interceptor/TargetMethod.java @@ -0,0 +1,20 @@ +package org.needcoke.aop.interceptor; + +import lombok.Data; +import java.lang.reflect.Method; + +@Data +public class TargetMethod { + + private Object component; + + private Method method; + + private Object[] ags; + + public void invoke() throws Throwable { + if (null != component && null != method) { + method.invoke(component, ags); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..3ec4444e133098b9b77e2a14f9fb0717db1cbcf9 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ + + +abc: dffwef \ No newline at end of file