資料監控
*
* 要求寫一段監控代碼,一旦foo的成員變數值發生變化(呼叫setX方法),
* 控制臺輸出變化的成員變數和變化前后的內容,如:
* foo.setA(1);//輸出:a:0->1
* foo.setB(2);//輸出:b:1->2
* foo.setC(3);//輸出:c:0->3
@Getter
@Setter
public class Foo {
private int a;
private int b;
private int c;
private int d;
private int e;
private int f;
}
uj5u.com熱心網友回復:
方法一參考觀察者模式,在foo類追加一個回呼的listener,每個set方法都呼叫listener列印
方法二
aop思想,利用springboot的aop注解,攔截foo的set方法,在set方法前列印
uj5u.com熱心網友回復:
解決方案一:package prop.change.v1;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;
public class Foo {
private final PropertyChangeSupport _pcs = new PropertyChangeSupport(this);
private int a;
private int b;
private int c;
private int d;
private int e;
private int f;
public void addPropertyChangeListener(PropertyChangeListener listener) {
_pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener){
_pcs.addPropertyChangeListener(listener);
}
public void setA(int a) {
int o = this.a;
this.a = a;
_pcs.firePropertyChange("a", o, a);
}
public void setB(int b) {
int o = this.b;
this.b = b;
_pcs.firePropertyChange("b", o, b);
}
public void setC(int c) {
int o = this.c;
this.c = c;
_pcs.firePropertyChange("c", o, c);
}
public void setD(int d) {
int o = this.d;
this.d = d;
_pcs.firePropertyChange("d", o, d);
}
public void setE(int e) {
int o = this.e;
this.e = e;
_pcs.firePropertyChange("e", o, e);
}
public void setF(int f) {
int o = this.f;
this.f = f;
_pcs.firePropertyChange("f", o, f);
}
public PropertyChangeSupport get_pcs() {
return _pcs;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
public int getC() {
return c;
}
public int getD() {
return d;
}
public int getE() {
return e;
}
public int getF() {
return f;
}
}
package prop.change.v1;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Useage {
public static void main(String[] args) {
Foo foo = new Foo();
foo.setA(0);
foo.setB(1);
foo.setC(0);
foo.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt.getPropertyName()+":"+evt.getOldValue()+"->"+evt.getNewValue());
}});
foo.setA(1);
foo.setB(2);
foo.setC(3);
}
}
a:0->1
b:1->2
c:0->3
uj5u.com熱心網友回復:
多謝大神。。還想請教一下,不改變setter的方法,因為我看題目里對Foo使用了lombok。
uj5u.com熱心網友回復:
aop對物體沒辦法攔截監控,必須對spring容器中的物件才能攔截uj5u.com熱心網友回復:
什么叫“不改變setter的方法”uj5u.com熱心網友回復:
解決方案二:<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
package prop.change.v2;
import java.beans.PropertyChangeListener;
public interface PropertyObserver {
public void addPropertyChangeListener(PropertyChangeListener listener);
public void removePropertyChangeListener(PropertyChangeListener listener);
}
package prop.change.v2;
import java.util.HashMap;
import javassist.*;
public class ProxyFactory {
private static final HashMap<Class<?>, Class<?>> proxyMapping = new HashMap<>();
private static String capitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
@SuppressWarnings("unchecked")
private static <T> Class<T> getClass(Class<T> klass) throws CannotCompileException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
String klassName = klass.getName();
// 1. create sub class
CtClass cc = pool.makeClass(klassName+"$Proxy");
cc.setSuperclass(pool.getCtClass(klassName));
// 2. implements PropertyObserver
Class<?>[] interfaces = klass.getInterfaces();
if(interfaces.length<=0) {
interfaces = new Class<?>[] {PropertyObserver.class};
} else {
Class<?>[] newInters = new Class<?>[interfaces.length+1];
System.arraycopy(interfaces, 0, newInters, 0, interfaces.length);
newInters[interfaces.length] = PropertyObserver.class;
}
CtClass cInterfaces[] = new CtClass[interfaces.length];
for(int i=0; i<interfaces.length; i++) {
cInterfaces[i] = pool.getCtClass(interfaces[i].getName());
}
cc.setInterfaces(cInterfaces);
// 3. add field
cc.addField(CtField.make("private final java.beans.PropertyChangeSupport _pcs = new java.beans.PropertyChangeSupport(this);", cc));
// 4. add method addPropertyChangeListener
StringBuilder body = new StringBuilder();
body.append(" public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { \n");
body.append(" _pcs.addPropertyChangeListener(listener); \n");
body.append(" }");
CtMethod cAdd = CtMethod.make(body.toString(), cc);
cc.addMethod(cAdd);
// 5. add method removePropertyChangeListener
body.setLength(0);
body.append(" public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { \n");
body.append(" _pcs.removePropertyChangeListener(listener); \n");
body.append(" }");
CtMethod cRmv = CtMethod.make(body.toString(), cc);
cc.addMethod(cRmv);
// 6. overwrite setters
CtClass original = pool.get(klassName);
CtField[] fields = original.getDeclaredFields();
for(CtField field : fields) {
String name = field.getName();
if("_pcs".equals(name)) {
continue;
}
String setterName = "set" + capitalize(name);
CtMethod old = original.getDeclaredMethod(setterName);
CtMethod setter = CtNewMethod.copy(old, setterName, cc, null);
boolean isPrimitive = field.getType().isPrimitive();
body.setLength(0);
body.append(" { \n");
body.append(" Object o = ").append(isPrimitive?"($w)":"").append("super.get").append(capitalize(name)).append("();");
body.append(" Object n = ").append(isPrimitive?"($w)":"").append("$1;");
body.append(" super.set").append(capitalize(name)).append("($$);");
body.append(" _pcs.firePropertyChange(\"").append(name).append("\", o, n);");
body.append(" }");
setter.setBody(body.toString());
cc.addMethod(setter);
}
return (Class<T>) cc.toClass();
}
@SuppressWarnings("unchecked")
public<T> T create(Class<T> klass) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException {
Class<?> proxyClass = proxyMapping.get(klass);
if(proxyClass == null) {
proxyClass = getClass(klass);
proxyMapping.put(klass, proxyClass);
}
return (T) proxyClass.newInstance();
}
}
package prop.change.v2;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Foo {
private int a;
private int b;
private int c;
private int d;
private int e;
private int f;
}
package prop.change.v2;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Useage {
public static void main(String[] args) throws Exception {
ProxyFactory factory = new ProxyFactory();
Foo foo = factory.create(Foo.class);
foo.setA(0);
foo.setB(1);
foo.setC(0);
((PropertyObserver)foo).addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt.getPropertyName()+":"+evt.getOldValue()+"->"+evt.getNewValue());
}});
foo.setA(1);
foo.setB(2);
foo.setC(3);
}
}
uj5u.com熱心網友回復:
解決方案三:<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
package prop.change.v3;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy<T> implements MethodInterceptor {
private final T target;
public CglibProxy(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public T create() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
private Object getValue(Method method, Object[] args) {
if(method.getName().startsWith("set") && args.length == 1) {
String getterName = "get" + method.getName().substring(3);
try {
Method getter = method.getDeclaringClass().getMethod(getterName, new Class<?>[0]);
return getter.invoke(target, new Object[0]);
} catch (Exception e) {
}
}
return null;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldValue = getValue(method, args);
Object result = method.invoke(target, args);
if(oldValue != null) {
String propertyName = method.getName().substring(3);
propertyName = propertyName.substring(0,1).toLowerCase() + propertyName.substring(1);
firePropertyChanged(target, propertyName, oldValue, args[0]);
}
return result;
}
private void firePropertyChanged(T target, String propertyName, Object oldValue, Object newVlaue) {
System.out.println(propertyName+":"+oldValue+"->"+newVlaue);
}
}
package prop.change.v3;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Foo {
private int a;
private int b;
private int c;
private int d;
private int e;
private int f;
}
package prop.change.v3;
public class Useage {
public static void main(String[] args) {
Foo foo = new Foo();
foo.setA(0);
foo.setB(1);
foo.setC(0);
foo = new CglibProxy<Foo>(foo).create();
foo.setA(1);
foo.setB(2);
foo.setC(3);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/201627.html
標籤:Java SE
