Implementing dynamic menu for Spring MVC/AOP appli

2019-05-07 15:04发布

问题:

I wish to implement dynamically changeable menu (updating whenever annotated method or controller added) for my Spring MVC application.

What i want is to introduce new annotation (@RequestMenuMapping) which will go to @Controller beans and their methods (just like @RequestMapping works).

Heres is what i want, User class, producing menu like

Users
    Index | List | Signup | Login

with following code:

@Controller
@RequestMapping("user")
@RequestMenuMapping("Users")
public class User {

    @RequestMapping("")
    @RequestMenuMapping("Index")
    public String index(/* no model here - just show almost static page (yet with JSP checks for authority)*/) {
        return "user/index.tile";
    }

    @RequestMapping("list")
    @RequestMenuMapping("List")
    public String list(Model model) {

        model.addAttribute("userList",/* get userlist from DAO/Service */);

        return "user/list.tile";
    }

    @RequestMapping("signup")
    @RequestMenuMapping("Signup")
    public String signup(Model model) {

        model.addAttribute("user",/* create new UserModel instance to be populated by user via html form */);

        return "user/signup.tile";
    }

    @RequestMapping("login")
    @RequestMenuMapping("Login")
    public String login(Model model) {

        model.addAttribute("userCreds",/* create new UserCreds instance to be populated via html form with login and pssword*/);

        return "user/login.tile";
    }
}

I think that Spring AOP may help me to pointcut methods with @RequestMenuMapping annotation and via @AfterReturning add something representing web-site menu to model.

But this raises two questions:

  1. How do i get Model instance in @AfterReturning advice method in case it is missing in adviced method (as in .index())?
  2. How do i get all methods (as in java reflection Method) and classes (as in java reflection Class) annotated with @RequestMenuMapping in order to build complete menu index?

回答1:

InterceptorDemo:

@Aspect
@Component
public class InterceptorDemo {

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void requestMapping() {
  }
  @Pointcut("@annotation(you.package.RequestMenuMapping)")
  public void requestMenuMapping() {
  }


  @AfterReturning("requestMapping() && equestMenuMapping()")
  public void checkServer(JoinPoint joinPoint,Object returnObj) throws Throwable {
      Object[] args = joinPoint.getArgs();
      Model m = (Model)args[0];
      // use joinPoint get class or methd...
  }
}

If you want to intercept Contoller with you own, you can wirte another pointcut and ProceedingJoinPoint object can get what you want.



回答2:

I think a better soultion would be a bean post processor to scan all controller classes for the @RequestMenuMapping and a HandlerInterceptor to add the menu items to every model map.



回答3:

Q1: ModelAndView object create at org.springframework.web.servlet.DispatcherServlet.doDispatch()

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// Do we need view name translation?
if (mv != null && !mv.hasView()) {
    mv.setViewName(getDefaultViewName(request));
}

So, you can intercept handle method after returing or override the method.

Q2:As far as i know, there are two ways getting annotation methods.

1.Use AOP: You can declare a pointcut like this:

@Pointcut("@annotation(you.package.RequestMenuMapping)")
public void requestMenuMappingPountcut() {
}

2.Use reflection.

Class clazz = Class.forName(classStr);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    if (method.isAnnotationPresent(RequestMapping.class)
            && method.isAnnotationPresent(RequestMenuMapping.class)) {
        // do something
    }
}