`

Spring MVC 与 Jython的联姻

 
阅读更多

Jython Spring MVC Controllers

 

 

In my last blog I wrote about Spring transactional services written in Jython, and now I want to write about my way to use Spring MVC with Jython.
I will describe the following steps:

 

  • preparing the web.xml
  • Spring servlet configuration
  • Jython controller

In the web.xml, beside the ContextLoaderServlet we need a Spring Front Controller that is the entry point to all requests. This Front Controller then dispatches to the specific Task or Action or how ever you want to call it. Here is the servlet declaration and it's mapping for all URLs ending with .py


=== web.xml ===
 <servlet>
  <servlet-name>example</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>example</servlet-name>
  <url-pattern>*.py</url-pattern>
 </servlet-mapping>


For the servlet name "example" we need a configuration file in the WEB-INF folder that starts with "example" and the servlet name that always ends with "-servlet.xml". This file is finally called "example-servlet.xml" and holds the bean configurations for the Spring MVC part.


=== example-servlet.xml ===
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean id="jythonController" class="com.my.view.controller.JythonController">
        <property name="mappings">
            <props>
                <prop key="/index.*">com.my.view.controller.ForwardController(view="home", activeMenu="home")</prop>
                <prop key="/login.*">com.my.view.controller.LoginController(view="login", activeMenu="login")</prop>
            </props>
        </property>
    </bean>

    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/*.py">jythonController</prop>
            </props>
        </property>
    </bean>
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
        <property name="prefix"><value>/WEB-INF/jsp/</value></property>
        <property name="suffix"><value>.jsp</value></property>
    </bean>
</beans>


This MVC configuration needs some detailed explanation because it wires the Spring and Jython world. The urlMapping part catches all requests that end with *.py and delegates them to the jythonController that is a native Java class. It also could be a java-compatible Jython class, but I simply chose native Java. The jythonController then delegates the specific request with its specific url to the mapped Jython controller class. This mappings are declared as a property element in the jythonController bean where the key is the url pattern like ".../index.py" or ".../login.py" and the value is a Jython expression, that will create a new instance of the Jython controller class. So while Spring initializes the Jython Controller bean, the Jython statements are executed and the resulting Jython controller instances are cached as singletons.
So the trick is simply that a native Java controller instance stores a number of Jython controller classes which will then execute the concrete controller functionality. You can see it as a babushka doll with 'controllers-within-controllers'.

The next advantage is that the Jython controller singletons can be configured declaratively. In this example I say that the LoginController should froward to the "login" view that will result in the "WEB-INF/jsp/login.jsp" page (see viewResolver bean), and that the highlighted menu (activeMenu argument) should be "login" in the shown page's navigation.
Because IMO it is so intuitive, here it is in other words.
The jython Controller executes the Jython statement


 com.my.view.controller.LoginController(view="login", activeMenu="login")


The corresponding Jython code of the controller would look like the following listing.


=== LoginController.py ===
 from org.springframework.web.servlet.mvc import Controller
 from org.springframework.web.servlet import ModelAndView
 from org.springframework.web.context.support import WebApplicationContextUtils
 from java.util import HashMap
 from java.lang import Boolean, System, Integer
 
 class LoginController(Controller):
     def __init__(self, view=None, activeMenu=None):
         self.view = view
         self.activeMainMenu = None
         self.activeSubMenu = None
         if activeMenu != None:
             activeMenuTokens = activeMenu.split(".")
             self.activeMainMenu = activeMenuTokens[0]
             if len(activeMenuTokens) == 2:
                 self.activeSubMenu = activeMenuTokens[1]
         
     def getApplContext(self, request):
         servletContext = request.getAttribute("servletContext")
         return WebApplicationContextUtils.getWebApplicationContext(servletContext)
         
     def getLoginService(self, request):
         return self.getApplContext(request).getBean("loginService");
         
         
     def handleRequest(self, req, res):
         """Ensures an existing user session, sets the user's view settings
         and calls completeModel().
         This method should not be overridden."""
         session = req.getSession(Boolean.TRUE)
         
         model = HashMap()
         model.put("activeMainMenu", self.activeMainMenu)
         model.put("activeSubMenu", self.activeSubMenu)
         # do the real processing
         self.completeModel(req, session, model)
         
         return ModelAndView(self.view, model)
    
     def completeModel(self, req, session, model):
         """Handles the complete controller logic and fills the model.
         The model is a java HashMap.
         This method can be overridden."""
         
         self.getTestService(req).checkLogin(req)
         # ...
         # ...


In the previous code the controller accesses the loginService bean, that is also a Spring bean, to get the service instance from the Spring application context. To accomplish this, the request has to hold the servletContetxt instance. This is not a very elegant solution, but better ones are wellcome.
The model is represented by a simple java.util.HashMap which is later on transformed to the request scope by Spring MVC (BTW this is the same way I handle the model in my open source project Flow4J).
You can also simply convert the LoginController in a generic BaseController of which you can subclass and override only the completeModel() method.
For the curious ones, I also support my implementation of the native Java JythonController class


=== JythonController.java ===
package de.my.view.controller;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.python.core.PyInstance;
import org.python.core.PyJavaInstance;
import org.python.util.PythonInterpreter;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.util.UrlPathHelper;

public class JythonController extends AbstractController {

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    private Map urlMappings;

    private Map controllerMappings = new HashMap();

    /**
     * Spring callback that creates the Jython controller instances and caches
     * the singletons.
     * 
     * @param mappings the contrller mappings
     */
    public void setMappings(Properties mappings) {
        this.urlMappings = mappings;

        for (Iterator iter = urlMappings.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            String urlPath = (String) entry.getKey();
            String expr = (String) entry.getValue();
            System.out.println("register controller [urlPath: " + urlPath
                    + ", expr: " + expr + "]");

            Controller controller = (Controller) getPyJavaObject(expr,
                    Controller.class);
            controllerMappings.put(urlPath, controller);
        }
    }

    /**
     * Evaluates the jython statement and returns the result casted to the given
     * type. Uses the JythonInitServlet that stores an sntance of the Jython interpreter.
     * 
     * @param stmnt jython statement
     * @param clazz type to which the result should be casted
     * @return the statements result casted to the given type
     * @see JythonInitServlet
     */
    static public Object getPyJavaObject(String stmnt, Class clazz) {
        PythonInterpreter interp = JythonInitServlet.getInterpreter();
        Object result = interp.eval(stmnt);
        if (result instanceof PyJavaInstance) {
            PyJavaInstance inst = (PyJavaInstance) result;
            return inst.__tojava__(clazz);
        } else if (result instanceof PyInstance) {
            PyInstance inst = (PyInstance) result;
            return inst.__tojava__(clazz);
        }

        throw new RuntimeException("Cannot evaluate statement '" + stmnt
                + "' and force result to '" + clazz.getName() + "'");
    }

    /**
     * Returns the Jython controller singleton that is mapped to the given url.
     * 
     * @param urlPath the url path for which the controller is looked up
     * @return the Jython controller
     */
    private Controller getController(String urlPath) {
        for (Iterator iter = controllerMappings.entrySet().iterator(); iter
                .hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            String registeredPath = (String) entry.getKey();
            if (PathMatcher.match(registeredPath, urlPath)) {
                return (Controller) entry.getValue();
            }
        }
        return null;
    }

    /**
     * The Spring MVS callback for executing the controllers functionality.
     * Looks up the Jython controller singleton and delegates the request to it.
     * 
     * @return the model and view
     * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    public ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        request.setAttribute("servletContext", getServletContext());

        String lookupPath = urlPathHelper.getLookupPathForRequest(request);
        Controller controller = getController(lookupPath);
        ModelAndView modelAndView = controller.handleRequest(request, response);

        return modelAndView;
    }
}




That is i for now, and comments on this topic are appreciated.

分享到:
评论

相关推荐

    Jython示例

    java调用Python示例,详情请参考博文:http://blog.csdn.net/l1028386804/article/details/50915556

    jython2.5.4_与jython2.7.0包

    该资源包含jython2.5.4.jar和jython2.7.0.jar,jython的版本要与jdk的版本要对应,这样可以解决“Unsupported major.minor version 51.0”问题。其中,对于jdk1.6.x的版本,请使用jython2.5.4.jar jdk1.7.x的版本,...

    spring-batch-jython:能够在python中实现一个spring批处理作业

    spring-batch-jython 能够在 python 中实现一个 spring 批处理作业这是一项正在进行的工作 使用: make deps 创建 deps。 make test 测试python接口

    java与Jython的相互调用

    java与Jython的相互调用,对初学者很有帮助

    Spring-Jython:Java将Python脚本和类集成到Spring Boot Web应用程序中。 由Jython提供技术支持

    一个具有Jython集成的简单Java Spring Boot应用程序。 安装 git clone git@github.com:amemifra/Spring-Jython.git 您的系统必须已安装 Java 玛文 VS代码 Java扩展包-VS代码扩展 其余客户端扩展-VS代码扩展 例子 ...

    jython介绍及使用

    自己总结了一下jython的特性。供大家共同学习。 Jython 是一种完整的语言,而不是一个简单的 Java 翻译器或 Python 编译器,它是 Python 在 Java 中的完整实现。由于 Jython 继承了 Java 和 Python 二者的特性从而使...

    jython-standalone-2.7.0(最新版本)

    如果windows的用户安装插件有问题的,尝试一下换这个jython: Jython官网: https://www.jython.org/downloads.html Jython环境安装包: ...

    jython官方jython-standalone-2.7.1b2版本(版本全)

    jython官方正版资源 为了省去大家找Jython安装包的时间,附上此Jython版本。 Jython是一种完整的语言,而不是一个Java翻译器或仅仅是一个Python编译器,它是一个Python语言在Java中的完全实现。

    jython_installer-2.5.2

    Jython是一种完整的语言,而不是一个Java翻译器或仅仅是一个Python编译器,它是一个Python语言在Java中的完全实现。Jython也有很多从CPython中继承的模块库。最有趣的事情是Jython不像CPython或其他任何高级语言,它...

    Burpsuite环境Jython安装方法1

    Burpsuite 环境 Jython 安装by:裁决Jython 介绍Jython 是一种完整的语言,而不是一个 Java 翻译器或仅仅是一个 Python

    jython-2.5.2

    jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-2.5.2jython-...

    jython-installer-2.7.3.jar

    交互式实验-Jython提供了一个交互式解释器,可用于与Java包或运行的Java应用程序交互。这允许程序员使用Jython来实验和调试任何Java系统。 快速应用程序开发——Python程序通常比等效Java程序短2-10倍。这直接转化为...

    jython各个版本下载地址

    jython各个版本下载地址 版本列表 --------------------------------------------- 2.2/ 19-Oct-2009 07:57 jython-2.2-sources.jar 24-Aug-2007 10:23 682810 jython-2.2-sources.jar.md5 24-Aug-2007 10:23 128 ...

    jython解析xml文件

    jython解析xml文件

    jython写jsp标签

    jython写jsp标签

    The Definitive Guide to Jython-Python for the Java Platform

    Jython is an open source implementation of the high-level, dynamic, object-oriented scripting language Python seamlessly integrated with the Java platform. The predecessor to Jython, JPython, is ...

    jython 2.2.1 下载

    jython 2.2.1 下载 jython 2.2.1 下载 jython 2.2.1 下载 jython 2.2.1 下载

    jython-standalone-2.7.2.jar

    jython-standalone-2.7.2.jar,java应用调用Python。

    jython中文详细教程

    ibm jython中文教程 讲得非常详细

    Jython教程

    Jython 教程 来自IBM developerworks

Global site tag (gtag.js) - Google Analytics