Web

SpringMVC

August 2, 2017
Java, Spring, Web, MVC

本文是我的学习过程,但是我认为学习过程中并不适合写给别人看。所以本文只是一个临时的笔记,待以后进行深入整合。 SpringMVC与Spring的关系是什么? SpringMVC是依托Spring框架开发的一个Web MVC框架,天生与Spring集成,也就是使用了Spring的依赖注入和切面编程。 上一篇中讲解了Web.xml,这是Java Web的基础,也就是Servlet容器的工作方式,那个也作为本文的基础。 SpringMVC整体可以看成是一个Servlet,事实上它就是一个Servlet。我们配置web.xml来启动这个Servlet,然后这个Servlet来负责处理每个请求。每个处理请求的类都是我们遵照SpringMVC的格式来写的类,由SpringMVC这个框架来调用完成整个请求的处理。 与原本粗放的原生Java Web的开发模式相比,做了一个聚合。原本是每个请求你都写一个Servlet去处理,在web.xml中配置好一个请求与处理类的映射关系,由容器来完成调用。然后SpringMVC是分担了容器的压力,将容器的工作交给他来做,它利用Spring框架的依赖注入的特性,以及bean的工厂模式重新实现后端的实际处理流程。 有好处也有坏处,我也不太清楚这样做的具体目的是什么,可能会有性能上的提升,因为毕竟工厂模式等特性的加入会提升一些性能和开发便利。坏处感觉也挺明显,首当其冲就是学习栈又深了,还不是深了一个单位是好几个单位。加入了新的配置等,开发过程的脑力成本又增加了。 多说也没用,也是要以后逐步深入才能参透其中的奥妙。 maven 配置项目 # 命令行使用mvn构建项目很简单。 mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0 -DgroupId=com.demo -DartifactId=demo -Dversion=1.0 生成的目录如下: . ├── pom.xml └── src └── main ├── resources └── webapp ├── index.jsp └── WEB-INF └── web.xml 配置一下项目所需依赖关系pom.xml: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>demo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>demo Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <jdk.version>1.8</jdk.version> <spring.version>4.3.9.RELEASE</spring.version> <jstl.version>1.2</jstl.version> <servletapi.version>3.1.0</servletapi.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org. ...

web.xml

August 1, 2017
Java, Web, Tomcat

其作用就是配置一个web程序的基本功能。容器在启动一个web项目的时候先读取这个配置文件,在配置文件读取无误后开始按照配置启动剩余的服务。 加载顺序 # <context-param> -> <listener> -> <filter> -> <servlet> 容器会创建一个ServletContext,整个项目都会共用这个上下文。而<context-param>的内容以键值对的形式存在这个上下文中。 然后对读取<listener>,并实例化listener类,并初始化。 listener类中会有contextInitialized(ServletContextEvent args)初始化方法,启动Web应用时,系统调用Listener的该方法,在这个方法中获得: ServletContext application = ServletContextEvent.getServletContext(); context-param的值 = application.getInitParameter("context-param的键"); 得到这个context-param的值之后,你就可以做一些操作了。 举例:你可能想在项目启动之前就打开数据库,那么这里就可以在<context-param>中设置数据库的连接方式(驱动、url、user、password),在监听类中初始化数据库的连接。这个监听是自己写的一个类,除了初始化方法,它还有销毁方法,用于关闭应用前释放资源。比如:说数据库连接的关闭,此时,调用contextDestroyed(ServletContextEvent args),关闭Web应用时,系统调用Listener的该方法。 所以归根结底,这个<context-param>就是用来存一些配置数据的地方。 比如:定义一个管理员email地址用来从程序发送错误,或者与你整个应用程序有关的其他设置。使用自己定义的设置文件需要额外的代码和管理;直接在你的程序中使用硬编码(Hard-coding)参数值会给你之后修改程序带来麻烦,更困难的是,要根据不同的部署使用不同的设置;通过这种办法,可以让其他开发人员更容易找到相关的参数,因为它是一个用于设置这种参数的标准位置。 web-app # 根元素,不用多说 display-name # 应用的名称,这个在tomcat的维护信息中会出现。 session # <session-config> <session-timeout>120</session-timeout> </session-config> listener # <!--****************************监听器配置*********************************--> <!-- Spring的log4j监听器 --> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 监听器用来监听各种事件,比如:application和session事件,所有的监听器按照相同的方式定义,功能取决去它们各自实现的接口,常用的Web事件接口有如下几个: ServletContextListener:用于监听Web应用的启动和关闭; ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变; ServletRequestListener:用于监听用户的请求; ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变; HttpSessionListener:用于监听用户session的开始和结束; HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变。 listener类是实现了以下两个接口中任何一个接口的简单java类:javax.servlet.ServletContextListener或javax.servlet.http.HttpSessionListener,如果想让你的类监听应用的启动和停止事件,你就得实现ServletContextListener接口;想让你的类去监听Session的创建和失效事件,那你就得实现HttpSessionListener接口。 有两种为应用配置listener的方式: 使用@WebListener修饰Listener实现类即可。 在web.xml文档中使用<listener>进行配置。 在下一节配置spring的加载类的时候会给出例子。 spring相关配置 # 配置spring,必须需要<listener>,<context-param>如果有就去加载配置的路径,没有就默认是/WEB-INF/applicationContext.xml。 <!-- spring config --> <!-- if this node is not exist, --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-configuration/*. ...

