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

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

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

Этот подход к «Интернету» с использованием «Отдых», как зависимость объясняется в Согласование содержимого с использованием Spring MVC для раздела Combining Data and Presentation Formats. Пока здесь для разработки/тестирования и производства все работает нормально. Это ценный подход.

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

Проблема с AOP

О своей инфраструктуре у меня есть:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}

О @Controllers У меня есть два класса:

  • PersonaDeleteOneController with:
    • deleteOne(@PathVariable String id, Model model) for @GetMapping
    • deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes) за @DeleteMapping
  • PersonaRestController
    • deleteOne(@PathVariable String id) for @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. для выполнения вызовов метода.


person Manuel Jordan    schedule 26.09.2016    source источник
comment
Можете ли вы предоставить примеры кода с вызовами методов класса @Controller? Проблема может заключаться в том, что вы пытаетесь сделать что-то вроде this.deleteOne(...).   -  person Sergey Bespalov    schedule 28.09.2016
comment
Готово, см. раздел Alpha. Спасибо.   -  person Manuel Jordan    schedule 28.09.2016


Ответы (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);
}
person Sergey Bespalov    schedule 29.09.2016
comment
Работает. Огромное спасибо. Это дело funny или tricky. Я надеюсь, что какой-нибудь AspectJ разработчик прочитает этот пост. И если это действительно ошибка, то следует поднять вопрос. - person Manuel Jordan; 29.09.2016
comment
Кстати, для другого @Pointcut для другого выражения (не для этого случая), содержащего (String, ..), у меня может быть args(id, ..). Я думал, что нельзя использовать .. в разделе args(). - person Manuel Jordan; 08.10.2016