diff --git a/pom.xml b/pom.xml index f3f01479..8cb41c8e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.1.0 + 2.1.1 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 861c4da6..18e7a0ea 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.1.0 + 2.1.1 4.0.0 diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index 09e4c5ce..a703134a 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.1.0 + 2.1.1 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 62ceda6c..01c2fa90 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.1.0 + 2.1.1 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 6a103129..8a2794b4 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.1.0 + 2.1.1 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldChangeEvent.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldChangeEvent.java new file mode 100644 index 00000000..f5409926 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldChangeEvent.java @@ -0,0 +1,46 @@ +package com.codingapi.springboot.framework.domain.field; + +import com.codingapi.springboot.framework.event.IEvent; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * 实体字段变更事件 + */ +@Setter +@Getter +@ToString(exclude = {"entity"}) +public class FieldChangeEvent implements IEvent { + + /** + * 实体对象 + */ + private Object entity; + + /** + * 实体类名称 + */ + private String simpleName; + + /** + * 时间戳 + */ + private long timestamp; + + /** + * 字段名称 + */ + private String fieldName; + /** + * 旧的值 + */ + private Object oldValue; + /** + * 新的值 + */ + private Object newValue; + + + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldProxyFactory.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldProxyFactory.java new file mode 100644 index 00000000..5a9884b7 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldProxyFactory.java @@ -0,0 +1,19 @@ +package com.codingapi.springboot.framework.domain.field; + +import java.lang.reflect.InvocationTargetException; + +/** + * 实体代理工厂 + */ +public class FieldProxyFactory { + + public static T create(Class entityClass, Object... args) { + FieldValueInterceptor interceptor = null; + try { + interceptor = new FieldValueInterceptor(entityClass, args); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + return (T) interceptor.createProxy(); + } +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldValueInterceptor.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldValueInterceptor.java new file mode 100644 index 00000000..8d9cf58c --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/field/FieldValueInterceptor.java @@ -0,0 +1,126 @@ +package com.codingapi.springboot.framework.domain.field; + +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * 实体代理 + */ +@Slf4j +public class FieldValueInterceptor implements MethodInterceptor { + + // 目标类 + private final Class targetClass; + // 目标类构造函数参数类型 + private final Class[] parameterTypes; + // 目标类构造函数参数 + private final Object[] args; + + // 目标类实例 + private final Object target; + // 目标类属性描述 + private final PropertyDescriptor[] propertyDescriptors; + // 目标类属性值 + private final Map fields; + + public FieldValueInterceptor(Class targetClass, Object... args) throws NoSuchMethodException, + InvocationTargetException, InstantiationException, IllegalAccessException { + this.targetClass = targetClass; + this.args = args; + this.parameterTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + parameterTypes[i] = args[i].getClass(); + } + this.target = targetClass.getConstructor(parameterTypes).newInstance(args); + this.propertyDescriptors = BeanUtils.getPropertyDescriptors(targetClass); + this.fields = new HashMap<>(); + } + + + /** + * 创建代理 + * @return 代理对象 + */ + public Object createProxy() { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(targetClass); + enhancer.setCallback(this); + return enhancer.create(parameterTypes, args); + } + + + /** + * 拦截方法 + * @param obj 代理对象 + * @param method 方法 + * @param args 参数 + * @param proxy 代理 + * @return 方法返回值 + * @throws Throwable 异常 + */ + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // 更新函数肯定有参数,如果没有参数,直接返回 + if (method.getParameterCount() <= 0) { + return method.invoke(target, args); + } + + if(fields.isEmpty()){ + this.readFields(); + } + Object result = method.invoke(target, args); + this.compareAndUpdateField(); + return result; + } + + /** + * 读取Entity字段 + * @throws InvocationTargetException InvocationTargetException + * @throws IllegalAccessException InvocationTargetException + */ + private void readFields() throws InvocationTargetException, IllegalAccessException { + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + String name = propertyDescriptor.getName(); + Object value = propertyDescriptor.getReadMethod().invoke(target); + fields.put(name, value); + } + } + + /** + * 对比字段 + * @throws InvocationTargetException InvocationTargetException + * @throws IllegalAccessException InvocationTargetException + */ + private void compareAndUpdateField() throws InvocationTargetException, IllegalAccessException { + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + String name = propertyDescriptor.getName(); + Object newValue = propertyDescriptor.getReadMethod().invoke(target); + Object oldValue = fields.get(name); + if (!newValue.equals(oldValue)) { + pushEvent(name, oldValue, newValue); + } + fields.put(name, newValue); + } + } + + private void pushEvent(String field, Object oldValue, Object newValue) { + FieldChangeEvent event = new FieldChangeEvent(); + event.setEntity(target); + event.setSimpleName(targetClass.getSimpleName()); + event.setFieldName(field); + event.setOldValue(oldValue); + event.setNewValue(newValue); + event.setTimestamp(System.currentTimeMillis()); + EventPusher.push(event); + } +} diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/Demo.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/Demo.java index 7b69f319..3b50686b 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/Demo.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/Demo.java @@ -10,6 +10,7 @@ public class Demo implements JsonSerializable, MapSerializable { @Getter private long id; + @Getter private String name; @@ -21,7 +22,7 @@ public Demo(String name) { public void changeName(String name) { String beforeName = this.name; this.name = name; - //push event +// push event EventPusher.push(new DemoChangeEvent(beforeName, name)); } diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/FieldProxyFactoryTest.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/FieldProxyFactoryTest.java new file mode 100644 index 00000000..735ea767 --- /dev/null +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/domain/FieldProxyFactoryTest.java @@ -0,0 +1,15 @@ +package com.codingapi.springboot.framework.domain; + +import com.codingapi.springboot.framework.domain.field.FieldProxyFactory; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class FieldProxyFactoryTest { + + @Test + void createEntity() { + Demo demo = FieldProxyFactory.create(Demo.class, "test"); + demo.changeName("123"); + } +} \ No newline at end of file diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java new file mode 100644 index 00000000..d4e7043d --- /dev/null +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java @@ -0,0 +1,14 @@ +package com.codingapi.springboot.framework.handler; + +import com.codingapi.springboot.framework.domain.field.FieldChangeEvent; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Handler +public class EntityFiledChangeHandler implements IHandler{ + + @Override + public void handler(FieldChangeEvent event) { + log.info("field change event -> {}",event); + } +}