angular

July 31, 2017
Web, Front

Angularjs 和Angular2 的区别,就像雷锋和雷峰塔的区别。 笑尿了。 我是跟着它的官方文档学习的,文档还是比较健全的,也很细致。地址:[Quick Start](npm install -g @angular/cli) AngularJS 与 Angular2 # 关于二者的区别,可以肯定的是angular2一定更好。 在细节上,先来说一下angular的特别之处。 其诞生于09年,提出了双向绑定等新特性的概念使得它一出现就被追捧。然而由于09年移动端还没有大规模出现,所以其不支持移动应用的使用。并且其数据绑定使用的是扫描的方式,性能瓶颈很难突破,所以被更适应时代的angular2代替是历史的必然。 所以angular2就拥有了这些它的小弟做的不好的特性。 性能快,支持移动应用开发等。其实现的方式也有很大的区别,所以就出现了雷锋和雷锋塔的区别。 直接来学习angular2的使用 # 首先在什么都不懂的时候先来构建一个实例应用打个招呼: 以下的部分需要:node 6.9.x and npm 3.x.x 及以上。这部分可以去看我的node相关的文章。 Step 1. Install the Angular CLI globally. npm install -g @angular/cli Step 2. Create a new project ng new my-app Step 3: Serve the application cd my-app ng serve --open 你可以看到效果,现在我们来看文件目录。 你的应用需要的内容都在src文件夹中。其他的文件都是用来支撑这个应用的框架代码。下面我们看一下src文件夹的结构: src # File Purpose app/app.component.{ts,html,css,spec.ts} 定义组件。HTML模板,CSS,Unit Test。这是一个root组件 app/app.module.ts 声明应用如何装配 assets/ 静态资源 environments/ 存放你应用在不同环境下的配置文件。例如在开发环境还是在发布环境 index. ...

react 初探-一些基础概念

July 31, 2017
Web, Javascript

