The servlet, as a web specification, is itself a web development framework, but its implementation of Web action (in response to a URI implementation) is class-based, not very convenient, and the previous version of 3.0 must also add a new action through the Web. XML configuration. There is a filter function in the servlet, and the ability to configure all URIs is filtered. We can implement a simple web framework based on the function of filter. In this framework, the mapping of the URI action is primarily improved, as is the configuration of the route in the play framework:
GET /hello com.codemacro.webdemo.test.TestController.helloGET /route Com.codemacro.webdemo.test.TestController.routePOST /hello Com.codemacro.webdemo.test.TestController.sayHello
A URI is mapped to the class interface level. The benefits of implementing a Web framework based on a servlet are simple and can be run on all Web servers that support the servlet container specification, such as Tomcat, Jetty.
The web framework demo mentioned in this article can be obtained from my GitHub: Servlet-web-framework-demo
Function
This web framework URI action part (or URI routing) is described earlier, and the action is written like this:
public class TestController extends Basecontroller { //return string public Result index () { return ok ("Hello World" ); } HTTP 404 Public Result code404 () { return status (404, ' not Found '); } Render public Result template using a JSP template () { string[] langs = new string[] {"C + +", "Java", "Python"}; Return OK (JSP ("index.jsp") . Put ("name", "Kevin") . Put ("Langs", langs) );} }
Once you have the action, the profile route
mapping URI is available:
Get/index com.codemacro.webdemo.test.testcontroller.indexget/404 com.codemacro.webdemo.test.testcontroller.code404get/index.jsp Com.codemacro.webdemo.test.TestController.template
Then configure web.xml
, add a filter:
<filter> <filter-name>MyWebFilter</filter-name> <filter-class> Com.codemacro.webdemo.myservletfilter</filter-class></filter><filter-mapping> < Filter-name>mywebfilter</filter-name> <url-pattern>/*</url-pattern></ Filter-mapping>
Finally, the war is deployed in the form of jetty to webapps
run. Think about what to look for next time Lightweight Java web framework, directly with this demo is enough. Next, let's talk about the implementation of some key parts.
Servlet Basic
In the case of servlet-based development, the introduction of the Servlet API is necessary:
<dependency> <groupId>javax.servlet</groupId> <artifactid>servlet-api</ artifactid> <version>2.5</version> <type>jar</type> <scope> Compile</scope></dependency>
The interface of the servlet filter contains:
public class Myservletfilter implements Filter { //web app is invoked once when it is started and can be used for web framework initialization of public void Init (filterconfig conf) throws Servletexception {} //satisfies the filter url-pattern, and req/res corresponds to HTTP request and response public void DoFilter ( ServletRequest req, servletresponse Res, Filterchain chain) throws IOException, servletexception {} public void Destroy () {}}
init
The interface can be used to load routes
a configuration file at startup and to establish a URI-to-action mapping.
Action Manager
ActionManager
Responsible for loading the configuration at startup routes
and establishing a URI-to-action mapping. A URI contains the HTTP method and Uri String, for example GET /index
. Now that the action is mapped to a class interface, the corresponding class and interface can be found at startup with the same Java reflection. For simplicity, each time a request for a URI is received, the object corresponding to that class is created and the mapped interface is called.
//for example: registerAction ("Com.codemacro.webdemo.test.TestController", "Index", "/index", "GET");p ublic void Registeraction (String clazname, String methodName, String uri, string method) {try {uri = "/" + AppName + uri; Load the corresponding Class class<? Extends basecontroller> clazz = (class<? extends basecontroller>) loadclass (clazname); Get the corresponding interface Method m = Clazz.getmethod (MethodName, (class<?>[]) null); The interface requirement must return Result if (M.getreturntype ()! = Result.class) {throw new RuntimeException ("action method return type M IsMatch: "+ uri"); } actionkey k = new Actionkey (URI, GetMethod (method)); Actionvalue v = new Actionvalue (Clazz, M); Logger.debug ("register action {} {} {} {}", Clazname, MethodName, Uri, method); Establish mapping Actions.put (k, v); } catch (Exception e) {throw new RuntimeException ("Registeraction failed:" + URI, E); }}
Controllers are required to derive BaseController
, so that they can take advantage of BaseController
more convenient access to request data, such as query String/cookie, and so on.
When a request is received, it is necessary to obtain the previously established mapping based on the requested HTTP method and URI string, and call it:
Public boolean Invoke (HttpServletRequest req, HttpServletResponse resp) throws IOException { String uri = req.getrequ Esturi (); String method = Req.getmethod (). toUpperCase (); try { //Get the previously established mapping, map lookup Actionvalue v = getaction (Uri, method); Create a new controller object Basecontroller ctl = (basecontroller) v.clazz.newinstance (); Ctl.init (req, resp, this); Logger.debug ("Invoke Action {}", URI); The interface on which the binding was invoked result result = (result) V.method.invoke (CTL, (object[]) null); Render result Result.render (); } catch (Exception e) { ... }}
Result rendering
The result rendering is simply rendering the result returned by the framework user as a string, written in HttpServletResponse
. This rendering process can be straightforward Object.toString
, either through the template engine rendering, or formatted as JSON.
By implementing a specific Result
class, you can extend different rendering methods, for example, the most basic Result
is to invoke the returned object toString
:
public class Result {public void render () throws IOException, servletexception { printwriter writer = response.ge Twriter (); Result is the writer.append (result.tostring ()) returned in the controller action; Writer.close (); }}
To be simple, do not introduce a third-party library, you can directly through the JSP to complete. The JSP itself is compiled into a Servlet object in the Servlet container.
public class Jspresult extends Result { ... @Override public Void Render () throws IOException, servletexception { //pass in some objects into the template for (map.entry< String, object> Entry:content.entrySet ()) { Request.setattribute (Entry.getkey (), Entry.getvalue ()); } Delegate to the JSP to complete rendering request.getrequestdispatcher (file). Forward (request, response);} }
You can use a traditional scriptlets expression in a JSP, or you can use a new El method, such as:
<%@ taglib prefix= "C" uri= "Http://java.sun.com/jsp/jstl/core"%>
If you use EL, you need to introduce<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
BasecontrollerBaseController
is a template pattern implementation that wraps a number of convenient interfaces for use by specific controllers, such as:
public class Basecontroller { //Gets the name parameter value in/index?name=kevin protected string getquerystring (string key) { return Request.getparameter (key); } protected Result Status (int code, String text) { response.setstatus (code); return new Result (response, text); } The default is HTTP protected result ok (Object obj) { return new result (response, obj); } Protected result OK (Result result) { return result; } Protected Jspresult jsp (String file) { return new Jspresult (request, response, file, actionmgr);} }
Reverse RoutingReverse routing refers to the development of the web process, in order to introduce a URL, we do not directly write the URL string, but rather write its mapped interface to make the code easier to maintain (because the URL may change as the project progresses). Also, after the Servlet app is deployed, the URL takes the name prefix of the app, for example, /web-demo/index
in /web-demo
. In a template file, for example to link to another URI, the better way is to write directly /index
.
The implementation here is ugly, or string-based, for example:
<a href= ' <route:reverse action= "Com.codemacro.webdemo.test.TestController.hello" name= "Kevin"/> ' > Index</a>
Implemented by customizing an El function reverse
. Here you need to introduce a library of JSPs:
<dependency> <groupId>javax.servlet</groupId> <artifactid>jsp-api</ artifactid> <version>2.0</version> <optional>true</optional></dependency >
First, to SimpleTagSupport
support ?name=kevin
This dynamic parameter, you also need to implements DynamicAttributes
:
public class Jsproutetag extends Simpletagsupport implements Dynamicattributes { @Override //output final URL public void Dotag () throws IOException { Jspcontext context = Getjspcontext (); ActionManager actionmgr = (actionmanager) context.findattribute (action_mgr); JspWriter out = Context.getout (); String URI = actionmgr.getreverseaction (action, attrmap); Out.println (URI); } @Override //name= "Kevin" when calling public void Setdynamicattribute (string uri, string name, Object value) throws Jsp Exception { attrmap.put (name, value); } ' action= ' xxx ' will call ' setaction ' public void Setaction (String action) { this.action = action; }}
In order to access ActionManager
, here is achieved by writing to Request context
, rather hack.
Public Jspresult (HttpServletRequest req, HttpServletResponse resp, String file, ActionManager actionmgr) { Super (RESP, null);. Put (Jsproutetag.action_mgr, actionmgr);}
The second step is to add a file that describes the new tag WEB-INF/route_tag.tld
:
<taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion> <shortname>URLRouteTags</shortname> <uri>/myweb-router</uri> <info> </info> <tag> <name>reverse</name> <tagclass> com.codemacro.webdemo.result.jsproutetag</tagclass> <bodycontent></bodycontent> < info></info> <attribute> <name>action</name> <required>true </required> </attribute> <dynamic-attributes>true</dynamic-attributes> </tag></taglib>
Finally, this custom tag is introduced in the JSP that needs to be used:
<%@ taglib prefix= "route" uri= "/myweb-router"%>
Resources
- Servlet life cycle and how it works
- Jsp/servlet Working principle
- El expression
- Developing web programs using Servlets and JSPs
- Introduction to the filter filter in Java Web note –servlet and the use of write filters
- Implement a simple servlet container
Original address: http://codemacro.com/2015/06/07/servlet-web-framework/
Written by Kevin Lynx posted athttp://codemacro.com
Implementing a Web framework based on a servlet