Java、Spring和Javascript的集成
来源:互联网 发布:闰秒 知乎 编辑:程序博客网 时间:2024/05/22 03:17
Java、Spring和Javascript的集成
本文讲解内容为如何基于Spring MVC来实现Java与Javascript集成。项目主要利用Spring来组织本次项目的Java生态,并使用Javascript脚本语言对宿主语言Java进行功能扩展。涉及到的知识点有以下几点:
Maven构建环境搭建
Java的ScriptEngine
Spring应用关联文
准备工作
为了简化配置,这里使用maven来实现项目构建。由于我使用的是STS(Spring Tool Suite),maven插件以及相关的实现已经内置在IDE中,所以不需要额外进行环境配置,对于没有准备好构建的环境的童鞋还请自己动手。
在配置maven的时候,由于GFW(Great Firewall of China,简写为Great Firewall)的存在,有时会导致一些存放在国外服务器上的maven repository无法访问,所以我们需要手动添加国内镜像仓储,具体详情请参考文档:http://maven.oschina.net/help.html
。按照文档中的说明配置完成之后,请右键点击项目图标,依次选择Maven、Update Projects…,然后静静等待5至10分钟(这时主要是下载构建时依赖的jar包,最终速度取决于网络情况),直至项目编译完成。
PS:如果项目总是无法编译且红叉不断的话,我们可以尝试删除未完成的依赖更新文件
*.jar.lastUpdated
,删除临时文件的目的是为了让maven重新下载依赖包。下面就是其中一种例子:
./repository/org/springframework/spring-test/3.2.3.RELEASE/spring-test-3.2.3.RELEASE.jar.lastUpdated
添加并配置Controller
配置Spring MVC
maven环境搭建完成之后我们就可以正式进入开发了。第一个要添加的就是Controller类(Spring MVC),为我们的项目提供一个入口文件。
配置好web.xml并在mvc-config.xml中配置Controller服务,示例代码如下:
<?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>JavaSpringJavascriptIntegrationWorld</display-name> <!-- - Location of the XML file that defines the root application context. - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application-config.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- - Servlet that dispatches request to registered handlers (Controller implementations). --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/mvc-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
web.xml里定义了两个配置文件,一个是供Spring MVC使用的mvc-config.xml,另一个是非Web关联文下的application-config.xml,至于这两者之间到底有什么区别,我们可以从后面的运行时信息里找到答案。出了两个关联文配置文件外,剩余的则是Spring MVC定义时所需的必要配置内容,这里不做多解释。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Uncomment and your base-package here: <context:component-scan base-package="org.springframework.samples.web"/> --> <context:component-scan base-package="info.woody" resource-pattern="**/*Controller.class"/> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' --> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> </bean></beans>
上面是mvc-config.xml文件的内容,自动扫描功能已经开启,位于包info.woody下的所有Controller类都会被自动加入Spring的容器管理范围内,并且支持注解形式的注入。
测试Spring Controller
配置文件完成后,Controller就能够按照预期设想的那样工作了。下面的代码仅供参考!
package info.woody;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.stereotype.Controller;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controller@RequestMapping(value="/cowboy")public class CowboyController { @RequestMapping(value="/greeting", method=RequestMethod.GET) public void greeting(HttpServletRequest request, HttpServletResponse response) throws IOException { String greetingString = "Hello, "; String name = "anonymous"; if (StringUtils.hasLength(request.getParameter("name"))) { name = request.getParameter("name"); } response.getWriter().println(greetingString.concat(name)); }}
接下来请检查一下Web关联文,其实也就是我们的程序访问路径。在项目图标上右键,选择Properties,Web Project Settings,找到Context root值:JavaSpringJavascriptIntegrationWorld
这时我们可以推测出最终完成的URL地址应该为:http://localhost:8080/JavaSpringJavascriptIntegrationWorld/cowboy/greeting
。JavaSpringJavascriptIntegrationWorld
是Web关联文,然后是Controller类中定义的映射地址cowboy
,最后是greeting
。好了,现在可以启动tomcat来调试啦!在eclipse里点击Debug As/Debug on Server/选择你配置好的tomcat或其他Servlet容器。
访问http://localhost:8080/JavaSpringJavascriptIntegrationWorld/cowboy/greeting
时结果如下:
Hello, anonymous
访问http://localhost:8080/JavaSpringJavascriptIntegrationWorld/cowboy/greeting?name=Woody
时结果如下:
Hello, Woody
服务扩展
访问入口已经准备完毕,接下来就是集成各种Service和利用Javascript进行功能扩展的时候了。基本思路是用Spring注入程序运行所需的服务,在某些服务中集成Javascript。脚本的集成可以让我们的业务行为更为灵活,快速地适应需要变化。还记得web.xml里面的application-config.xml
吗?
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application-config.xml</param-value> </context-param>
我们定义的大部分服务都是处于这个配置文件的,这样做的目的就是为了让Controller和Service进行分离,程序界限一下子就清晰多了。下面是Service的具体配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Uncomment and add your base-package here: <context:component-scan base-package="org.springframework.samples.service"/> --> <context:component-scan base-package="info.woody" resource-pattern="**/*Service.class"/></beans>
下面是Service的实现,它实现了:
Javascript集成 —— 利用ScriptEngine运行Javascript脚本,这里的Javascript虽是hardcode的,但我们完全可以重新对它进行扩展,动态调整实现逻辑。这样就可以让程序的行为在运行时得到改善。
自动bean注入 —— 这里我们将Spring容器中管理的Service全部放置于Javascript的运行环境中,这样Javascript脚本就可以直接访问这些Service所提供的服务啦。
类BuzzLightYearService
中的代码先是在方法fly里利用ScriptEngine实现了Javascript脚本集成,并在正式运行脚本之前准备了受Spring容器管理的Service,之后变量fly$cript
中存放的脚本一方面利用脚本本身的功能进行计算(把name中的字符全部转换成大写格式),另一方面脚本作者可以在毫不知情的情况下直接使用宿主环境中Spring容器里的Service(buzzLightYearService)。脚本执行的输出结果我们可以从变量bindings
中获取,其实bindings既可以传入参数,又可以输出参数。
package info.woody;import javax.script.Bindings;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.stereotype.Component;@Componentpublic class BuzzLightYearService { @Autowired private ApplicationContext ac; @Autowired private ContextsApplicationListenerService cals; private String fly$cript = "// JavaScript " + "\n function upperName(name) { " + "\n if (name) { " + "\n return name.toUpperCase(); " + "\n } " + "\n return name; " + "\n } " + "\n var greetingString = 'Hello, '; " + "\n var name = words.replace(greetingString, ''); " + "\n var result = greetingString + upperName(name); " + "\n result += ' - Thanks for the invocation from ' + " + "\n 'the outer Java service: ' + buzzLightYearService; "; public String fly(String words) { cals.dumpBeanNames(ac); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine se = sem.getEngineByExtension("js"); Bindings bindings = se.createBindings(); if (null != this.ac) { String[] names = this.ac.getBeanDefinitionNames(); for (String name : names) { Object springBean = this.ac.getBean(name); if (!name.startsWith("org.spring")) { bindings.put(name, springBean); } } } try { bindings.put("words", words); se.eval(fly$cript, bindings); } catch (ScriptException e) { e.printStackTrace(); } return bindings.get("result") + "!\nLet me help you be 13 and fly!!!"; }}
为了识别出mvc-config.xml和application-config.xml两者之间的分别,我对之前的Controller做了下调整,目的是为了打印出一些与运行时有关的信息。
package info.woody;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.stereotype.Controller;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controller@RequestMapping(value="/cowboy")public class CowboyController { @Autowired private ApplicationContext ac; @Autowired private ContextsApplicationListenerService cals; @Autowired BuzzLightYearService buzzLightYearService; @RequestMapping(value="/greeting", method=RequestMethod.GET) public void greeting(HttpServletRequest request, HttpServletResponse response) throws IOException { cals.dumpBeanNames(ac); String greetingString = "Hello, "; String name = "anonymous"; if (StringUtils.hasLength(request.getParameter("name"))) { name = request.getParameter("name"); } response.getWriter().println(buzzLightYearService.fly(greetingString.concat(name))); }}
运行时信息的打印主要依靠下面的类,它会自动收集用于Bean管理的Spring应用关联文对象,详情请参考Spring的ApplicationListener。
package info.woody;import java.util.ArrayList;import java.util.Arrays;import java.util.Hashtable;import java.util.List;import java.util.Map;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ApplicationContextEvent;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.context.event.ContextStartedEvent;import org.springframework.stereotype.Component;@Componentpublic class ContextsApplicationListenerService implements ApplicationListener<ApplicationContextEvent> { private Map<String,ApplicationContext> contextMap = new Hashtable<String,ApplicationContext>(); @Override public void onApplicationEvent(ApplicationContextEvent event) { if( event instanceof ContextStartedEvent || event instanceof ContextRefreshedEvent){ this.getContextMap().put(event.getApplicationContext().getDisplayName(), event.getApplicationContext()); } } public Map<String,ApplicationContext> getContextMap() { return contextMap; } public void dumpBeanNames(ApplicationContext ac) { StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2]; List<Object> metaInfo = new ArrayList<>(Arrays.asList( stackTraceElement.getFileName(), stackTraceElement.getMethodName(), stackTraceElement.getLineNumber() )); System.out.println(metaInfo); System.out.println(String.format("Application Context Class: %s, hash code: %s", ac.getClass().getName(), ac.hashCode())); for (String name : ac.getBeanDefinitionNames()) { System.out.println(name); } }}
前端输出与后端输出的分析
前端分析
访问http://localhost:8080/JavaSpringJavascriptIntegrationWorld/cowboy/greeting
时结果如下:
Hello, ANONYMOUS - Thanks for the invocation from the outer Java service: info.woody.BuzzLightYearService@51107492!Let me help you be 13 and fly!!!
访问http://localhost:8080/JavaSpringJavascriptIntegrationWorld/cowboy/greeting?name=Woody
时结果如下:
Hello, WOODY - Thanks for the invocation from the outer Java service: info.woody.BuzzLightYearService@51107492!Let me help you be 13 and fly!!!
与之前的输出结果相比,所有的用户名都自动变成了大写格式,这是因为name在Javascript中被转换为大写的缘故。
后端分析
每次访问我们都会从后台看到下面的输出信息:
[CowboyController.java, greeting, 31]Application Context Class: org.springframework.web.context.support.XmlWebApplicationContext, hash code: 248613184cowboyControllerorg.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalRequiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessormvcContentNegotiationManagerorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0org.springframework.format.support.FormattingConversionServiceFactoryBean#0org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0org.springframework.web.servlet.handler.MappedInterceptor#0org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0org.springframework.web.servlet.handler.BeanNameUrlHandlerMappingorg.springframework.web.servlet.mvc.HttpRequestHandlerAdapterorg.springframework.web.servlet.mvc.SimpleControllerHandlerAdapterorg.springframework.web.servlet.view.InternalResourceViewResolver#0org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor[BuzzLightYearService.java, fly, 35]Application Context Class: org.springframework.web.context.support.XmlWebApplicationContext, hash code: 1787740029buzzLightYearServicecontextsApplicationListenerServiceorg.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalRequiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
这两部分信息分别来自于配置文件mvc-config.xml
和application-config.xml
。事实说明我们之前的说法是正确的,前者中只包括Controller相关的类信息,后者包括Service相关的类信息。为了明确区分两者,我们还把运行时的文件及行号信息输出以供参考。
完整的项目源码下载地址:http://download.csdn.net/detail/rcom10002/9292623
。其中包含两部分内容,一部分是项目源码,另一部分是可以直接部署运行的WAR程序。如果在编译项目的时候缺少相关依赖jar文件,可以按照文章最开始的maven配置一节进行依赖配置,或者是从WAR程序下的WEB-INF/lib目录中拷贝所需要的jar文件。
- Java、Spring和Javascript的集成
- Java的单元测试和集成spring单元测试
- mongodb java api和spring的集成使用
- drools和spring的集成
- spring和struts的集成
- spring和struts的集成
- Spring 和 Hibernate的集成
- JSF和Spring的集成
- JSF和Spring的集成
- spring和quartz的集成
- Spring 和 Hibernate的集成
- spring和mongo的集成
- drools和spring的集成
- spring和hibernate的集成
- spring和mybatis的集成
- Java定时器quartz和spring集成
- JCR集成Java内容仓库和Spring
- spring和struts1和jpa的集成
- iOS关于Xcode上的Other linker flags
- Mac 环境下配置opencv(Eclipse)
- AndroidManifest.xml权限设置
- 红黑树
- 对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
- Java、Spring和Javascript的集成
- C# 对象类型的转化
- linux block层的class diagram
- 编译器的工作过程
- JAVA 3DES加密解密
- oracle 连接 web service (利用soap_api)
- oracle 分区表的建立方法
- python codeforces 322 div2 C
- 拍摄的照片上传之后旋转如何解决?