(这一篇写的很杂,因为自己也是没有章法的断断续续的看了react的东西,所以内容没有组织好。勉强有个认知就好了) 前端的技术现在发展的其实相对简单了很多,但是我觉得这才是正常的前端,以前的前端发展的太混乱,光是屏幕适配的问题就要搞出很多的工作量。而且以前的前后端的耦合过大,使得前端想发展也不发展不动,不过设计总是在进步,异步请求的普适催生了新的前后端设计模式,使得前后端完全解耦成为可能,前端的事情在前端搞定,后台的事情在后台封装掉。前端发展到今天主要就是接下来我要学习的几个js的框架,利用js实现动态的数据绑定,以及异步的请求,所有的前端操作都在封装在前端,开发的脑智压力开始下降,所以现在的全栈也并不是太难。 这样前端框架会用的人一般都比较多,因为学习栈比较浅,很多人自学一天就可以了。所以相对的资料也就非常完善,网上的学习教程编写教学例子也很方便,所以在这里我主要就是偏理论一些的认知,其背后的设计实现等。 简介 # React是Facebook出品的一款开源框架,虽说是开源,但是其在标准的BSD协议后添加了一些条款,使得现在很多公司并不敢使用这个框架了,其添加条款中说明,如果使用该开源项目的产品威胁到facebook的商业产品,facebook有权起诉该产品。不过没有并那么可怕,因为貌似想威胁到也不太容易。 React这类框架主要的注意力放在的是用户的交互界面上,最突出的就是内容的动态绑定,内容变化后的高效刷新。并且屏蔽了DOM。 React使用的是JSX语法,是一种将js与html混合编写的语言。 Javascript # 在学习使用这个框架之前,必须对Javascript有所了解,因为这是一个Javascript框架。对于这部分了解的就直接跳过去看JSX的语法部分。 这门语言传说是被人误解最深的语言,确实貌似大家的学习路线都没有关注过这个东西,以为就是前端脚本搞着玩的语言,但是其语言还是有很多优点并且应用的场景正越来越多。 JavaScript 是一种面向对象的动态语言,它的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。需要注意的一个主要区别是 JavaScript 不支持类,类这一概念在 JavaScript 通过对象原型(object prototype)得到延续。另一个主要区别是 JavaScript 中的函数也是对象,JavaScript 允许函数在包含可执行代码的同时,能像其他对象一样被传递。 类型 # Number(数字) String(字符串) Boolean(布尔) Symbol(符号)(第六版新增) Object(对象) Function(函数) Array(数组) Date(日期) RegExp(正则表达式) Null(空) Undefined(未定义) 等 更多基础的语法不冗余重复了。 Javascript对象 # 如果你知道Json的全称,你就理解了这个东西:JSON(JavaScript Object Notation, JS 对象标记)。一个符合Json协议的字符串可以直接解析为Javascript对象。 对于Object的成员访问可以使用链式访问: var obj = { name: "Carrot", "for": "Max", details: { color: "orange", size: 12 } } obj.details.color; // orange obj["details"]["size"]; // 12 下面的例子创建了一个对象原型,Person,和这个原型的实例,You。 ...

CGI 通用网关协议

July 29, 2017
Web

这是实现Web服务的最基础程序。目前只是看到了一些比较,但是我还没有时间去Read the fucking source code. 这样的程序算是计算机网络中比较有学习意义的代码。 在此简单的介绍一下先。 tcp通信过后,服务端需要有一个程序做为衔接程序,来将请求进一步转发给逻辑服务程序,并将结果返回给浏览器,做了个信使的角色。

Servlet

July 28, 2017
Java, Web, Servlet

