Finding a solution to extend Spring MVC with another component / annotation

Suppose I have a Website that is used in normal mode (in a browser) and in another mode, for example, in MobileView mode (inside a mobile application). For each Controller you create, there may be a corresponding controller for MobileView processing the same URL.

The simplest solution is to create ifs in all controllers that have MobileView logic. Another solution would be to use the appropriate URL for MobileView (similar to a normal URL) and two separate controllers (perhaps if one of them extends from the other or use the other way to reuse the common code)

But a more elegant solution would be to have some additional annotations, such as @SupportsMobileView (to mark the controller and say that the application will have the corresponding MobileView controller) and @MobileViewController (to mark the second controller and tell the application that this controller should start immediately after the initial controller marked with @SupportsMobileView). Communication between the regular controller and the MobileView controller will be through the URL that they process (determined using @RequestMapping).

Is it possible to extend Spring MVC (A)? Where to introduce new annotation scanners (B) and annotation handlers / component handlers (C)? How the MobileView (D) controller should be executed (now I think that it can be executed through AOP, where the new handler of my new controller type programmatically creates a connection point on the corresponding normal controller)

Note that I did not mention how this MobileView mode is activated and detected. Let's say that for this there is a logical variable Session (flag).

Critics are welcome at any point (A), (B), (C) or (D), as well as technical tips and an alternative solution to any point or the whole solution.

+4
source share
2 answers

HandlerInterceptor can be used to intercept RequestMapping processing. This is a simple example of configuration and implementation.

You can check your session variable and get a bunch of methods that will allow you to do custom processing or just exchange the view from the normal operation of the controller using your mobile view.

+5
source

Ok, warnings:

this is just a proof of concept , which I understood should be done like this:

+ @MobileViewEnable and @MobileView annotated (and related) methods should remain in the same controller

+ no validation of used httpAction

+ both methods must have the same signature

+ MobileView annotation value and requestMapping annotation value must be equal and uniques

+ the logic inside the call YourLogic (..) determines which method will be called, at the moment there is a very simple logic that checks if the parameter ("mobile") exists in the request, just to check

+ this code is not intended to be used as is (in general)

+ I don’t know if it works at all outside my computer (joke: D, ehm ..)

SO:

Annotations:

@Retention(RetentionPolicy.RUNTIME) public @interface MobileView { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) public @interface MobileViewEnable { } 

ExampleController:

 @Controller public class MainController extends BaseController { private final static Logger logger = LoggerFactory.getLogger(MainController.class); private final static String PROVA_ROUTE = "prova"; @MobileViewEnable @RequestMapping(PROVA_ROUTE) public String prova() { logger.debug("inside prova!!!"); return "provaview"; } @MobileView(PROVA_ROUTE) public String prova2() { logger.debug("inside prova2!!!"); return "prova2view"; } } 

Definition of aspect:

 <bean id="viewAspect" class="xxx.yyy.ViewAspect" /> <aop:config> <aop:pointcut expression="@annotation(xxx.yyy.MobileViewEnable)" id="viewAspectPointcut" /> <aop:aspect ref="viewAspect" order="1"> <aop:around method="around" pointcut-ref="viewAspectPointcut" arg-names="viewAspectPointcut"/> </aop:aspect> </aop:config> 

Aspect of implementation:

 public class ViewAspect implements BeforeAdvice, ApplicationContextAware { private final static Logger logger = LoggerFactory.getLogger(ViewAspect.class); private ApplicationContext applicationContext; public Object around(ProceedingJoinPoint joinPoint) { Method mobileViewAnnotatedMethod = null; HttpServletRequest request = getCurrentHttpRequest(); String controllerName = getSimpleClassNameWithFirstLetterLowercase(joinPoint); Object[] interceptedMethodArgs = getInterceptedMethodArgs(joinPoint); String methodName = getCurrentMethodName(joinPoint); Method[] methods = getAllControllerMethods(joinPoint); Method interceptedMethod = getInterceptedMethod(methods, methodName); String interceptedMethodRoute = getRouteFromInterceptedMethod(interceptedMethod); if (callYourLogic(request)) { mobileViewAnnotatedMethod = getMobileViewAnnotatedMethodWithRouteName(methods, interceptedMethodRoute); if (mobileViewAnnotatedMethod != null) return invokeMethod(mobileViewAnnotatedMethod, interceptedMethodArgs, controllerName); } return continueInterceptedMethodExecution(joinPoint, interceptedMethodArgs); } private Object continueInterceptedMethodExecution(ProceedingJoinPoint joinPoint, Object[] interceptedMethodArgs) { try { return joinPoint.proceed(interceptedMethodArgs); } catch (Throwable e) { logger.error("unable to proceed with intercepted method call: " + e); } return null; } private Object[] getInterceptedMethodArgs(JoinPoint joinPoint) { return joinPoint.getArgs(); } private boolean callYourLogic(HttpServletRequest request) { // INSERT HERE YOUR CUSTOM LOGIC (eg: is the server accessed from a mobile device?) // THIS IS A STUPID LOGIC USED ONLY FOR EXAMPLE return request.getParameter("mobile")!= null; } private HttpServletRequest getCurrentHttpRequest() { return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); } private String invokeMethod(Method method, Object[] methodArgs, String className) { if (method != null) { try { Object classInstance = getInstanceOfClass(method, className); return (String) method.invoke(classInstance, methodArgs); } catch (Exception e) { logger.error("unable to invoke method" + method + " - " + e); } } return null; } private Object getInstanceOfClass(Method method, String className) { return applicationContext.getBean(className); } private Method getMobileViewAnnotatedMethodWithRouteName(Method[] methods, String routeName) { for (Method m : methods) { MobileView mobileViewAnnotation = m.getAnnotation(MobileView.class); if (mobileViewAnnotation != null && mobileViewAnnotation.value().equals(routeName)) return m; } return null; } private String getRouteFromInterceptedMethod(Method method) { RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class); if (requestMappingAnnotation != null) return requestMappingAnnotation.value()[0]; return null; } private String getCurrentMethodName(JoinPoint joinPoint) { return joinPoint.getSignature().getName(); } private Method[] getAllControllerMethods(JoinPoint joinPoint) { return joinPoint.getThis().getClass().getSuperclass().getMethods(); } private String getSimpleClassNameWithFirstLetterLowercase(JoinPoint joinPoint) { String simpleClassName = joinPoint.getThis().getClass().getSuperclass().getSimpleName(); return setFirstLetterLowercase(simpleClassName); } private String setFirstLetterLowercase(String simpleClassName) { String firstLetterOfTheString = simpleClassName.substring(0, 1).toLowerCase(); String restOfTheString = simpleClassName.substring(1); return firstLetterOfTheString + restOfTheString; } private Method getInterceptedMethod(Method[] methods, String lookingForMethodName) { for (Method m : methods) if (m.getName().equals(lookingForMethodName)) return m; return null; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } 
0
source

Source: https://habr.com/ru/post/1497635/


All Articles