`

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

    Jython是一种基于Java平台的Python实现,它允许Python代码与Java平台无缝集成。Jython的出现使得Python开发者能够利用Java的强大功能,同时保持Python的简洁性和易读性。它不仅实现了Python标准库,还支持大部分Java...

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

    Jython教程

    8. **Jython for Web开发**:Jython可以与Java Web框架如Spring、Struts或Play Framework集成,用于构建高效、可扩展的Web应用。 9. **Jython与Java的互操作性**:Jython提供了`__javabridge__`模块,使Python代码...

    jython中文详细教程

    1. **Python与Jython的区别** Python是一种跨平台的高级编程语言,以其易读性强和代码简洁著称。Jython则是Python的一个变种,它与Java紧密集成,允许开发者使用Python语法编写Java应用程序。 2. **Jython的优势**...

    jython官方jython-standalone-2.7.0版本(版本缺失)

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

    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简易教程

    1. Web开发:Jython可以结合Java的Web框架,如Spring或Struts,构建Web应用。 2. 数据分析:借助Java的数据处理库,如Apache Commons Math,Jython可进行复杂的数据分析。 3. 自动化测试:利用Jython编写测试脚本,...

    jython-standalone-2.7.0.zip

    标签 "burp" 和 "jython" 确定了这个压缩包的主要用途,即与Burp Suite的Python插件开发有关。Burp Suite的用户通常会用Jython来创建自定义扫描规则、代理拦截逻辑或自动化测试脚本,提高他们的渗透测试效率。 在...

    jython各个版本下载地址

    ### Jython 各个版本下载地址解析 Jython 是一种Python的实现,它允许Python代码在Java平台上运行。本文将详细介绍Jython不同版本的下载地址及相关文件信息。 #### Jython 2.2 版本 - **发布日期**:2007年8月24...

    jython-standalone-2.7.0.jar

    总的来说,Jython是Java与Python融合的一种有效手段,尤其适用于那些需要利用Python生态但又不希望完全脱离Java环境的项目。无论是进行快速原型开发,还是在现有Java系统中引入Python功能,Jython都是一个值得考虑的...

    Burpsuite环境Jython安装方法1

    Jython 与传统的 CPython(Python 的标准实现)有所不同。它不是简单的 Java 翻译器或编译器,而是一个完整的 Python 解释器,实现了 Python 的语法和标准库,并且能够调用 Java 类库。这意味着 Jython 程序员不仅...

    Jython简单配置和使用

    Jython 简单配置和使用 Jython 是一种完整的语言,而不是一个 Java 翻译器或仅仅是一个 Python 编译器,它是一个 Python 语言在 Java 中的完全实现。Jython 也有很多从 CPython 中继承的模块库。最有趣的事情是 ...

    jython2.5-安装包

    1. **与Python兼容**:Jython遵循Python的语法和语义,使得熟悉Python的开发者能够无缝地过渡到Jython环境。它支持大部分标准库,同时也提供了对Java类库的访问。 2. **运行在Java平台上**:Jython程序可以直接在...

    jython安装和使用方法

    Jython 安装和使用方法 Jython 是一个基于 Java 语言的 Python 实现,它可以让 Python 语言运行在 Java 平台上。下面将详细介绍 Jython 的安装和使用方法。 安装 Jython 首先,需要从 Jython 官方网站下载 Jython...

    jython学习文档

    这份文档主要介绍了两个核心概念:一个是可以与Jython对象映射的Java接口类,以及一个用于从Jython文件中获取Java对象、Jython对象和Jython函数对象的工厂方法类。 ### Jython与Java接口的映射 首先,我们关注到的...

    jython

    标题 "Jython" 提到的是一个与Python编程语言相关的开源项目——Jython。Jython是Python的一个实现,它允许Python代码在Java平台上运行。Jython将Python语言的灵活性和简洁性与Java平台的强大功能相结合,使得开发者...

    jython-standalone-2.7.1.jar

    《Jython:Java与Python的桥梁》 Jython,这个名字源于Java和Python的结合,是一种Python的实现,它允许在Java平台上运行Python代码。标题中的"jython-standalone-2.7.1.jar"是一个独立的Jython版本,特别适用于...

Global site tag (gtag.js) - Google Analytics