个人水平比较菜,没有这么高的实力简单实现springmvc框架,我是看了一个老哥的博客,这老哥才是大神!

  原文链接:https://www.cnblogs.com/xdp-gacl/p/4101727.html

  ok,开始瞎扯一下,我们用springmvc的时候是不是要导入依赖或者是jar包啊,那是由于别人将很多功能都给设计好了,我们直接拿过来用,这有好处也有坏处;好处是用起来很方便,直接用就好了,但是坏处就是封装的太好了我们很难真正的弄清楚其中的运行原理,虽然说可以走源码,但是总是感觉差了一点什么东西。

  反正我走源码就是总感觉有点欠缺,但是又说不上来哪里不对劲,直到现在醒悟了一点,其实就是对原理了解的不够彻底,还有就是源码都是一层套一层,层层封装,走着走着我也不知道上一步做了什么事了,简直就是山路十八弯,哈哈哈。

  瞎扯结束,接下来我根据那个老哥的博客自己稍微整理一下,简单实现一个springmvc的框架,只为更透彻的了解springmvc的原理!

  在看本篇博客之前,可以先忘记以前springmvc的所有东西,我们以 servlet+自定义注解 实现

  新建一个普通的java web项目,我的目录结构如下:

1.工具类

  自己简单实现一些什么框架,必不可少的就是要提前准备很多的工具类

  首先就是要把下面这七个工具类准备好,我把代码复制过来;

  1.1 BeanUtils:对反射的一些操作,可以根据传入xxx.Class参数,进行实例化或者找到其中某个方法

package com.wyq.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; /**
* 对java反射的一些封装
*/
public class BeanUtils {
/**
* 实例化一个class
*/
public static <T> T instanceClass(Class<T> clazz){
if(!clazz.isInterface()){
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
} /**
* 通过构造函数实例化
*
*/
public static <T> T instanceClass(Constructor<T> ctor, Object... args)
throws IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException{
makeAccessible(ctor);
return ctor.newInstance(args);//调用构造方法实例化
} /**
* 查找某个class的方法
*
*/
public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes){
try {
return clazz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
return findDeclaredMethod(clazz, methodName, paramTypes);//返回共有的方法
}
} public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes){
try {
return clazz.getDeclaredMethod(methodName, paramTypes);
}
catch (NoSuchMethodException ex) {
if (clazz.getSuperclass() != null) {
return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
}
return null;
}
} public static Method [] findDeclaredMethods(Class<?> clazz){
return clazz.getDeclaredMethods();
} public static void makeAccessible(Constructor<?> ctor) {
if ((!Modifier.isPublic(ctor.getModifiers())
|| !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
&& !ctor.isAccessible()) {
ctor.setAccessible(true);//如果是私有的 设置为true 使其可以访问
}
} public static Field[] findDeclaredFields(Class<?> clazz){
return clazz.getDeclaredFields();
} }

  1.2 DispatchActionConstant:里面定义两个常量,“forward”和“redirect”,就是为了取值方便,没什么特殊的

package com.wyq.utils;

public class DispatchActionConstant {
public static String FORWARD = "forward";//转发 public static String REDIRECT = "redirect";//重定向 }

  1.3 RequestMapingMap:这里面封装了一个HashMap,键是@RequestMapping注解的value的值,就是那个路径;值为当前被@Controller注解修饰的类的xxx.Class;这个键值对很关键,等一下好好体会一下;例如这样的形式{“/aaa”:“MyController.class”,“xxx”:“xxxx.Class”}

package com.wyq.utils;

import java.util.HashMap;
import java.util.Map; /**
* 存储方法的访问路径
*/
public class RequestMapingMap { /**
* requesetMap:用于存储方法的访问路径和类
*/
private static Map<String, Class<?>> requesetMap = new HashMap<String, Class<?>>(); public static Class<?> getClassName(String path) {
return requesetMap.get(path);
} public static void put(String path, Class<?> className) {
requesetMap.put(path, className);
} public static Map<String, Class<?>> getRequesetMap() {
return requesetMap;
}
}