这里有一些概念非常的恶心,一开始的时候不太好理解是什么。通过一段时间的实践,我逐渐理解,所以直到今天才开始写这一篇。 容器Container # 这个概念从我一开始接触Java就让我很困惑。 首先你需要先去看一下我在后面的一篇关于tomcat的配置文件:server.xml。在那里了解一下配置文件的结构,你可以看懂下面的这张图片。 首先,容器这个东西是针对servlet来说的,也就是我们说的容器都是放置和运行servlet的程序。tomcat是个容器。但是上面的每一级都算是一种容器,只是大小不同。 为什么要有容器 # 其实对于计算机软件体系来说,一切的东西的出现都是因为标准和规定。 所谓容器其实就是一个程序,这个程序按照servlet需要的标准实现了一些接口,使得当你写了一个servlet以后可以成功的放在这个程序中运行,再直白一点就是,你把你的servlet程序交给这个容器,容器可以成功的运行你的程序。 他们之间通过约定一些标志物信息来实现无缝对接。 所以学习这写东西就是在学习了解他们之间约定的事物,以及,为什么这么约定,这样的约定有什么好处和坏处。 容器是一段程序吗? # 在tomcat中,并没有容器这个东西,所以容器并不是一个东西,就像水果并不是一个具体的苹果一样。容器是一个抽象的概念。 在tomcat中,如上图,有很多的层。这些层就是一个容器结构。他们一起构成了一个完整的容器。 Engine 是一段基础的引擎程序。我认为是整个tomcat的基础,所有的其他容器都在其的调度控制之下。 Host 这是一个逻辑概念,但也有对应的实体。在讲tomcat的配置文件servers.xml 的时候,有提到这个host是什么回事。有了这一层,可以在一个tomcat容器中部署不同的项目,这些项目的域名都是不同的。也就是这个容器可以容纳很多的servlet并且这些servlet可以是几种粒度的。 你可以是完全不同的两个网站,域名都不同,放在一个容器中运行。你也可以是一个网站的不同组件,放在不同的Context内运行。 所以容器是个体系,不是一个东西。 其实按照这个思路,一切皆容器。因为一切的这些都是调度管理,操作系统调度进程,进程调度线程。一切都是接口和约定。所以万物皆容器。 什么是servlet # 这是一个标准,你的程序符合这个标准就是一个servlet程序。 一个类其实就是一个servlet,但是很多个类在一起只要有一个servlet的接口,也是一个servlet。 (在此我们不再讨论tomcat的启动过程,我们更关心一个servlet放入tonmcat容器以后的过程) 来看一下这个图片,这就是servlet主要关联的几个类。 **摘抄:** 从上图可以看出 Servlet 规范就是基于这几个类运转的,与 Servlet 主动关联的是三个类,分别是 ServletConfig、ServletRequest 和 ServletResponse。这三个类都是通过容器传递给 Servlet 的,其中 ServletConfig 是在 Servlet 初始化时就传给 Servlet 了,而后两个是在请求达到时调用 Servlet 时传递过来的。 我们很清楚 ServletRequest 和 ServletResponse 在 Servlet 运行的意义,但是 ServletConfig 和 ServletContext 对 Servlet 有何价值? 仔细查看 ServletConfig 接口中声明的方法发现,这些方法都是为了获取这个 Servlet 的一些配置属性,而这些配置属性可能在 Servlet 运行时被用到。 而 ServletContext 又是干什么的呢? Servlet 的运行模式是一个典型的“握手型的交互式”运行模式。所谓“握手型的交互式”就是两个模块为了交换数据通常都会准备一个交易场景,这个场景一直跟随个这个交易过程直到这个交易完成为止。这个交易场景的初始化是根据这次交易对象指定的参数来定制的,这些指定参数通常就会是一个配置类。所以对号入座,交易场景就由 ServletContext 来描述,而定制的参数集合就由 ServletConfig 来描述。而 ServletRequest 和 ServletResponse 就是要交互的具体对象了,它们通常都是作为运输工具来传递交互结果。 ...

chrome 控制台

July 16, 2017
Web, Http, Console

chrome是一个非常好用的Web开发的工具,而不仅仅是个好用的浏览器。 按下就可以调出控制台,控制台可以与页面分离成为一个单独的窗口。这个在调试大屏效果的时候很方便,但是我不是搞前端设计的,所以不管这个。 这里可以看到有很多选项,常用的有以下几个:Elements、Console、Sources、Network。 Elements 审查元素 # 这里可以看到你的HTML源码,和对应的css。在前端工程师的工作中经常会用到,不过现在自动化的工具越来越多了,也不一定非用这个了,但是这个一定是很强大的功能。 在这个框的最左上角有一个鼠标箭头的标志,点一下就可以到页面上定位某个元素的代码,很好用。 这一块没有什么技术含量 Console 控制台 # 这是一个命令交互的控制台,语言是Javascript。下面就介绍一下常用的几个命令。 console.log() # 这是一组四个命令分别是:console.log()、console.info()、console.error()、console.warn()。其作用就是在控制台中打印信息,在编写前端工程的时候可以这样在js代码中打印log信息来排查问题。 $.cookieStorage.set(‘id’, 1234) # 这是一个cookie操作函数。可以对应的设置cookie的参数值,如上,如果某个网站的用户登录验证使用cookie来做的,你又猜到了其参数的名字,就直接设置cookie就可以登录上去了,免密码呢。 Sources # 这个在调试的时候可以看到自己的页面的每个资源的加载情况,包括时间、来源、是否加载成功等,也可以把资源down下来。 NetWork # 这个我感觉是最好用的一个,他会记录你的异步请求。可以记录请求的地址,参数,返回值等等。 Anything more? Nothing more…

