Java | JavaBean监控

时间:2013-12-24 09:39:22

标签: java

在处理某些事情时,我需要检查JavaBean(在我的情况下是一个表单bean)中的值是否已经改变。

通常在网络表单提交中,我们在更新/编辑流程中遇到类似的情况。我们一般 在更新操作之前存储表单bean是会话,并比较新提交的表单bean中的值。 这很简单但会使代码变得混乱,并且在添加/修改属性时需要更改代码。

问题陈述: -

  1. 我有一个包含属性,基本类型,数组和其他类类型的类....

  2. 我有这个类的对象。此对象通过一个可能会更改此对象属性的方法。

  3. 我如何透明地了解: -

  4. 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是个好主意吗?可行性?

2 个答案:

答案 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。