4

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

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

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

是否可以扩展 Spring MVC (A)?在哪里注入新的注释扫描器(B)和注释处理程序/组件处理程序(C)?MobileView 控制器应该如何执行 (D)

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

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

4

2 回答 2

5

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

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

于 2013-08-21T19:29:56.977 回答
0

好的,警告:

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

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

    }
于 2013-08-26T21:54:34.097 回答