在处理某些事情时,我需要检查JavaBean(在我的情况下是一个表单bean)中的值是否已经改变。
通常在网络表单提交中,我们在更新/编辑流程中遇到类似的情况。我们一般 在更新操作之前存储表单bean是会话,并比较新提交的表单bean中的值。 这很简单但会使代码变得混乱,并且在添加/修改属性时需要更改代码。
问题陈述: -
我有一个包含属性,基本类型,数组和其他类类型的类....
我有这个类的对象。此对象通过一个可能会更改此对象属性的方法。
我如何透明地了解: -
a)如果对象属性已更改。
b)改变了哪些属性。
c)将侦听器添加到属性更改事件。
JDK有java.beans.PropertyChangeSupport解决了这个问题。这很好,但我认为可以更好的方式完成。
我想使用注释/反射为我的POJO声明添加“监控”功能。 例如: -
@Monitored
class UserFormBean{
private String name;
private UserAccount account;
}
这个类应该有类似注入的方法: -
public boolean hasChanged();
// null if not changed, old value if changed.
public Object hasChanged(String propName);
问题: -
1)是否存在类似于我建议的现有API?
2)编写这样的API是个好主意吗?可行性?
答案 0 :(得分:0)
1)是否存在类似于我建议的现有API?
不是我知道的。
2)编写这样的API是个好主意吗?可行性?
AOP可以帮助您完成此操作,但是您无法在代码中调用hasChanged()
,因为该方法在运行时才会存在。
你可以有这样的方法
boolean changed = Monitoring.hasChanged(myPojo);
答案 1 :(得分:0)
这就是我试图解决的问题: -
方面: -
/**
* @author Kumar Sambhav Jain
*
*/
public aspect SetterMonitoringAspect {
/**
* All classes annotated with @Monitored will implement the MonitoredBean
* interface.
*/
declare parents : (@com.samsoft.bean.monitor.annotation.Monitored *) implements com.samsoft.bean.monitor.MonitoredBean;
/*
* ******************************************** INTER TYPE DECLARATION START
*/
/**
* Injected PropertyChangeSupport
*
* @return
*/
private Map<String, Object> MonitoredBean.cache = null;
/**
*
* @return true if start monitored was invoked on the bean.
*/
public boolean MonitoredBean.isMonitorinActive() {
return cache != null;
}
/**
* Stop Monitoring the bean.
*/
public void MonitoredBean.stopMonitoring() {
cache = null;
}
/**
* Start monitoring the bean for changes. Also Resets the
* PropertyChangeSupport property.
*
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public void MonitoredBean.startMonitor() {
cache = new HashMap<String, Object>();
}
/**
*
* @return null if monitoring is not enabled. true if the bean
* property/properties changed after startMonitor() was invoked on
* it.
*/
public Boolean MonitoredBean.hasChanged() {
if (cache == null) {
return null;
} else {
return cache.size() > 0;
}
}
/**
* Check if a particular property has changed.
*
* @param propertyName
* Exact case sensitive property name.
* @return null if the property was not changed. Old value if the property
* was changed.
*/
public Object MonitoredBean.hasChanged(String propertyName) {
return cache.get(propertyName);
}
/*
* ********************************************* INTER TYPE DECLARATION END
*/
/**
* Point cut for setter methods of Java bean implementing.
*
* {@link MonitoredBean}
*/
pointcut monitoredBeanInterfaceSetters(
com.samsoft.bean.monitor.MonitoredBean monitoredBean) : target(monitoredBean) && within(@com.samsoft.bean.monitor.annotation.Monitored *) && execution(public void set*(..));
/**
* Before advice on a setter of a monitored bean.
*
* @param monitoredBean
* @param joinPoint
*/
@Before(argNames = "monitoredBean", value = "monitoredBeanInterfaceSetters(monitoredBean)")
public void beforeSetterAdvice(MonitoredBean monitoredBean,
JoinPoint joinPoint) {
if (monitoredBean.isMonitorinActive()) {
try {
String fieldName = joinPoint.getStaticPart().getSignature()
.getName().substring(3).toLowerCase();
Object newValue = joinPoint.getArgs()[0];
Field declaredField = monitoredBean.getClass()
.getDeclaredField(fieldName);
declaredField.setAccessible(true);
Object oldValue = declaredField.get(monitoredBean);
if (oldValue == null && newValue == null) {
return;
} else if ((oldValue == null && newValue != null)
|| (oldValue != null && newValue == null)) {
monitoredBean.cache.put(fieldName, oldValue);
} else if (oldValue != null && newValue != null) {
if (!oldValue.equals(newValue)) {
monitoredBean.cache.put(fieldName, oldValue);
} else {
monitoredBean.cache.remove(fieldName);
}
}
} catch (Exception e) {
monitoredBean.cache = null;
e.printStackTrace();
}
}
}
}
需要监控的示例JavaBean: -
@Monitored
public class UserDetails{
private String name;
private String email;
private short age;
// getter setter ommitted
}
JUnit测试: -
@Test
public void test() {
UserDetails userDetails = new UserDetails();
userDetails.setAge((short) 23);
userDetails.setEmail("kumar.sambhav.jain@gmail.com");
Assert.assertNotNull(userDetails);
Assert.assertNotNull(userDetails.getEmail());
// Start monitoring changes
userDetails.startMonitor();
userDetails.setAge((short) 23);
Assert.assertFalse((userDetails.hasChanged())); // 23 to 23 -> not changed
userDetails.setEmail("kjai10@gmail.com");
Assert.assertTrue(userDetails.hasChanged());
Assert.assertNotNull(userDetails.hasChanged("email"));
Assert.assertTrue(userDetails.hasChanged("email").equals(
"kumar.sambhav.jain@gmail.com"));
SampleBean sb = new SampleBean();
sb.startMonitor();
System.out.println(sb.hasChanged());;
}
可以找到源代码here。
此API的客户端项目必须使用AspectJ启用编译时编织。
对于Maven用户,可以使用插件实现: -
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<encoding>UTF-8</encoding>
<verbose>false</verbose>
<outxml>true</outxml>
<showWeaveInfo>false</showWeaveInfo>
<XaddSerialVersionUID>true</XaddSerialVersionUID>
<source>1.7</source>
<target>1.7</target>
<complianceLevel>1.7</complianceLevel>
<aspectLibraries>
<aspectLibrary>
<groupId>com.samsoft</groupId>
<artifactId>bean-monitor</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
</plugin>
在运行更多测试用例后,我将尝试将API上传到Central Maven Repository。