Как может Spring bean обнаружить, был ли он сам завернут в прокси-сервер AOP?
мы используем пружины TransactionInterceptor
чтобы установить некоторую информацию о разделе Базы данных, используя ThreadLocal
всякий раз, когда метод DAO помечен @Transactional
аннотация выполняется. Нам нужно, чтобы это позволяло направлять наши запросы в разные разделы базы данных.
это отлично работает для большинства методов DAO:
// this causes the invoke method to set a thread-local with the host name of
// the database server the partition is on
@Transactional
public int deleteAll() throws LocalDataException {
проблема в том, когда нам нужно ссылаться на DAO прокси объект внутри DAO. Обычно мы должны иметь абонентский пропуск в прокси-Дао:
public Pager<Foo, Long> getPager(FooDao proxyDao) {
это выглядит следующим образом в коде, который, очевидно, является грубым.
fooDao.getPager(fooDao);
проблема в том, что когда мы находимся внутри FooDao,this
is не прокси-DAO, который нам нужен.
есть ли лучший механизм для Боба, чтобы обнаружить, что вокруг него есть прокси-оболочка? Я посмотрел на Весна AOPUtils но я не вижу способа найти прокси для объекта. Я не хочу!--7--> например. Я также прочитал Spring AOP docs но я не вижу решения там, если я не реализую свой собственный код AOP, который я надеялся избежать.
Я подозреваю, что я мог бы ввести Дао в себя с помощью ApplicationContextAware
утилита bean и A setProxyDao(...)
метод, но это также похоже на хак. Любые другие идеи, как я могу обнаружить прокси, чтобы я мог использовать его изнутри самого компонента? Спасибо за любую помощь.
3 ответов
хакерское решение в соответствии с тем, что вы предложили, учитывая, что AspectJ время компиляции или время загрузки ткачество не будет работать для вас:
создайте интерфейс по следующим линиям:
public interface ProxyAware<T> {
void setProxy(T proxy);
}
пусть ваш Dao реализует реализацию ProxyAware, Теперь создайте BeanPostProcessor с упорядоченным интерфейсом для последнего запуска, по следующим строкам:
public class ProxyInjectingBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (AopUtils.isAopProxy((bean))){
try {
Object target = ((Advised)bean).getTargetSource().getTarget();
if (target instanceof ProxyAware){
((ProxyAware) target).setProxy(bean);
}
} catch (Exception e) {
// ignore
}
}
return bean;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
это уродливо, но работает.
используйте Spring, чтобы ввести ссылку на боб в Боб, даже тот же Боб, как и для любой другой ссылки на Боб. Никаких специальных действий не требуется.
наличие такой переменной явно подтверждает в дизайне класса, что класс ожидает, что будет проксирован каким-то образом. Это не обязательно плохо, так как АОП может изменить поведение, которое нарушает контракт класса.
ссылка на bean обычно будет для интерфейса, и этот интерфейс может быть, даже другой для внутренних методов, на которые ссылаются сами.
сохранить его простым. Это путь безумия. :-)
Что еще более важно, убедитесь, что семантика имеет смысл. Необходимость этого может быть запахом кода, который класс смешивает в нескольких обязанностях, лучше всего разложенных на отдельные бобы.
есть удобная статическая утилита AopContext.currentProxy()
метод, предоставленный Spring, который возвращает прокси-сервер объекту, из которого он был вызван.
хотя использование его считается плохой практикой, семантически тот же метод существует и в Java EE: SessionContext.getBusinessObject()
.
Я написал несколько статей об этом методе утилиты и различные подводные камни: 1, 2, 3.