在传统的java web开发过程中,我们使用url通知后台服务器调用java代码。
不管是页面submit还是ajax数据提交,总是要提供一个url。结果我们看到,jsp或者js中到处充斥着url代码(就像当年jsp中到处充斥着java代码一样)。
有时,服务器的某功能的url地址发生改变了(比如我以前参与的一个struts1的项目,struts-config-module改变了,或者删除了),那么要到页面中找到所有写了这个url地址的页面,一个一个给修改来。如果项目涉及5000过个jsp,而有的jsp中的路径中的一部分是由变量生成出来的,那么找到这些url并修正简直是一场噩梦。当然,可以在整个工程中进行批量替换,但是由此引起的系统变化,总会给担心质量的人造成很大的心理负担。
最早的时候,我看到页面上有java代码让我感觉心理很没底,现在这个ajax横行的年代,很难让一个程序员有自制力去管理好他的url,页面上乱七八糟的url带着匪夷所思的问号及参数,更让人觉得无所适从。调试和系统维护的时候,从一个url找到这个url最终调用的java类及方法,还是要费一番脑筋和周折的。
感谢dwr的出现,事情好像有了一些转机。我有幸在一个项目中用到了dwr技术,那是三年前,dwr的版本应该比较低,需要在一个配置文件中配置dwr调用需要的java类、方法好像还有参数或者返回值的类型。然后,在页面中,还要引入两个dwr的js,一个好像是dwr-core,另外一个是和刚刚在配置文件中相关的java类的名字相同的一个js,这个js好像是动态生成的。当时我刚学习java技术,在一家公司中从事程序员,都是把别人的代码复制过来改改,没有做深入的研究。当时虽然没有明白dwr的原理,但是还是感谢dwr可以让我偷懒:我不用再配置struts-congfig-xyz.xml,不用在action里加if,else,不用在页面上写讨厌的url。只要声明一个dwr配置文件,一切都好办了。
后来,就是最近几天,我在搭建一个最小的开发框架(也就是组装网上一些开源的框架)时,意识到我又将面对那些url:项目组其它人在使用这个小开发框架开发系统时,又会在jsp上把url写的到处都是。于是我想象了一种网页请求服务器的开发方式,在jsp页面中,如果能写如下代码,就好了: import com.siqisoft.dushuhui.book.controller.RecommendBookAction;
RecommendBookAction recommendBookAction = new RecommendBookAction();
bookName = "<三国演义>";
recommendBookAction.addBook(bookName); -嗯?又在jsp页面中写java代码?
-不是的,我是希望能用javascript写出以上的代码,用javascript调用java类。
-开玩笑,是不是不懂?javascript是jsp被编译输入为html到了浏览器端才执行的代码,写成上面那个样子,怎么可能调用java啊!你在页面<scirpt>标签中写上这个,真让人笑掉大牙啊!再说javascript的语法都不支持import! 好吧,javascript既然不支持这种写法,那么我退一步,我希望我的jsp将来是这样的: <%@ include file="/taglib.jsp"%>
<base:import name="com.siqisoft.dushuhui.book.controller.RecommendBookAction" />
<script>
recommendBookAction.addBook(bookName);
</script> 看起来好像有点儿晕.
那么我来解释一下: javascript不是不支持import,那么我想在页面上应该有一个和java类同名的javascript类,这个javascript类中的方法和java中的方法是一样一样的。
然后,只要调用这个javascript的类的方法,这个javascript类的方法就去找到它对应的java类在web服务器上的url,并请求。我的同事们只管像java一样使用javascript调用java方法就可以了,不用再去处理恼人的url了。javascript类的方法寻找服务器url的事情,不需要程序员去对应,只要交给base:import这个jsp标签去做就可以了。
所以,上面代码的第一行的作用是引入一个标签库,第二行使用import标签,引入了一个java类。那么这个标签到底做了什么呢?
1、遍历name参数的java类中所有方法,算出这些方法在web服务器上的url(如果这个方法真的在web服务器上有url的话)。
2、根据这些有url的java类的方法,生成一个javascript类,输出到页面。这javascript类的放和java中的方法名一样。
方法体内就是使用ajax请求远程服务器。 其中第一步算url的时候,不同的框架有不同的算法,如SpringMVC,可以从一些缓存中获得到一些url和java方法的映射关系。第二步javascript的ajax调用也可以选用不同的框架。 下面介绍一下我的做法:
不管是页面submit还是ajax数据提交,总是要提供一个url。结果我们看到,jsp或者js中到处充斥着url代码(就像当年jsp中到处充斥着java代码一样)。
有时,服务器的某功能的url地址发生改变了(比如我以前参与的一个struts1的项目,struts-config-module改变了,或者删除了),那么要到页面中找到所有写了这个url地址的页面,一个一个给修改来。如果项目涉及5000过个jsp,而有的jsp中的路径中的一部分是由变量生成出来的,那么找到这些url并修正简直是一场噩梦。当然,可以在整个工程中进行批量替换,但是由此引起的系统变化,总会给担心质量的人造成很大的心理负担。
最早的时候,我看到页面上有java代码让我感觉心理很没底,现在这个ajax横行的年代,很难让一个程序员有自制力去管理好他的url,页面上乱七八糟的url带着匪夷所思的问号及参数,更让人觉得无所适从。调试和系统维护的时候,从一个url找到这个url最终调用的java类及方法,还是要费一番脑筋和周折的。
感谢dwr的出现,事情好像有了一些转机。我有幸在一个项目中用到了dwr技术,那是三年前,dwr的版本应该比较低,需要在一个配置文件中配置dwr调用需要的java类、方法好像还有参数或者返回值的类型。然后,在页面中,还要引入两个dwr的js,一个好像是dwr-core,另外一个是和刚刚在配置文件中相关的java类的名字相同的一个js,这个js好像是动态生成的。当时我刚学习java技术,在一家公司中从事程序员,都是把别人的代码复制过来改改,没有做深入的研究。当时虽然没有明白dwr的原理,但是还是感谢dwr可以让我偷懒:我不用再配置struts-congfig-xyz.xml,不用在action里加if,else,不用在页面上写讨厌的url。只要声明一个dwr配置文件,一切都好办了。
后来,就是最近几天,我在搭建一个最小的开发框架(也就是组装网上一些开源的框架)时,意识到我又将面对那些url:项目组其它人在使用这个小开发框架开发系统时,又会在jsp上把url写的到处都是。于是我想象了一种网页请求服务器的开发方式,在jsp页面中,如果能写如下代码,就好了: import com.siqisoft.dushuhui.book.controller.RecommendBookAction;
RecommendBookAction recommendBookAction = new RecommendBookAction();
bookName = "<三国演义>";
recommendBookAction.addBook(bookName); -嗯?又在jsp页面中写java代码?
-不是的,我是希望能用javascript写出以上的代码,用javascript调用java类。
-开玩笑,是不是不懂?javascript是jsp被编译输入为html到了浏览器端才执行的代码,写成上面那个样子,怎么可能调用java啊!你在页面<scirpt>标签中写上这个,真让人笑掉大牙啊!再说javascript的语法都不支持import! 好吧,javascript既然不支持这种写法,那么我退一步,我希望我的jsp将来是这样的: <%@ include file="/taglib.jsp"%>
<base:import name="com.siqisoft.dushuhui.book.controller.RecommendBookAction" />
<script>
recommendBookAction.addBook(bookName);
</script> 看起来好像有点儿晕.
那么我来解释一下: javascript不是不支持import,那么我想在页面上应该有一个和java类同名的javascript类,这个javascript类中的方法和java中的方法是一样一样的。
然后,只要调用这个javascript的类的方法,这个javascript类的方法就去找到它对应的java类在web服务器上的url,并请求。我的同事们只管像java一样使用javascript调用java方法就可以了,不用再去处理恼人的url了。javascript类的方法寻找服务器url的事情,不需要程序员去对应,只要交给base:import这个jsp标签去做就可以了。
所以,上面代码的第一行的作用是引入一个标签库,第二行使用import标签,引入了一个java类。那么这个标签到底做了什么呢?
1、遍历name参数的java类中所有方法,算出这些方法在web服务器上的url(如果这个方法真的在web服务器上有url的话)。
2、根据这些有url的java类的方法,生成一个javascript类,输出到页面。这javascript类的放和java中的方法名一样。
方法体内就是使用ajax请求远程服务器。 其中第一步算url的时候,不同的框架有不同的算法,如SpringMVC,可以从一些缓存中获得到一些url和java方法的映射关系。第二步javascript的ajax调用也可以选用不同的框架。 下面介绍一下我的做法:
解决方案 »
- dwr问题
- 搜索中下拉菜单分百度与本站
- 求一条联合sql语句.谢谢大家.
- 用eclipse开发mapxtreme for java
- 在线急等 jsp找不到自己的jar包
- 急!!!!两个问题:servlet中得不到session值;session.getValue与session.getAttribute的区别
- 帮帮我 jsp中的包的问题 急
- JExcelAPI哪里有的下载?/??谢谢 或者帮我发一个,
- jsp新手入门,高分请高手指点……
- who can help me?去掉浏览器里的地址!
- request.getParamegter("");得到值以后中文乱码怎么解决?
- OpenLayers里面如何根据你在文本框中输入的一个地图名字然后在地图上显示出来
我使用的是springMVC框架,同样因为不喜欢url的原因,我在controller类中使用@ResultMapping注解的时候,从来不加参数(我猜这样做可能不安全),但是如果这样的话,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping无法帮我映射controller的方法到url。于是我复制了DefaultAnnotationHandlerMapping的源代码,改了改形成了自己映射风格: package com.siqisoft.core.framework.spring.framework;import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping;import com.siqisoft.core.framework.spring.framework.events.ClassAndUrlMappedEvent;public class AnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping { private String subKeyWord = ""; private String suffix = ""; public void setSubKeyWord(String subKeyWord) {
this.subKeyWord = subKeyWord;
} public void setSuffix(String suffix) {
this.suffix = suffix;
} @Override
protected String[] determineUrlsForHandler(String beanName) {
ApplicationContext context = getApplicationContext();
Class<?> handlerType = context.getType(beanName);
if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
return determineUrlsForHandlerMethods(handlerType);
} else {
return null;
}
} /**
* 根据传入的类,遍历这个类的所有方法. 如果方法上标记了RequestMapping注释
* 并且RequestMapping没有value参数(这个并且是为了兼容springMvc的默认模式)那么采取这样的映射方式:
* 类的全路径名先将'.'换为'/',然后去掉成员变量subKeyWord中包含的以';'分隔的关键字,最后+方法名.do
*
* @param handlerType
* @return
*/
protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) {
final ApplicationContext objApplicationContext = this.getApplicationContext();
final Set<String> urls = new LinkedHashSet<String>();
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (mapping != null) {
String[] mappedPatterns = mapping.value();
if (mappedPatterns.length == 0) {
String className = method.getDeclaringClass().getName();
if (className.indexOf('$') == -1) {// 过滤掉cglib自动生成的类型
// 处理url
String urlPath = className;
urlPath = urlPath.replace('.', '/');
String[] subKeyWords = subKeyWord.split(";");
for (int i = 0; i < subKeyWords.length; i++) {
urlPath = urlPath.replaceFirst(subKeyWords[i], "");
}
String methdName = method.getName();
urlPath += "/" + methdName + suffix;
urls.add(urlPath);
// 发布事件
ClassAndUrlMappedEvent classAndUrlMappedEvent = new ClassAndUrlMappedEvent(className);
classAndUrlMappedEvent.setMothodName(methdName);
classAndUrlMappedEvent.setUrl(urlPath);
objApplicationContext.publishEvent(classAndUrlMappedEvent);
}
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
// 准备事件
return StringUtils.toStringArray(urls);
}
} 我每次发现一个类的方法到url的映射的时候,我都发一个事件告诉我的一个缓存,这个缓冲同学就根据这个时间,准备javascript类。下面是缓冲器的代码:(写的很纠结,因为我在url映射器中发出的事件有些不伦不类。)
package com.siqisoft.core.framework.jswork;import java.util.HashMap;
import java.util.Map;import org.apache.log4j.Logger;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;import com.siqisoft.core.framework.spring.framework.events.ClassAndUrlMappedEvent;@Component
public class ImportGenerate implements ApplicationListener<ClassAndUrlMappedEvent> { private static Map<String, String> cachedJavaScriptClass = new HashMap<String, String>(); private static String VAR_NAME = "${varName}"; protected Logger logger = Logger.getLogger(this.getClass()); public static String getJavaScritptClass(String className, String varName) {
String strJavaScript = cachedJavaScriptClass.get(className);
if (strJavaScript != null) {
strJavaScript = strJavaScript.replace(VAR_NAME, varName);
} else {
strJavaScript = " <!-- 找不到类:" + className + " 对应的URL,所以无法为其生成javascript类 -->";
}
return strJavaScript == null ? "" : strJavaScript;
} public void onApplicationEvent(ClassAndUrlMappedEvent event) {
String className = event.getClassName();
String method = event.getMothodName();
String url = event.getUrl();
compileClass(className, method, url);
} /**
* 编译javascript类中的方法
*
* @param methodName
* @param url
* @return
*/
private String compileMethod(String methodName, String url) {
String result = " "+methodName + ":function(callBack,extraData,formId){\r\n";
result += " callRemoteJava('" + url + "',callBack,extraData,formId,onFailed);\r\n";
result += " }\r\n";
return result;
} /**
* 编译javascript类
*
* @param className
* @param varName
* @return
*/
private void compileClass(String className, String methodName, String url) {
String strCode = cachedJavaScriptClass.get(className);
if (strCode != null) {
strCode = strCode.replace(" };\r\n", " ,\r\n");
strCode = strCode.replace(" </script>\r\n", "");
} else {
strCode = "<script type=\"text/javascript\" language=\"javascript\">\r\n";
strCode += " //类" + className + "对应的javascript类\r\n";
strCode += " var " + VAR_NAME + " = {\r\n";
} strCode += " " + compileMethod(methodName, url); strCode += " };\r\n";
strCode += " </script>\r\n";
cachedJavaScriptClass.put(className, strCode);
}}
好了,我现在已经把所有类的方法和url的映射关系存储到ImportGenerate类的cachedJavaScriptClass成员变量中了。我相信springMVC铁定是有缓存的,但是我没有使用它的缓存。原因很简单:因为我找不到它缓存在什么地方。下面是接轨的impot标签:
package com.siqisoft.core.framework.jswork;import java.io.IOException;import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;public class ImportTag extends TagSupport { private String name; private String var; public int doStartTag() throws JspTagException {
JspWriter out = pageContext.getOut();
try {
if (var == null || "".equals(var.trim())) {
var = name.substring(name.lastIndexOf('.') + 1);
}
out.print(ImportGenerate.getJavaScritptClass(name, var));
} catch (IOException ex) {
throw new JspTagException("IOError:" + ex.getMessage());
}
return Tag.SKIP_BODY;
} public int doEndTag() throws JspTagException {
this.name="";
this.var="";
return Tag.EVAL_PAGE;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getVar() {
return var;
} public void setVar(String var) {
this.var = var;
}}为了防止同名不同路径的类在同一个jsp中被引入的极端情况出现,import标签支持var属性,自定义javascript类的名字。第一步就说完了,如果有如下类:package com.siqisoft.dushuhui.book.controller;import javax.servlet.http.HttpServletRequest;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import com.siqisoft.dushuhui.book.model.Book;
import com.siqisoft.dushuhui.book.model.ReadHistory;
import com.siqisoft.dushuhui.book.model.Section;
import com.siqisoft.dushuhui.book.service.BookService;
import com.siqisoft.dushuhui.book.service.SectionService;@Controller
public class ReadBookAction { @Autowired
private BookService bookService; @Autowired
private SectionService sectionService; public void setBookService(BookService bookService) {
this.bookService = bookService;
} @RequestMapping
public ModelAndView readBook(ReadHistory readHistory, HttpServletRequest request) throws Exception {
int iBookId = readHistory.getBookId();
Book book = bookService.readBook(iBookId);
ModelAndView mv = new ModelAndView();
mv.addObject(book);
mv.addObject(readHistory);
return mv;
} @RequestMapping
public ModelAndView readNextSection(Section section, HttpServletRequest request) throws Exception {
sectionService.loadSection(section);
ModelAndView mv = new ModelAndView();
mv.addObject(section);
return mv;
}}
那么在jsp页面上可以这样写:<base:import name="com.siqisoft.dushuhui.book.controller.ReadBookAction" />最后生成的HTML代码是这样的: <script type="text/javascript" language="javascript">
//类com.siqisoft.dushuhui.book.controller.ReadBookAction对应的javascript类
var ReadBookAction = {
readBook:function(callBack,extraData,formId){
callRemoteJava('/dushuhui/book/ReadBookAction/readBook.do',callBack,extraData,formId,onFailed);
}
,
readNextSection:function(callBack,extraData,formId){
callRemoteJava('/dushuhui/book/ReadBookAction/readNextSection.do',callBack,extraData,formId,onFailed);
}
};
</script>至于 readBook、readNextSection中调用的是什么飞机,我们下帖再说!
【为什么这么非主流,为什么不是struts2+jquery?这和有的人喜欢吃榴莲有的人不喜欢吃是一样的。从技术选型角度讲,列举一大推这个的好处,那个的坏处,都是纸上谈兵。所以,总结的出来三个字:我喜欢。】页面javascript调用java,就好像COM技术一样:接口协议+数据类型。上贴我们说了接口协议,就是一个简单的封装,下面来说说数据类型,更具体的说:数据怎么在java和javascript间传递:
1、传参:springMVC的controller中的方法,第一个参数就是model,这个model是springMVC在request中自己拼出的,所以,javascript给java传参,只要把当前页面的form提交给后台就可以了。
2、返回值:那么java给javascript返回值,怎么办?只有回调函数一种办法。不如在调用的时候指定:
ReadBookAction.readBook(function(result){
alert(result);
});
这个result就是java的返回值。从宏观上看,无论是java返回了json、xml、html,都算java的返回值,在这里都能接收到,建议使用json,不使用html,这个完全拆开前后台,说不定哪天可以用现有的服务器代码,前端给换成了EclipseRCP呢(意淫一下)。 参考上贴生成的HTML中的js代码,简单说一下我的处理方式:
如果有form中不包含的参数,放到extraData中,如果页面有多个form,使用formId指定参数,如果服务出错了,要自定义错误处理,那么传进去onFailed进行处理。 下面贴一下使用mootools实现的callRemoteJava方法:
//用javascript调用java的方法
function callRemoteJava(url,onSuccess,extraData,formId,onFailed){
//获得到Form
var form = getForm(formId);
//获得到额外的数据
var tempFields = null;
if(extraData!=undefined){
tempFields = setDataToForm(form,extraData);
}
//注册回调事件
var request = form.get('send');
//成功
if(onSuccess!=undefined&&onSuccess!=null){
request.addEvent('success',onSuccess);
}else{
request.addEvent('success',function(){});
}
//失败
if(onFailed!=undefined&&onFailed!=null){
request.addEvent('failure',onFailed);
}else{
request.addEvent('failure',callJavaFailed);
}
//向后台请求数据
form.send(url);
//清除掉临时域
if(tempFields != null){
for(var i =0 ; i< tempFields.length;i++){
tempFields[i].destroy();
}
}
}//获得表单对象
function getForm(formId){
var form = $(formId);
if(form==null){
//寻找document内的第一个Form元素,如果找不到到,就自己构造一个
var forms = $$("form");
if(forms.length>0){
form = forms[0];
}else{
form = new Element("form");
form.set('name','tempForm');
form.inject($(document.body),'top');
}
}
return form;
}//获得数据对象
function setDataToForm(form,extraData){
//判断Form中是否有data中的值,没有的话,增加到Form.有的话,meger掉Form中的值
var tempFields = new Array();
if("array"!=typeOf(extraData)){
alert("调用java类时,非Form内的额外参数必须使用Array对象传递,否则这些数据将不会被处理");
}else{
//遍历额外的数据
for(prop in extraData){
if("string"==typeOf(extraData[prop])){
var field = form.getElement('[name='+prop+']');
//如果Form中不包含此域,创建一个临时的Field域
if(field==null){
field = new Element('input',{'type':'hidden','name':prop});
field.inject(form,'top');
tempFields.push(field);
}
//如果Form中包含此域,用extraData的值覆盖Field的值
field.set('value',extraData[prop]);
}
}
}
return tempFields;
}//请求出错是调用的函数
function callJavaFailed(instance){
alert('操作失败!');
}
快吃饭了,此贴完!
如有意交流、鄙视可以回帖。
对ajax两端的入口进行了简单的封装。