Spring @Controller + BeanPostProcessor with proxy

181
19 января 2022, 13:30

Похожие вопросы, но ответов на них так и не последовало, возможно сейчас сюда забредет гуру спринга и разложит все по полочкам: тыц и тыц

Суть проблемы: пишу свой BeanPostProcessor (BPP) для "логгирования" классов, которые помечены моей аннотацией. Соответственно, если класс помечен данной аннотацией, то пускаем его через прокси и пишем логику необходимую. Однако, если класс был помечен другими аннотациями (например @Controller), то Proxy это все дело игнорирует и на выходе мы получаем логгирующийся класс, но без всех остальных наворотов.

Вопрос: Каким способом можно реализовать BPP и проксировать в нем классы так, чтобы другая логика, связанная с аннотациями не терялась? Или же можно обойтись как нибудь без прокси (но с BPP). (Аспекты не предлагать, вопрос именно в BPP).

UPD: Как один из вариантов решения данной проблемы - объявлять бины не с помощью аннотаций. Но это ограничения, которые нежелательны. Так же будут "заигнорены" другие аннотации, кроме "пользовательской, по которой создается прокси.

Answer 1

Надеюсь мой ответ не слишком поздно для тебя

У меня есть подозрение, что ты не правильно создаешь прокси

У меня была похожая задача, мне было необходимо логировать реквест и респонс контроллеров, если на методе была повешена моя аннотация @Logging

Реализация моей задачи выглядит следующим образом. Аннотация с двумя параметрами, для гибкой настройки логирования реквеста и респонса (мне не всегда необходимо логировать респонс):

@Inherited
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
    boolean isRequest() default true;
    boolean isResponse() default true;
}

БинПостПроцессор выглядит следующим образом. Для создания динамическим прокси на лету, я использовал CGLIB, т.к. она работает быстрее стандартных инструментов JDK:

@Slf4j
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
    private Map<String, Class> loggingBeans = new HashMap<>();
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        if (Arrays.stream(beanClass.getMethods()).anyMatch(method -> method.isAnnotationPresent(Logging.class))) {
            loggingBeans.put(beanName, beanClass);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class originalBean = loggingBeans.get(beanName);
        if (originalBean != null) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(originalBean);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                Optional<Method> originalMethod = Arrays.stream(originalBean.getMethods())
                        .filter(method::equals)
                        .findFirst();
                if (originalMethod.isPresent()) {
                    Logging annotation = originalMethod.get().getAnnotation(Logging.class);
                    if (annotation != null) {
                        if (annotation.isRequest()) {
                            log.info("{}#{}: {}", originalBean.getSimpleName(), method.getName(), Arrays.asList(args).toString());
                        }
                        Object invoke = proxy.invoke(bean, args);
                        if (annotation.isRequest()) {
                            log.info("{}#{}: {}", originalBean.getSimpleName(), method.getName(), invoke != null ? invoke.toString() : "void");
                        }
                        return invoke;
                    }
                }
                return method.invoke(bean, args);
            });
            return enhancer.create();
        }
        return bean;
    }
}

Кусок кода с использованием аннотации из контроллера:

@GetMapping("/type")
@Logging(isResponse = false)
public Object getTypes() {
    //мясо метода
}
READ ALSO
Проблема с парсингом сайта с помощью Jsoup

Проблема с парсингом сайта с помощью Jsoup

Несколько дней назад сайт перевели на https, и сайт перестал парситьсяДругие https сайты парсятся без проблем

95
failed to load applicationcontext в тестах DAO слоя

failed to load applicationcontext в тестах DAO слоя

Суть проблемы: пишу тесты для дао слоя и не при запуске вылазит ошибка failed to load applicationcontextПомогите, кто знает в чем проблема

104
Перестали отрываться jar-файлы ubuntu

Перестали отрываться jar-файлы ubuntu

Раньше по двойному клику открывалось gui приложение на OpenJDK Java 8 Policy ToolНе знаю что случилось, но стало запускаться только из консоли

70
Как сделать боковые линии от текста в css?

Как сделать боковые линии от текста в css?

Как лучше сделать вот такие линии

237