A
A
Arthur2015-01-20 12:26:21
Java
Arthur, 2015-01-20 12:26:21

Can you advise Best-practice on the implementation of end-to-end logic for detecting changes in an object?

In the book "Spring 3 for Professionals" I saw an example of end-to-end logic "Object modification detection using injections". Slightly changed the code indicated there:
interface

public interface IsModified {
    public boolean isModified();
}

interface implementation
public class IsModifiedMixin extends DelegatingIntroductionInterceptor implements IsModified {

    private boolean isModified = false;
    private Map<Method, Method> methodCache = new HashMap<Method, Method>();

    public boolean isModified() {
        return isModified;
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (!isModified) {
            if ((invocation.getMethod().getName().startsWith("set"))
                    && (invocation.getArguments().length == 1)) {
                // Вызвать соответствующий метод get, чтобы посмотреть,
                // изменилось ли значение.
                Method getter = getGetter(invocation.getMethod());
                if (getter != null) {
                    // Для методов, предназначенных только для записи,
                    // проверка модификации не важна.
                    Object newVal = invocation.getArguments()[0];
                    Object oldVal = getter.invoke(invocation.getThis(), null);
                    if ((newVal == null) && (oldVal == null)) {
                        isModified = false;
                    } else if ((newVal == null) && (oldVal != null)) {
                        isModified = true;
                    } else if ((newVal != null) && (oldVal == null)) {
                        isModified = true;
                    } else {
                        isModified = (!newVal.equals(oldVal));
                    }
                }
            }
        }
        return super.invoke(invocation);
    }

    private Method getGetter(Method setter) {
        Method getter = null;
        // Попытка извлечения из кеша.
        getter = (Method) methodCache.get(setter);
        if (getter != null) {
            return getter;
        }
        String getterName = setter.getName().replaceFirst("set", "get");
        try {
            getter = setter.getDeclaringClass().getMethod(getterName, null);
            // Метод извлечения из кеша.
            synchronized (methodCache) {
                methodCache.put(setter, getter);
            }
            return getter;
        } catch (NoSuchMethodException ex) {
            // Должен быть только для записи.
            return null;
        }
    }
}

adviser
public class IsModifiedAdvisor extends DefaultPointcutAdvisor {
    public IsModifiedAdvisor() {
        super(new IsModifiedMixin());
    }
}

Adding an impurity using slice
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="modifiedAdvisor"
          class="ru.its360.system.modifiedmixin.advisor.IsModifiedAdvisor">
        <property name="pointcut">
            <bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
                <property name="expression" value="execution(* ru.its360.development360..domain.model..*(..))"/>
            </bean>
        </property>
    </bean>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</beans>

In theory, the following should be done: IsModified logic is added to each model and a proxy is created.
Unfortunately the code doesn't work. When starting a project on Tomcat, an exception is issued:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'creditProgramManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.document.ticket.service.api.ticket.category.ITicketCategoryManager ru.its360.development.development360.credits.creditprogram.service.application.CreditProgramManager.ticketCategoryManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ticketCategoryManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.leasing360.workflow.IWorkFlowManager ru.its360.security.accesslayer.service.application.SecurityAbstractManager.flowManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'workFlowManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.development.development360.tenders.tender.service.api.ITenderManager ru.its360.development360.workflow.workflow.service.application.WorkFlowManager.tenderManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenderManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.development.development360.tenders.developmentcontract.service.api.IDevelopmentContractManager ru.its360.development.development360.tenders.tender.service.application.TenderManager.developmentContractManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'developmentContractManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.development.development360.tenders.paymentprofile.service.api.IPaymentProfileManager ru.its360.development.development360.tenders.developmentcontract.service.application.DevelopmentContractManager.paymentProfileManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentProfileManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.development.development360.tenders.paymentprofile.service.calc.api.IPaymentProfileCalculatorFactory ru.its360.development.development360.tenders.paymentprofile.service.application.PaymentProfileManager.paymentProfileCalculatorFactory; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentProfileCalculatorFactory': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ru.its360.development.development360.tenders.paymentprofile.service.calc.api.IDDUFullOfShareWithoutCreditCalculator ru.its360.development.development360.tenders.paymentprofile.service.calc.application.PaymentProfileCalculatorFactory.dduFullOfShareWithoutCreditCalculator; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DDUFullOfShareWithoutCreditCalculator': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: protected ru.its360.development.development360.buildings.section.service.api.IRealtyObjectManager ru.its360.development.development360.tenders.paymentprofile.service.calc.application.AbstractPaymentProfileCalculator.realtyObjectManager; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'realtyObjectManager': Bean with name 'realtyObjectManager' has been injected into other beans [statusRealtyObjectManager] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

I think this is due to the fact that the DefaultAdvisorAutoProxyCreator creates a proxy for beans of some abstract type (I haven’t figured it out yet) and the rest of the spring beans cannot work with them.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Arthur, 2015-05-29
@Headshrinker

I solved the problem in this way: I do mixing to the object immediately before the place where the object can probably change and after that I check. Works for JSF when working through a form.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question