【问题标题】:Looking for a solution to extend Spring MVC with another Component/Annotation寻找使用另一个组件/注释扩展 Spring MVC 的解决方案
【发布时间】:2013-08-27 06:05:50
【问题描述】:

假设我有一个在正常模式(浏览器)和其他模式下使用的网站,例如 MobileView 模式(在移动应用程序内)。对于我创建的每个控制器,可能有 MobileView 的对应控制器,处理相同的 url。

最简单的解决方案是在所有具有 MobileView 逻辑的 Controller 中创建 ifs。另一种解决方案是使用 MobileView 的对应 url(类似于普通 url)和两个单独的 Controller(可能一个从另一个扩展;或使用其他方式来回收公共代码)

但是,一个更优雅的解决方案是添加一些额外的注释,例如 @SupportsMobileView(标记一个控制器,并告诉应用这将有一个对应的 MobileView 控制器)和 @MobileViewController(标记第二个控制器,以及告诉应用此控制器需要在标有@SupportsMobileView 的初始控制器之后立即运行)。普通控制器和 MobileView 控制器之间的链接将通过它们处理的 url(使用 @RequestMapping 定义)。

是否可以扩展 Spring MVC (A)?在哪里注入新的注释扫描器(B)和注释处理程序/组件处理程序(C)? MobileView 控制器应该如何执行(D)(现在我在想它可以通过 AOP 执行,其中我的新控制器类型的新处理程序以编程方式在相应的普通控制器上创建一个 Join-Point)

注意我没有提到这个 MobileView 模式是如何被触发和检测的。我们只是说有一个 Session 布尔变量(标志)。

欢迎对任何点 (A)、(B)、(C) 或 (D) 提出批评,以及任何点或整个解决方案的技术提示和替代解决方案。

【问题讨论】:

  • 我认为这在实际意义上是不可能的,因为您必须重新编写所有 Spring 注释才能执行它们现在所做的其他事情。对于这个“移动视图”,我假设这不仅仅是一个以不同方式格式化的 HTML 页面?
  • @CodeChimp 不,如果我在 MobileView 中,我需要做一些业务逻辑,这与普通视图逻辑不同
  • 什么样的业务逻辑?这可能是适合作为听众或过滤器的东西吗?或者也许你可以用 AOP 做点什么?
  • @CodeChimp 实际上,我需要的是,或者,在控制器内部,调用代表某些业务逻辑的接口,其中将根据某些内容注入具体逻辑(普通或移动视图)在会话中(并通过过滤器进行这种策略注入),或者,创建两个单独的控制器,对它们进行注释以区分哪个是正常的,哪个是移动视图(但让它们处理相同的url),然后决定哪个控制器根据会话中的某些内容使用(假设再次由过滤器决定,因为它在控制器之前运行)
  • 再一次,我认为您正在尝试做一些不一定可能的事情,或者至少不是事情的设计方式。但是,我认为您可以研究一些可行的解决方法。我认为最简单的建议是使用 (at)RequestMapping 注释对按参数排序的请求进行排序,但这是假设您可以通过 POST/GET 参数直接发送“移动”指示符作为会议。或者,是否可以更改使用的 URI?

标签: spring spring-mvc architecture


【解决方案1】:

HandlerInterceptor 可用于拦截 RequestMapping 处理。 This 是一个如何配置和实现的简单示例。

您可以检查您的会话变量,并且会有一堆方法允许您进行自定义处理,或者只是将来自普通控制器处理的视图与您的移动视图交换。

【讨论】:

    【解决方案2】:

    好的,警告:

    这只是我所理解的必须这样做的概念证明:

    +@MobileViewEnable 和@MobileView 注解的(和相关的)方法需要留在同一个控制器中

    +没有检查使用的httpAction

    +这两个方法必须有相同的签名

    +mobileView注解值和requestMapping注解值必须相等且唯一

    +callYourLogic(..) 里面的逻辑定义了要调用哪个方法,目前有一个非常简单的逻辑来检查请求中是否存在参数(“mobile”),只是为了测试

    +此代码不打算按原样使用(根本)

    +不知道它在我的电脑之外是否能正常工作(笑话:D,嗯..)

    所以:

    注释:

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

    示例控制器:

    @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";
        }
    }
    

    方面定义:

    <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>
    

    方面实现:

    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 (e.g.: 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;
            }
    
        }
    

    【讨论】:

      猜你喜欢
      • 2016-03-01
      • 1970-01-01
      • 2016-06-04
      • 1970-01-01
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多