用过springmvc的可能都知道,要集成springmvc需要在web.xml中加入一个跟随web容器启动的DispatcherServlet,然后由该servlet初始化一些东西,然后所有的web请求都被这个servlet接管。所以自己写mvc的关键就是弄懂这个servlet干了啥。先分析一下springmvc的功能,首先我们写一个接口,就是写一个Controller,然后里面写一个方法,在类或者方法里面使用@RequestMapping直接修饰,当该注解对应的path被请求时,会按照指定格式传入参数并调用该方法,然后按照指定格式将调用的结果写出到向浏览器的输出流中(@ResponseBody),或者转发到jsp,再去由jsp转换生成的servlet去将结果写出到输出流(默认的请求转发),或者重定向到指定的jsp(return "redirect:/test.jsp"等。上面仅仅使用jsp举例子,不代表springmvc只支持使用jsp渲染。但是我们自己写的mvc只是为了演示整个流程和若干细节,并不能全面重写springmvc我也没那个能力重写,所以页面层只用jsp。
  详细分析下写一个mvc的流程:第一:我们也可以使用一个servlet将前端所有请求都接管到一个servlet中去(这里其实filter,servlet都可以实现,比如strus2采用filter接管,springmvc采用servlet,原理大同小异),第二:这个servlet是随着容器自启动,所以需要配置load-on-startup,然后我们在这个servlet的init方法里面可以扫描指定的包(扫描哪些包,可以通过servlet在web.xml中的init-param配置),加载一些注解并将注解配置的属性和类,方法对象的关系保存在一些map中,第三:当页面访问任意后端接口时,最终会经过doGet或者doPost(上层是service方法,为了方便不用service方法),我们可以在这两个方法中根据请求的url路径去找到对应的Controller类和Method对象,然后从请求中拿出参数传入方法需要的参数,得到方法执行的结果,最后根据方法里面指定的返回格式(@ResponseBody这种),或者请求转发,或者重定向做处理。废话不多说,直接上代码。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>mymvc</display-name>
<servlet>
<servlet-name>dispacher</servlet-name>
<servlet-class>com.rd.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>package</param-name>
<param-value>com.rd.controller</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispacher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
package com.rd.servlet;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import com.rd.annotation.Path;
import com.rd.annotation.RespJson;
import com.rd.util.JsonUtil;
import com.rd.util.MethodUtil;
import com.rd.util.ScanClassUtil; /**
* 类似springmvc的将请求全部纳入控制范围的servlet
* @author rongdi
* @date 2017年9月20日 下午1:58:26
*/
public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; //path和Class的映射
private final static Map<String, Class<?>> classMap = new HashMap<String, Class<?>>(); //path和Method的映射
private final static Map<String, Method> methodMap = new HashMap<String, Method>(); //存放被@RespJson修饰的类
private final static Set<Class<?>> classRespJsons = new HashSet<Class<?>>(); //存放被@RespJson修饰的方法
private final static Set<Method> methodRespJsons = new HashSet<Method>(); @Override
public void init(ServletConfig config) throws ServletException {
System.out.println("---DispatcherServlet初始化开始---");
//获取web.xml中配置的要扫描的包
String basePackage = config.getInitParameter("package");
//配置了多个包
if (basePackage.indexOf(",")>0) {
//按逗号进行分隔
String[] packageNameArr = basePackage.split(",");
for (String packageName : packageNameArr) {
add2ClassMap(packageName);
}
}else {
add2ClassMap(basePackage);
}
System.out.println("----DispatcherServlet初始化结束---");
} /**
* 将被注解修饰的类
* @param packageName
*/
private void add2ClassMap(String packageName){
Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
for (Class<?> clazz :setClasses) {
String pathAttrValue = null;
//判断类被注解修饰
if (clazz.isAnnotationPresent(Path.class)) {
//获取path的Annotation的实例
Path pathInstance = clazz.getAnnotation(Path.class);
//获取Annotation的实例的value属性的值
pathAttrValue = pathInstance.value();
if (StringUtils.isNotEmpty(pathAttrValue)) {
pathAttrValue = handPathStr(pathAttrValue);
classMap.put(pathAttrValue, clazz);
}
}
if(clazz.isAnnotationPresent(RespJson.class)) {
classRespJsons.add(clazz);
}
//判断方法被注解修饰
Method[] methods = clazz.getMethods();
for(Method m:methods) {
//判断方法被注解修饰
if(m.isAnnotationPresent(Path.class)) {
//获取path的Annotation的实例
Path pathInstance = m.getAnnotation(Path.class);
//获取Annotation的实例的value属性的值
String methodPathValue = pathInstance.value();
if (StringUtils.isNotEmpty(methodPathValue)) {
methodPathValue = handPathStr(methodPathValue);
pathAttrValue = handPathStr(pathAttrValue);
methodMap.put(pathAttrValue+methodPathValue, m);
}
}
if(m.isAnnotationPresent(RespJson.class)) {
methodRespJsons.add(m);
}
}
}
} /**
* 处理一下路径,前面后面的斜杠
* @param pathStr
* @return
*/
private String handPathStr(String pathStr) {
if(pathStr.endsWith("/")) {
pathStr = pathStr.substring(0,pathStr.length()-1);
}
if(!pathStr.startsWith("/")) {
pathStr = "/"+pathStr;
}
return pathStr;
} @Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String,String[]> params = request.getParameterMap();
String url = request.getRequestURI();
//当请求根路径是,请求转发到首页
if("/".equals(url)) {
request.getRequestDispatcher("/index.jsp").forward(request, response);
return;
}
if("/favicon.ico".equals(url)) {
return;
}
Method m = null;
if(methodMap.containsKey(url)) {
m = methodMap.get(url);
} else {
//这个过程,其实可以优化,如果存在通配符匹配,不用每次都循环匹配,可以缓存起来,第二次直接用,这里暂时忽略优化问题
Set<String> urls = methodMap.keySet();
for(String murl:urls) {
String reg = "^"+murl.replace("*", ".*?")+"$";
if(Pattern.matches(reg, url)) {
m = methodMap.get(murl);
break;
}
}
}
if(m == null) {
throw new ServletException("没有找到与路径:"+url+"对应的处理方法");
} try {
/**
* 这里需要获取参数名,jdk1.8之后可以直接直接反射获取,条件比较恶心,需要开启开关
* 如下直接使用javassist字节码工具类实现,也可以用asm等其他工具
*/
String[] paramNames = MethodUtil.getAllParamaterName(m);
List<String> paramValues = new ArrayList<String>();
for(String paramName:paramNames) {
if(params.get(paramName) == null) {
paramValues.add(null);
} else {
paramValues.add(params.get(paramName)[0]);
} }
/**
* 调用方法所在类的默认构造方法,生成执行方法的对象(springmvc里的这个对象是单例的,这里为了省事,每次都new一个出来),
* 然后执行方法,返回结果
*/
Class<?> cla = m.getDeclaringClass();
Object result = m.invoke(cla.newInstance(), paramValues.toArray());
/**
* 如果方法返回类型为 void,则该调用结果返回 null,如果返回值为void,则直接跳转到同路径的jsp页面上,
* 为了简单起见这里后缀写死.jsp,实际上springmvc是支持配置ViewResolver,
* 可以指定请求转发或者重定向所在的界面层的前缀和后缀。
*/
if(result == null) {
request.getRequestDispatcher(url+".jsp").forward(request, response);
}
/**
* 这里springmvc默认是请求转发到jsp
* 为了方便这里直接根据修饰返回类型的注解,确定用哪种方式序列化,
*/
if(classRespJsons.contains(cla) || methodRespJsons.contains(m)) {
result = JsonUtil.serialize(result);
//输出json到浏览器
response.getWriter().print(result);
} else if(result.toString().startsWith("redirect:")) { //重定向
//去掉前缀就是重定向到的路径,实际上这里不严谨,应该加上一个项目的上下文路径
response.sendRedirect(result.toString().substring(9)+".jsp");
} else { //请求转发
request.getRequestDispatcher(result.toString()+".jsp").forward(request, response);
} } catch (Exception e) {
e.printStackTrace();
}
} @Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
} public static void main(String[] args) {
String url = "/test/a";
String reg = "^/test/.*?$";
System.out.println(Pattern.matches(reg, url));
System.out.println(Long.class); }
}
package com.rd.controller;

