В книге "Spring 3 для профессионалов" увидел пример сквозной логики "Обнаружение модификации объекта с помощью введений". Немного изменил указанный там код:
интерфейс
public interface IsModified {
public boolean isModified();
}
реализация интерфейса
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;
}
}
}
советчик
public class IsModifiedAdvisor extends DefaultPointcutAdvisor {
public IsModifiedAdvisor() {
super(new IsModifiedMixin());
}
}
Добавление примеси с использованием среза
<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>
По идее должно выполняться следующее: к каждой модели примешивается логика IsModified и создается прокси.
Код к сожалению не работает. При запуске проекта на томкате выдается эксепшн:
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.
Я думаю это связано с тем что DefaultAdvisorAutoProxyCreator создает прокси для бинов какого то абстрактного типа (пока не разобрался) и остальные бины спринга не могут работать с ними.