Как может 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.