import java.util.HashMap;
import java.util.Map; import com.rd.annotation.Path;
import com.rd.annotation.RespJson; @Path(value="/test")
public class TestController { @Path(value="/text")
public void text() {
} @Path(value="/redirect")
public String redirect(String a) { return "redirect:/test/redirect";
} @Path(value="/json")
@RespJson
public Map json(String a) {
return new HashMap(){{put("a",a);}};
} }
package com.rd.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Path { //访问的匹配路径
String value(); }
package com.rd.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RespJson { }
package com.rd.util;

import java.util.HashMap;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; public class JsonUtil { private static ObjectMapper mapper; static {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
} public static String serialize(Object obj) throws Exception { if (obj == null) {
throw new IllegalArgumentException("obj should not be null");
}
return mapper.writeValueAsString(obj);
} public static void main(String[] args) throws Exception {
System.out.println(serialize(new HashMap(){{put("name","zhangsan");}}));
}
}
package com.rd.util;
import java.lang.reflect.Method; import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo; /**
* 使用javassist的方法工具
* @author rongdi
* @date 2017年9月20日 上午11:21:29
*/
public class MethodUtil { public static String[] getAllParamaterName(Method method)
throws NotFoundException {
Class<?> clazz = method.getDeclaringClass();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(MethodUtil.class);
pool.insertClassPath(classPath);
CtClass clz = pool.get(clazz.getName());
CtClass[] params = new CtClass[method.getParameterTypes().length];
for (int i = 0; i < method.getParameterTypes().length; i++) {
params[i] = pool.getCtClass(method.getParameterTypes()[i].getName());
}
CtMethod cm = clz.getDeclaredMethod(method.getName(), params);
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
.getAttribute(LocalVariableAttribute.tag);
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
String[] paramNames = new String[cm.getParameterTypes().length];
for (int i = 0; i < paramNames.length; i++) {
paramNames[i] = attr.variableName(i + pos);
}
return paramNames;
} }
package com.rd.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; /**
* @author rongdi
* @date 2017年9月16日 下午3:36:08
*/
public class PropertyUtil { private static Properties props; synchronized static private void loadProps() {
props = new Properties();
InputStream in = null; try {
try {
String path = getJarDir()+"/application.properties";
in = new FileInputStream(path);
} catch(Exception e){
in = PropertyUtil.class.getClassLoader().getResourceAsStream("application.properties");
}
props.load(in);
} catch (FileNotFoundException e) {
//logger.error("application.properties文件未找到");
} catch (IOException e) {
//logger.error("出现IOException");
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
//logger.error("application.properties文件流关闭出现异常");
}
}
} public static String getProperty(String key) {
if (null == props) {
loadProps();
}
return props.getProperty(key);
} public static String getProperty(String key, String defaultValue) {
if (null == props) {
loadProps();
}
return props.getProperty(key, defaultValue);
} /**
* 获取jar绝对路径
*
* @return
*/
public static String getJarPath() {
File file = getFile();
if (file == null)
return null;
return file.getAbsolutePath();
} /**
* 获取jar目录
*
* @return
*/
public static String getJarDir() {
File file = getFile();
if (file == null)
return null;
return getFile().getParent();
} /**
* 获取jar包名
*
* @return
*/
public static String getJarName() {
File file = getFile();
if (file == null)
return null;
return getFile().getName();
} /**
* 获取当前Jar文件
*
* @return
*/
private static File getFile() {
// 关键是这行...
String path = PropertyUtil.class.getProtectionDomain().getCodeSource()
.getLocation().getFile();
try {
path = java.net.URLDecoder.decode(path, "UTF-8"); // 转换处理中文及空格
} catch (java.io.UnsupportedEncodingException e) {
return null;
}
return new File(path);
}
}
package com.rd.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; /**
* 扫描字节码工具类
* @author rongdi
* @date 2017年9月19日 下午4:13:19
*/
public class ScanClassUtil { /**
* 从包package中获取所有的Class
* @param pack
* @return
*/
public static Set<Class<?>> getClasses(String pack) { // 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(
packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
System.err.println("file类型的扫描");
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath,recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
System.err.println("jar类型的扫描");
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection())
.getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(
packageName.length() + 1, name
.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.'
+ className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} return classes;
} /**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
<properties>
<project.deploy>deploy</project.deploy>
<jackson.version>2.5.4</jackson.version>
</properties> <dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency> </dependencies>

完整代码百度云地址(不要吐槽要放git啥的,我屌丝一个,不用那么高大上的东西):https://pan.baidu.com/s/1mi24Lbq

自己动手实现mvc框架的更多相关文章

  1. 自己动手写PHP MVC框架

    自己动手写PHP MVC框架 来自:yuansir-web.com / yuansir@live.cn 代码下载: https://github.com/yuansir/tiny-php-framew ...

  2. 自己动手写Spring框架--IOC、MVC

    对于一名Java开发人员,我相信没有人不知道 Spring 框架,而且也能够轻松就说出 Spring 的特性-- IOC.MVC.AOP.ORM(batis). 下面我想简单介绍一下我写的轻量级的 S ...

  3. 自己动手写一个简单的MVC框架(第二版)

    一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在这个System.W ...

  4. 自己动手做Web框架—MVC+Front Controller

    在我前面一篇博文<逃脱Asp.Net MVC框架的枷锁,使用Razor视图引擎>发表之后,很多人关心,脱离了之后怎么办?那么这可以说是它的续篇了. 同时,这也是eLiteWeb开源软件的一 ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  7. 从零开始学 Java - 搭建 Spring MVC 框架

    没有什么比一个时代的没落更令人伤感的了 整个社会和人都在追求创新.进步.成长,没有人愿意停步不前,一个个老事物慢慢从我们生活中消失掉真的令人那么伤感么?或者说被取代?我想有些是的,但有些东西其实并不是 ...

  8. 【WEB】初探Spring MVC框架

    Spring MVC框架算是当下比较流行的Java开源框架.但实话实说,做了几年WEB项目,完全没有SpringMVC实战经验,乃至在某些交流场合下被同行严重鄙视“奥特曼”了.“心塞”的同时,只好默默 ...

  9. MVC框架模式技术实例(用到隐藏帧、json、仿Ajax、Dom4j、jstl、el等)

    前言: 刚刚学完了MVC,根据自己的感悟和理解写了一个小项目. 完全按照MVC模式,后面有一个MVC的理解示意图. 用MVC模式重新完成了联系人的管理系统: 用户需求: 多用户系统,提供用户注册.登录 ...

随机推荐

  1. 【20171025中】alert(1) to win 脚本渲染自建

    游戏误人生,一下午玩了将近四个小时的三国杀,后悔不已,然后重新拾起xss challenge,突发奇想,自己构建渲染后的html. 从最简单的开始. 自动检测html: <!DOCTYPE ht ...

  2. net start mongodb 服务名无效解决方案

    net start mongodb 服务名无效 或者 net start mongodb 发生错误5,拒绝访问.是因为没有用管理员权限运行cmd. 解决方案:在window中,在搜索框输入cmd后,在 ...

  3. 用C写的计算运行时间

    #include <stdio.h> #include <stdlib.h> #include <time.h> int main( void ) { long i ...

  4. Leetcode题解(21)

    62. Unique Paths 题目 分析: 机器人一共要走m+n-2步,现在举个例子类比,有一个m+n-2位的二进制数,现在要在其中的m位填0,其余各位填1,一共有C(m+n-2,m-1)种可能, ...

  5. 修改Servlet模板,让Servlet更清新

    每次新建一个Servlet,都会有很多的代码跟注释,但是在实际开发中我们是用不到这些的,所以每次都得手动删除,非常麻烦,所以接下来讲一下如何让自己的Servlet看上去更清新: 首先找到你的Myecl ...

  6. 注销/etc/passwd带来的系统登陆不上

    今天在修改虚拟机密码上的时候,将/etc/passwd中root所在的哪行注销掉了,想象是注销了,root登陆时应该不要输入密码,结果是系统进度条走到最后的时候 进入不了系统了. 结果去普及了下/et ...

  7. JAVAscript学习笔记 js条件语句 第三节 (原创) 参考js使用表 (2017-09-14 15:55)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. PHP面试随笔

    1.常见的HTTP状态码: 1xx系列:代表请求已被接受,需要继续处理 2xx系列:代表请求已成功被服务器接收.理解并接受 200:表示请求已成功,请求所希望的响应头或数据体将随此响应返回 201:表 ...

  9. Python的property装饰器的基本用法

    Python的@property装饰器用来把一个类的方法变成类的属性调用,然后@property本身又创建了另一个装饰器,用一个方法给属性赋值.下面是在类中使用了@property后,设置类的读写属性 ...

  10. Lucene全文检索引擎

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...