tomcat

July 12, 2017
Web, Java, Tomcat

日志 # Tomcat的日志系统还算很完善的。来看一下,当你的应用出现问题的时候要去哪里找原因吧。 apache-tomcat/logs/ |_ catalina.2017-07-12.log |_ catalina.out |_ host-manager.2017-07-12.log |_ localhost.2017-07-12.log |_ localhost_access_log.2017-07-12.txt |_ manager.2017-07-10.log |_ 对应的App应用的log文件夹,取决与你项目用的日志工具。 catalina.out 这里的数据都是代码输出到标准输出流的重定向,所以你的SOUT都在这里。 localhost.DATE.log 是你的应用抛出的错误,如页面运行错误,servlet错误等,常用查错工具。 其实目前我也就用过以上两种日志,可能用法还是不太对,入门的时候查日志感觉日志乱的要死。其实这个也不影响使用就是很影响心情,找错误一般可以直接搜索字符串定位就好了。但是我自己感觉还是要在项目开发之前就做好日志的规划工作,什么时候要打log什么时候不打,log的格式是怎样的。心情必须照顾! tomcat 安装到发布应用 # 这一部分非常简单。 安装 # 下载tomcat-core代码包,并解压。 到其bin/目录下,linux执行./startup.sh, windows执行startup.bat脚本。 前提是需要配置JDK到环境变量。 打开http://IP:8080 查看效果。 发布应用 # 将你的应用编译打包成一个war包,然后copy到其webapps文件夹下,通过http://IP:8080/NameOfWar访问。 如何开发Java Web?你需要了解:servlet、SpringMVC、maven、javaBean等。 tomcat 源码梳理 # 打算看一下源码是怎么玩的,也有可能看不下去,坑先挖上…

rest_framework

July 9, 2017
Django, Web, API

这是一款Django下的API开发框架,对于业务逻辑较简单的API来说非常方便,开发速度非常快。 此坑待填…

Django

July 6, 2017
Python, Django, Web