  1.4ScanClassUtil:可以根据传入的包名或者jar包名称,获取到包下所有类的xxx.Class,然后放进集合中返回给你

package com.wyq.utils;

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; /**
* 扫描指定包或者jar包下面的class
*/
public class ScanClassUtil { /**
* 从包package中获取所有的Class
*/
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) {
// log
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} return classes;
} /**
* 以文件的形式来获取包下的所有Class
*/
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));
//经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}

  1.5 View:就是一个普通类,里面存放了要跳转的url,用在Controller中方法的返回值那里;这里还可以往VIewDate中放数据(最后就是放进request中了)

package com.wyq.utils;

/**
* 视图模型
**/
public class View { private String url;//跳转路径 private String dispathAction = DispatchActionConstant.FORWARD;//跳转方式 public View(String url) {
this.url = url;
} public View(String url,String name,Object value) {
this.url = url;
ViewData view = new ViewData();
view.put(name, value);
} public View(String url,String name,String dispathAction ,Object value) {
this.dispathAction = dispathAction;
this.url = url;
ViewData view = new ViewData();//请看后面的代码
view.put(name, value);
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public String getDispathAction() {
return dispathAction;
} public void setDispathAction(String dispathAction) {
this.dispathAction = dispathAction;
}
}

  1.6 ViewData:封装了HttpServletRequest对象,可以往request中放数据,在jsp中可以取到数据

package com.wyq.utils;

import javax.servlet.http.HttpServletRequest;

/**
* 需要发送到客户端显示的数据模型
*/
public class ViewData { private HttpServletRequest request; public ViewData() {
initRequest();
} private void initRequest(){
//从requestHodler中获取request对象
this.request = WebContext.requestHodler.get();
} public void put(String name,Object value){
this.request.setAttribute(name, value);
}
}

  1.7 WebContext:这里是为了多线程设计的,这里保存了当前线程的HttpServletReques和HttpServletReponse对象,可以在其他类中随时获取这两个对象  

package com.wyq.utils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; /**
* WebContext主要是用来存储当前线程中的HttpServletRequest和HttpServletResponse
* 当别的地方需要使用HttpServletRequest和HttpServletResponse,就可以通过requestHodler和responseHodler获取
**/
public class WebContext { public static ThreadLocal<HttpServletRequest> requestHodler = new ThreadLocal<HttpServletRequest>();
public static ThreadLocal<HttpServletResponse> responseHodler = new ThreadLocal<HttpServletResponse>(); public HttpServletRequest getRequest(){
return requestHodler.get();
} public HttpSession getSession(){
return requestHodler.get().getSession();
} public ServletContext getServletContext(){
return requestHodler.get().getSession().getServletContext();
} public HttpServletResponse getResponse(){
return responseHodler.get();
}
}

2.自定义注解@MyController和@RequestMapping

  这两个注解名字随意取,我觉得最好区分我们熟悉的@Controller和@RequestMapping

package com.wyq.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 我们自定义的Controller注解
*/ @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyController {
public String value() default ""; }
package com.wyq.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 我们自定义的ResponseMapping注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
public String value() default "";
}

3.我们自己的Controller

  注意其中的注解哦~~

package com.wyq.test;

import com.wyq.annotation.MyController;
import com.wyq.annotation.MyRequestMapping;
import com.wyq.utils.View; @MyController
public class LoginUI { //使用MyRequestMapping注解指明forward1方法的访问路径
@MyRequestMapping("aaa")
public View forward1(){
//执行完forward1方法之后返回的视图
return new View("/WEB-INF/login.jsp");
} //使用MyRequestMapping注解指明forward2方法的访问路径
@MyRequestMapping("bbb")
public View forward2(){
//执行完forward2方法之后返回的视图
return new View("/bbb.jsp");
} }

4.注解处理器(最重要!!本质上就是一个普通的servlet,没什么稀奇)

  说一下大概逻辑:

  这里由于我们自定义的两个注解只有我们自己认识,但是程序不认识啊,于是我们要写一个servlet,当我们用浏览器发送请求的时候,服务器首先会自动执行这个servlet,然后去解析拿到有@MyController注解的类,遍历其中所有的方法,将带有@MyRequestMapping注解的value的值作为键,@MyController注解的类作为值,保存在一个HashMap中,就是放在前面说的工具 RequestMapingMap中;

  然后我们的请求到服务器的时候,假如请求路径为http://localhost:8090/myspringmvc/aaa.do,这个servlet中就会对这个路径进行分割,变成取到“aaa”,然后根据这个“aaa”为键,从 RequestMapingMap中取出我们要的那个类(被注解@MyController修饰的),再拿到这个类下的所有方法,遍历,看看哪个方法被@MyRequestMapping(“aaa”)注解修饰,这个方法就是我们的目标!最后用反射调用就这个方法,比如返回一个   View("/aaa.jsp")   对象,根据View中的这个url就可以用request或者response进行转发或者重定向操作,跳转到相应的jsp中返回给浏览器。

  

