Нулевые зависимости в прокси-классе - PullRequest
2 голосов
/ 13 марта 2020

Предисловие: Я хочу проксировать некоторые классы и перехватывать их методы (используя cglib и BeanPostProcessor). Эти классы являются бобами Spring (@Service), и обычно они имеют некоторую зависимость, например repository.

Проблема : Когда я создаю прокси для бина, зависимости прокси класс null (они не вводятся в прокси-класс).

что мне делать с зависимостью прокси-класса, введенного правильно?


Сценарий : Я хочу создать прокси для service классов, которые реализуют интерфейс CompensateAware, а затем я хочу напечатать некоторый журнал перед вызовом их методов.

Мои коды следующие:

Класс обслуживания (класс оригинала)

@Service
public class RequestTrackingService implements CompensateAware {

    @Autowired
    public RequestTrackingRepository repository;

    @Transactional
    public void startTracking() {
        RequestTrackingEntity requestTrackingEntity = new RequestTrackingEntity();
        requestTrackingEntity.setStatus(TransactionStatus.RECEIVED);
        requestTrackingEntity.setId(ServiceContext.getTrackingNo());
        repository.save(requestTrackingEntity);
    }

}

Создание классов Proxt с использованием BeanPostProcessor и cglib

@Component
public class ServiceProxy implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof CompensateAware) {
            return beanProxy(bean);
        } else return bean;
    }

    //Create proxy using cglib
    private Object beanProxy(Object bean) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
              System.out.println("SOME LOG BEFORE METHOD");
             return methodProxy.invokeSuper(o, objects);
        });
        return enhancer.create();
    }
}

Вызовите service метод (startTracking)

@Service
public class TestClass {

    @Autowired
    private RequestTrackingService service;

    public void test() {
        //I get NullPointerException because of null dependency (repository) in service 
        service.startTracking();
    }

}

Короче :

Когда я вызываю startTracking метод (метод класса прокси) где-то в моем приложение, я получаю NullPointerException, потому что его зависимость (repository) не вводится, и это ноль`.

1 Ответ

0 голосов
/ 14 марта 2020

Enhancer. create ()

При необходимости создайте новый класс и используйте указанные обратные вызовы (если есть) для создания нового экземпляра объекта. Использует конструктор без аргументов суперкласса.

создает новый экземпляр настроенного SuperClass. Новый экземпляр не является компонентом, управляемым пружиной, и, следовательно, внедрение зависимостей не произошло. Также обратите внимание, что поведение transactional метода startTracking() также будет потеряно с экземпляром Enhancer.

Чтобы обойти NPE, вам нужно будет установить компонент репозитория программным способом.

Я сделал следующее изменение кода, чтобы заставить код работать

@Service
public class RequestTrackingService implements CompensateAware {

    public RequestTrackingRepository repository;

    public RequestTrackingService(RequestTrackingRepository repository) {
        this.repository = repository;
    }

    @Transactional
    public void startTracking() {
        RequestTrackingEntity requestTrackingEntity = new RequestTrackingEntity();
        requestTrackingEntity.setStatus(TransactionStatus.RECEIVED);
        requestTrackingEntity.setId(ServiceContext.getTrackingNo());
        repository.proxyRepoTestMethod();
    }


    public RequestTrackingRepository getRepository() {
        return repository;
    }
}

и

   //Create proxy using cglib
    private Object beanProxy(Object bean) {
        Enhancer enhancer = new Enhancer();
        // Set RequestTrackingService as the super class 
        enhancer.setSuperclass(((Advised)bean).getTargetClass());
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
              System.out.println("SOME LOG BEFORE METHOD");
             return methodProxy.invokeSuper(o, objects);
        });
        RequestTrackingService serviceBean = (RequestTrackingService)enhancer.create(new Class[] {RequestTrackingRepository.class}, new Object[] {((RequestTrackingService)bean).getRepository()});
        return serviceBean;
    }

Надеюсь, это поможет

Обновление 1: Это может быть не полный ответ. Я не могу напечатать строку "SOME LOG BEFORE METHOD" с этим изменением кода.

Обновление 2: Оказывается, что bean.getClass() возвращает класс уже улучшенного объекта (прокси, созданного пружиной). Это каким-то образом предотвращает регистрацию кода в коде делегирования proxyMethod.

пример класса перезапущен:

class com.xxx.xxx.proxy.RequestTrackingService$$EnhancerBySpringCGLIB$$40bcb718

Изменение кода для установки фактического целевого класса устраняет проблему ведения журнала а также.

enhancer.setSuperclass(((Advised)bean).getTargetClass());

Последний вопрос, хотя бы, какая-либо конкретная c причина, по которой вы не используете Spring AOP для этого варианта использования?

...