(注:本文写在N天之中,随着对其的深入学习,很多的看法会发生变化。我也尽量把东西放在这一篇中解决掉,文章思路会有些跳跃,可能先上了细节后面再归档等等。另外,本文会屏蔽一些技术细节,用一些术语来表达,这些术语均是较容易学习的知识块。) 这段时间神奇的技术走位,Java与Python的爱恨交织。 所以既然写到了肯定就是用到了呗,拿到了一个Django的项目,改写成Java的,玩的就是这么欢脱,做为一个写C++出身的入门级Java工程师生活已经如此艰难,然后发现现实是还要兼职Python工程师。 Django也算是工程师玩的玩意儿?封装成了这个样子还有什么好玩的,估计随随便便学2个小时语法加一点Django的框架设计就可以玩起来的,我只能说写这项目的哥们太会偷懒了,服气,骗完钱就离职,更是任性的不行。 言归正传!框架这种东西嘛,就是为了简化开发,提高速度效率,降低合作成本用的,所以django的设计也算是符合了这个目的。但是他并不是简单的照搬了JavaWeb框架的MVC设计思路,而是他妈的又提出了一个神奇的MTC,不用想也知道,就在人家的基础上变来变去还自以为参透了宇宙的奥秘。所谓的MTV就是把几个分工重新分了一下,对,就是要跟人家不一样,日你哥! 不过话回来,就算是对于MVC这个业界都普遍遵循的设计模式,也有很多不同的理解,比如业务逻辑到底该Controller来做还是Model来做,甚至还有人要把这个交给view来做的。之前貌似还看到了个文章讲什么官方设定,话说都发展到这个地步了谁还管你官方设定。不过呢,这都是个设计思路,不同的程序员当然有自己的理解,自己写项目的时候怎么搞都行,但是现在这框架数量真的有点爆炸了,所以大家还是在设计的时候多用点心吧,实现框架的技术不难,但是设计是艺术。 所谓的MTV分别就是: Model、Templete、View。熟悉就对了,你肯定一眼就看懂了,我也是一瞬间就明白了。但是很抱歉,仔细想一下这个Templete如果是前端模板的话,view是啥?如果view用来组装前端模板的话,要model来做业务逻辑吗?那controller岂不是没有了?……就是这么神奇,人家把业务逻辑交给了view来做,我特么也是佩服的吐血。Model就纯纯的数据库操作,就像是一个sql的python版。至于Templete,我已经没有耐心再跟他浪费时间了,老子一个写接口的要什么前端! 对于理解一个框架来说什么最重要?我在很久以前初学框架的时候一脸懵比。我草,这么多代码我得看到什么年?怎么连个主函数都没有,日了狗。。。好在我当时看了一会php,这个比较简单,不用编译一下就能懂,后来我从别人的文章中看到了学习框架的几个步骤等,才知道,看懂路由,就懂了这个框架的一半。 路由 # 提到这个路由,Python这个框架还是让我吃了一惊的。因为这玩意儿不需要容器,厉害啊。写过其他Web的都知道,我们的Web程序是要在一个Web服务程序的组织下做应答的。但是Django抛弃了这个服务程序,严格来说是Python的Web Server抛弃了其他的服务程序,自己写了一个简单的网关程序–wsgi。其实作用就是做个协议转换,链接机器与python程序,替代了apache2的一些基础功能。不过据说性能欠佳,做做测试还行,上线还是要靠Nginx的帮助,搞个uwsgi。 底层的东西,可以不懂,但是懂一定没有坏处,但是我还没时间搞懂。下面讲一下上层路由。 一个Django的项目特点就是目录比较清晰,比如这个urls.py就是存放路由信息的文件。django在自动化这个地方并没有做太多东西,对于路由信息要显示的写在这里。for一个zample. from django.conf.urls import url, include from django.contrib import admin import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^docs/', include('rest_framework_docs.urls')), url(r'^er/', views.ERView), # ui view url(r'^ui/Node', ui_views.node_view), url(r'^ui/Cluster', ui_views.cluster_view), url(r'^ui/InstanceGroup', ui_views.instance_group_view), url(r'^ui/Chart', ui_views.chart_view), url(r'^ui.*', ui_views.home_view), # API url(r'^v1/', include('rest_api.urls')), url(r'^v2/', include('rest_api.urls_v2')), #url(r'^report/', include('report.urls')), url(r'^.*', include('rest_framework_docs.urls')), ] 路由中可以预留参数位置,可以写正则表达式,论灵活性也还行。看到有些东西后买你跟的不是一个类,而是一个import语句,这是将路由转移到这个新的应用中去了,所以要去这个应用下再找对应的文件去继续路由,直到找到某个类为止。上面这个程序中其实是用到了一个叫rest_framwork的东西,这个玩意貌似是用来写网页文档用的东西,不过这个不是重点。 既然找到了这个类,那么有个经典的问题又来了。去调用那个函数啊?这就涉及到了django路由的两种方式,一种是定义到函数,另一种是调用到类。一把小项目就自己定义到函数就好了。但是当项目比较大的时候就需要将路由定义到类的身上,注意这里我们说的都是在views.py 中的类。 对于基类View,其暴露了一个as_view()的函数,该函数会对请求进一步做分发。下面两个就是这种方式路由的。 url(r'^Unit/(?P<pk>\w+)', UnitDetailView.as_view(), name='UnitDetail'), url(r'^Unit', UnitView.as_view(), name='Unit'), as_view() 是如何做分发的呢?它会调用一个dispatch函数对请求方式做个路由出去再调用到你真正的处理函数。总是很麻烦,还没来得及看代码。 不过这里有一个简化的另外一个类,继承的时候继承Generic. ...