Spring MVC и AOP: @Pointcuts применяются только для остальных контроллеров, но не для обычных веб-контроллеров

Я работаю с Spring Framework 4.3.3 в веб-среде.

у меня есть @Controller используется для Web запросы через Web Browser который использует, как зависимость другого @Controller но для Rest цели. Последнее упомянутое использует @Service так далее...

Этот подход к "Web" с использованием "Rest" объясняет, как зависимость описывается в Content Negotiation с использованием Spring MVC для Combining Data and Presentation Formats раздел. Пока здесь для разработки / тестирования и производства работает нормально. Это ценный подход.

Примечание Rest класс аннотируется @Controller потому что я работаю с ResponseEntity<?> а также @ResponseBody,

Проблема с AOP

Про свою инфраструктуру я имею:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}

О @Controllerу меня есть эти два класса:

  • PersonaDeleteOneController с:
    • deleteOne(@PathVariable String id, Model model) за @GetMapping
    • deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes) за @DeleteMapping
  • PersonaRestController
    • deleteOne(@PathVariable String id) за @DeleteMapping

Эти два класса объявлены в одном и том же пакете с именем:

  • com.manuel.jordan.controller.persona

У меня есть следующее @Pointcut:

@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(String, ..)) 
&& args(id) && target(object)")
public void deleteOnePointcut(String id, Object object){}

Тот @Pointcut используется для следующих советов:

@Before(value="ControllerPointcut.deleteOnePointcut(id, object)")
public void beforeAdviceDeleteOne(String id, Object object){
    logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}

Когда я выполню Rest тесты, которые я могу подтвердить через AOP + logging это печатает следующий образец:

  • @Controller (Отдых) -> @Service -> @Repository

Пока здесь все работает так, как ожидается

Когда я выполню Web тесты, которые я могу подтвердить через AOP + logging это печатает следующий образец:

  • @Controller (Отдых) -> @Service -> @Repository

Что мне нужно или ожидать, это следующее:

  • @Controller (Интернет) -> @Controller (Отдых) -> @Service -> @Repository

Что не так или нет? deleteOne подписи не являются двусмысленными относительно их параметров.

То же самое для производства.

Альфа

Здесь контроллеры:

@Controller
@RequestMapping(value="/personas")
public class PersonaDeleteOneController {

    private final PersonaRestController personaRestController;

    @Autowired
    public PersonaDeleteOneController(PersonaRestController personaRestController){
        this.personaRestController = personaRestController;
    }

    @GetMapping(value="/delete/{id}",
                produces=MediaType.TEXT_HTML_VALUE)
    public String deleteOne(@PathVariable String id, Model model){
        model.addAttribute(personaRestController.findOneById(id));
        model.addAttribute("root", "/personas/delete");
        return "persona/deleteOne";
    }

    @DeleteMapping(value="/delete/{id}",
                   produces=MediaType.TEXT_HTML_VALUE)
    public String deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes){
        personaRestController.deleteOne(id);
        redirectAttributes.addFlashAttribute("message", "process.successful");
        return "redirect:/message";
    }

}

А также

@Controller
@RequestMapping(value="/personas")
public class PersonaRestController {

    private final PersonaService personaService;

    @Autowired
    public PersonaRestController(PersonaService personaService){
        this.personaService = personaService;
    }

    @DeleteMapping(value="/{id}")
    public ResponseEntity<Void> deleteOne(@PathVariable String id){
        personaService.deleteOne(id);
        return ResponseEntity.noContent().build();
    }

    ....

Как вы можете видеть, я не использую this. выполнить вызовы метода.

1 ответ

Решение

Кажется, проблема в тебе pointcut определение. Вы можете заметить, что ваш метод рекомендации выполняется только для методов с одним параметром, так как это связано с тем, что вы указали args(id) в объявлении pointcut. Он должен работать так, как вы ожидаете, если вы удалите args(id), но в этом случае необходимо использовать какой-то обходной путь для раскрытия значения параметра.

Я думаю, что это странное поведение формы AspectJ, потому что такие конструкции, как execution(* *.*(String, ..)) && args(arg) && target(t)) имеет четкий смысловой смысл, чтобы охватить все методы с String первый параметр и выставить его args, Это может быть ошибка или особенность, по крайней мере, для разработчиков AspectJ.

Чтобы получить то, что вы хотите, вы можете использовать обходной путь с joinPoint.getArgs() метод внутреннего совета, как это:

@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(..)) && target(object)")
public void deleteOnePointcut(Object object){}

@Before(value="ControllerPointcut.deleteOnePointcut(object)")
public void beforeAdviceDeleteOne(JoinPoint jp, Object object){
    Object id = jp.getArgs()[0];
    logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}
Другие вопросы по тегам