package com.wyq.handler;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.wyq.annotation.MyController;
import com.wyq.annotation.MyRequestMapping;
import com.wyq.utils.BeanUtils;
import com.wyq.utils.DispatchActionConstant;
import com.wyq.utils.RequestMapingMap;
import com.wyq.utils.ScanClassUtil;
import com.wyq.utils.View;
import com.wyq.utils.WebContext; /**
* Description: AnnotationHandleServlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求<p>
*/
public class AnnotationHandleServlet extends HttpServlet { private String pareRequestURI(HttpServletRequest request){
String path = request.getContextPath()+"/";
String requestUri = request.getRequestURI();
String midUrl = requestUri.replaceFirst(path, "");
String lasturl = midUrl.substring(0, midUrl.lastIndexOf("."));
return lasturl;
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.excute(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.excute(request, response);
} private void excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//将当前线程中HttpServletRequest对象存储到ThreadLocal中,以便在Controller类中使用
WebContext.requestHodler.set(request);
//将当前线程中HttpServletResponse对象存储到ThreadLocal中,以便在Controller类中使用
WebContext.responseHodler.set(response);
//解析url
String lasturl = pareRequestURI(request);
//获取要使用的类
Class<?> clazz = RequestMapingMap.getRequesetMap().get(lasturl);
//创建类的实例
Object classInstance = BeanUtils.instanceClass(clazz);
//获取类中定义的方法
Method [] methods = BeanUtils.findDeclaredMethods(clazz);
Method method = null;
for(Method m:methods){//循环方法,找匹配的方法进行执行
if(m.isAnnotationPresent(MyRequestMapping.class)){
String anoPath = m.getAnnotation(MyRequestMapping.class).value();
if(anoPath!=null && !"".equals(anoPath.trim()) && lasturl.equals(anoPath.trim())){
//找到要执行的目标方法
method = m;
break;
}
}
}
try {
if(method!=null){
//执行目标方法处理用户请求
Object retObject = method.invoke(classInstance);
//如果方法有返回值,那么就表示用户需要返回视图
if (retObject!=null) {
View view = (View)retObject;
//判断要使用的跳转方式
if(view.getDispathAction().equals(DispatchActionConstant.FORWARD)){
//使用转发方式
request.getRequestDispatcher(view.getUrl()).forward(request, response);
}else if(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
//使用重定向方式
response.sendRedirect(request.getContextPath()+view.getUrl());
}else{
request.getRequestDispatcher(view.getUrl()).forward(request, response);
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} @Override
public void init(ServletConfig config) throws ServletException {
/**
* 重写了Servlet的init方法后一定要记得调用父类的init方法,
* 否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时
* 就会出现java.lang.NullPointerException异常
*/
super.init(config);
System.out.println("---初始化开始---");
//获取web.xml中配置的要扫描的包
String basePackage = config.getInitParameter("basePackage");
//如果配置了多个包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
if (basePackage.indexOf(",")>0) {
//按逗号进行分隔
String[] packageNameArr = basePackage.split(",");
for (String packageName : packageNameArr) {
initRequestMapingMap(packageName);
}
}else {
initRequestMapingMap(basePackage);
}
System.out.println("----初始化结束---");
} /**
* @Method: initRequestMapingMap
* @Description:添加使用了Controller注解的Class到RequestMapingMap中
* @Anthor:孤傲苍狼
* @param packageName
*/
private void initRequestMapingMap(String packageName){
Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
for (Class<?> clazz :setClasses) {
if (clazz.isAnnotationPresent(MyController.class)) {
Method [] methods = BeanUtils.findDeclaredMethods(clazz);
for(Method m:methods){//循环方法,找匹配的方法进行执行
if(m.isAnnotationPresent(MyRequestMapping.class)){
String anoPath = m.getAnnotation(MyRequestMapping.class).value();
if(anoPath!=null && !"".equals(anoPath.trim())){
if (RequestMapingMap.getRequesetMap().containsKey(anoPath)) {
throw new RuntimeException("RequestMapping映射的地址不允许重复!");
}
RequestMapingMap.put(anoPath, clazz);
}
}
}
}
}
}
}

5.将注解处理器(servlet)配置到web.xml中

<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>myspringmvc</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>AnnotationHandleServlet</servlet-name>
<servlet-class>com.wyq.handler.AnnotationHandleServlet</servlet-class>
<init-param>
<description>扫描包含@MyController注解的包; 如果有多个包,以逗号分隔</description>
<param-name>basePackage</param-name>
<param-value>com.wyq.test</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>AnnotationHandleServlet</servlet-name>
<!-- 拦截所有以.do后缀结尾的请求 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>

6.两个简单的jsp文件

aaa.jsp

bbb.jsp

多加一个jsp,新建webapps/WEB-INF/login.jsp,随便写点什么东西

7.简单测试

  现在就可以启动tomcat输入localhost:8090/myspringmvc/aaa.do看到效果了(由于我以前改过tomcat端口8090,你们的端口应该是8080),可以访问WEB-INF中的jsp

  

8.稍微提一下

    注意:我们在web.xml中拦截的是xxx.do请求,所以我们的url路径要类似这样的  http://localhost:8090/myspringmvc/aaa.do

   也可以根据自己需要设置拦截类型,比如"*action",或者直接"/",这个时候你就需要在注解处理器做点修改就ok了;

   举个例子,比如我要直接用http://localhost:8090/myspringmvc/aaa去访问,不加do了,注解处理器修改如下:

  

  web.xml修改如下:

  然后就可以看到效果了

9.总结

  有没有发现我们这里最重要的就是那个注解处理器啊,没有这个注解处理器,那些所谓的@Controller,@ResponseBody,@RequestMapping等等,都是一文不值,只不过注解处理器被那些框架封装得太好了,我们一般也接触不到,我们看到的只是这样的--------->“哇,原来这些注解这么牛啊!”“好厉害的注解啊,我要记下来这个用法”....

  而注解处理器其实就是一个servlet,在tomcat服务器启动的时候会运行init()方法,将Controller路径和类存到一个map中;然后我们发请求的时候(比如Get请求),就会到service()方法,接着就分发到doGet()方法,再接着就是到真正处理请求的excute()方法,这个方法可以说是核心的核心,将请求路径进行解析,就可以找到对应的Controller,遍历这个COntroller下的所有方法并且拿到所有方法的@RequestMapping的值,找到真正的处理方法,运行,返回一个view,在这个view的内部封装了对ViewData的操作(ViewData其实就是将数据丢到request域中,以便在jsp中可以获取到数据),所以返回的View也可以是这样的   return new View("/WEB-INF/login.jsp",“name”,“这里是数据”);,最后就是在jsp中展示数据了;

  搞懂了这些我们再回头看看所谓的springmvc,简直就是一目了然啊,只是在这个基础上又封装了很多而已!比如前端控制器就很类似于我们这里的注解处理器,处理器映射器其实就是在一个全局map中根据请求路径找到对应的Controller,处理器适配器不就是从这个Controller中找到目标方法执行么,后面的视图解析器其实跟我们这也很类似啊,根据方法返回的路径去对应的目录下找相应的jsp文件,然后根绝request域中的数据填充进去就ok了,是不是突然感觉整个springmvc就不再那么云里雾里了啊!哈哈哈

  原文链接:https://www.cnblogs.com/xdp-gacl/p/4101727.html

简单实现springmvc框架(servlet+自定义注解)的更多相关文章

  1. SpringMvc框架MockMvc单元测试注解及其原理分析

    来源:https://www.yoodb.com/ 首先简单介绍一下Spring,它是一个轻量级开源框架,简单的来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开 ...

  2. SpringMVC框架搭建 基于注解

    本文将以一个很简单的案例实现 Springmvc框架的基于注解搭建,一下全为个人总结 ,如有错请大家指教!!!!!!!!! 第一步:创建一个动态web工程(在创建时 记得选上自动生成 web.xml ...

  3. 用Maven搭建简单的SpringMVC框架

    本文会详细阐述如何用Maven搭建一个简单的SpringMVC框架 这里就不介绍SpringMVC框架了,咱们直接来搭建 第一步 创建一个Maven的web项目  这里有一个简单的方法 new一个Ma ...

  4. 在springMVC中使用自定义注解来进行登录拦截控制

    1:java注解使用是相当频繁,特别是在搭建一些框架时,用到类的反射获取方法和属性,用的尤其多. java中元注解有四个: @Retention     @Target     @Document  ...

  5. 搭建一个简单的springMVC框架

    //新建一个简单的maven项目,选择war包 //web.xml配置 <?xml version="1.0" encoding="UTF-8"?> ...

  6. 搭建最简单的SpringMVC框架(使用maven)

    本文说明:本文介绍使用maven搭建SpringMVC最简单的框架程序过程,适合初学者上手. 下载链接 点此进入下载链接 1.创建一个maven webapp工程. 2.修改WEB-INF目录下的we ...

  7. 简单搭建SpringMVC框架详解

    在公司待了两年,用的一直是Spring+SpringMVC+Hibernate框架,都是公司自己搭建好的,自己从来没有主动搭建过,闲来无聊,自己搭建试试.一下即我搭建的过程以及搭建所遇到的问题,有部分 ...

  8. 利用Maven, 搭建最简单的SpringMVC框架

    本文介绍使用maven搭建SpringMVC最简单的框架程序过程,适合初学者上手. 文章下载

  9. spring框架校验自定义注解

    起因: 项目开发时遇到一个问题是对于金额类型的字段,数据库中格式一般为BigDecimal类型,两位小数点,然后在接口定义中如果不定义成String类型的话,就不能使用@pattern注解限定格式,而 ...

随机推荐

  1. 安卓---TextVies、Button、EditText

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  2. I/O----复制文本文件

    文件 "我的青春谁做主.txt" 位于 D 盘根目录下,要求将此文件的内容复制到 C:/myPrime.txt 中. package io.day03; import java.i ...

  3. Java 线程池(ThreadPoolExecutor)原理分析与使用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...

  4. 【BZOJ 3697】采药人的路径

    题目链接: TP 题解: 调了好久233. 大概想一想就是树分,然后考虑这样路径(u,v)的特征,以根节点(root)切开,u到root的阴阳差值,和v到root巧合互为相反数,然后考虑要有一个点可作 ...

  5. BZOJ_1801_[Ahoi2009]chess 中国象棋_DP

    BZOJ_1801_[Ahoi2009]chess 中国象棋_DP Description 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像 ...

  6. ubuntu ssh 免密码登录

    1 ssh 是什么? ssh 是一种 加密协议,ssh 是两个加密的密码,一个是公钥一个是私钥,公钥加密的信息只有是要才能解密.ssh协议可用于服务之间的通信.例如:登录验证,git的授权等等 2 s ...

  7. XMR挖矿教程

    XMR挖矿教程 XMR介绍 门罗币(Monero,代号XMR)是一个创建于2014年4月开源加密货币,它着重于隐私.分权和可扩展性.与自比特币衍生的许多加密货币不同,Monero基于CryptoNot ...

  8. ACM——八大输出方式总结

    个人做题总结,希望能够帮助到未来的学弟学妹们的学习! 永远爱你们的 ----新宝宝 1: 题目描述 Your task is to Calculate a + b. Too easy?! Of cou ...

  9. 基于SDRAM的视频图像采集系统

    本文是在前面设计好的简易SDRAM控制器的基础上完善,逐步实现使用SDRAM存储视频流数据,实现视频图像采集系统,CMOS使用的是OV7725. SDRAM控制器的完善 1. 修改SDRAM的时钟到1 ...

  10. Vue.js-11:第十一章 - Vue 中 ref 的使用

    一.前言 在之前的前端开发中,为了实现我们的需求,通常采用的方案是通过 JS/Jquery 直接操纵页面的 DOM 元素,得益于 Jquery 对于 DOM 元素优异的操作能力,我们可以很轻易的对获取 ...