一、自定义SpringMVC的常用注解
-----------------------------------CDAutowried---------------------------------------
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CDAutowried {
String value() default "";
}
-----------------------------------CDController--------------------------------------
@Documented //注解是否将包含在JavaDoc中
@Target(ElementType.TYPE) //注解用于什么地方
@Retention(RetentionPolicy.RUNTIME) //定义该注解的生命周期
public @interface CDController {
String value() default "";
}
-----------------------------------CDRequestMapping----------------------------------
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CDRequestMapping {
String value() default "";
}
-----------------------------------CDRequestParam------------------------------------
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CDRequestParam {
String value() default "";
}
-----------------------------------CDService-----------------------------------------
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CDService {
String value() default "";
}
ps:为了区别SpringMVC的注解,我在自定义的注解前加上公司名称CD(虫洞)。
关于自定义注解可以参考我的另一篇博文菜鸟程序猿之自定义注解。
二、pom.xml中添加servlet的依赖
<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>springMVC</groupId>
<artifactId>com.chongdong</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>com.chongdong Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--servlet的依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>com.chongdong</finalName>
</build>
</project>
三、新增相关接口和controller
--------------------------------------controller--------------------------------------
@CDController
@CDRequestMapping("/user")
public class CDUserController {
@CDAutowried
CDUserService cDUserService;
@CDRequestMapping("/getUserList")
public void getUserList(HttpServletRequest request,
HttpServletResponse response,@CDRequestParam("userId") Long userId) {
try {
String name = cDUserService.getUserByUserId(userId).toString();
response.getWriter().write(name);
} catch (IOException e) {
}
}
}
--------------------------------------service--------------------------------------
public interface CDUserService {
User getUserByUserId(Long userId);
}
--------------------------------------serviceImpl------------------------------------
@CDService("cDUserService")
public class CDUserServiceImpl implements CDUserService {
public User getUserByUserId(Long userId) {
User user = new User();
user.setId(userId);
user.setName("name"+userId);
user.setAge(userId.intValue()+20);
return user;
}
}
四、自定义一个DisPatherServlet(核心)
1、模仿Spring的application.xml,这里我使用application.properties代替。
在 application.properties中配置一个参数,basePackage=com.chongdong.work,和springMVC的包扫描类似,指定哪些包下的类将会被扫描。
2、web.xml配置
配置自定义的DisPatherServlet的参数
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>CDDispatherServlet</servlet-name>
<init-param>
<!--这个参数名称是固定的,不可修改-->
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<servlet-class>com.chongdong.servlet.CDDispatherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CDDispatherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
3、重写父类的init(ServletConfig config)方法
@Override
public void init(ServletConfig config) {
//1、加载配置文件
initConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关类
doScanner(prop.getProperty("basePackage"));
//3、初始化所有相关类,加载到IOC(自定义)中
doInstance();
//4、实现依赖注入
doAutowried();
//5、构造一个HandlerMapping,将构造好的url和method建立关系
initHandlerMapping();
//6、等待请求,调用doPost或doGet,根据匹配的url去动态地调用对应的方法
//如果加载没问题
System.out.println("----------------CDSpringMVC is starting!-----------");
}
其中初始化分为5大步:
a、加载配置文件
private void initConfig(String path) {
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(path);
try {
prop.load(stream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
b、扫描指定包相关的类
private void doScanner(String packageName) {
/**得到包的路径**/
URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
/**得到包的文件夹**/
File dir = new File(url.getFile());
/**遍历**/
for (File file : dir.listFiles()) {
if(file.isDirectory()) {
/**递归**/
doScanner(packageName + "." + file.getName());
}else {
/**加入类名集合**/
classNameList.add(packageName +"."+file.getName().replaceAll(".class", ""));
}
}
}
c、初始化所有相关的类,并加入到自定义的IOC容器中
private void doInstance() {
/**类名集合为空,直接返回*/
if(classNameList.size()==0)
return;
try {
/**遍历类名集合,进行循环初始化,放入IOC**/
for (String className : classNameList) {
/**利用反射得到当前类**/
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(CDController.class)) {
/**当前类上加了这个注解,认为其是一个controller,可以初始化**/
/**当前类名**/
String beanName = clazz.getSimpleName();
/**类名首字母小写,作为ID**/
beanName = getName(beanName);
/**反射初始化**/
IOC.put(beanName, clazz.newInstance());
}else if(clazz.isAnnotationPresent(CDService.class)) {
/**当前类上加了这个注解,也可以初始化**/
CDService annotation = clazz.getAnnotation(CDService.class);
String beanName = annotation.value();
if(!"".equals(beanName.trim())) {
//1、如果注解中自定义名称,使用自定义
IOC.put(beanName, clazz.newInstance());
}else {
//2、如果没有自定义,默认首字母小写
/**类名首字母小写,作为ID**/
beanName = getName(beanName);
/**反射初始化**/
IOC.put(beanName, clazz.newInstance());
}
//3、对方注入的对象是接口,采用其实现接口的全程作为beanName
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> c : interfaces) {
IOC.put(c.getSimpleName(), clazz.newInstance());
}
}
}
} catch (Exception e) {
}
}
d、实现依赖注入
private void doAutowried() {
if(IOC.isEmpty())
return;
/**注入就是对类中定义的一些属性进行赋值,只要加了autowried注解就注入**/
for (Entry<String, Object> enrty : IOC.entrySet()) {
Field[] fields = enrty.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
/**属性没有此注解,跳过**/
if(!field.isAnnotationPresent(CDAutowried.class))
continue;
CDAutowried annotation = field.getAnnotation(CDAutowried.class);
String beanName = annotation.value().trim();
if("".equals(beanName)) {
//默认属性名称
beanName = field.getType().getSimpleName();
}
//通过反射机制强制赋值
field.setAccessible(true);//强制访问
try {
field.set(enrty.getValue(), IOC.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
e、构建一个handlerMapping,是url和method建立映射关系
private void initHandlerMapping() {
if(IOC.isEmpty())
return;
for (Entry<String,Object> entry : IOC.entrySet()) {
//扫描所有的方法
Class<? extends Object> clzz = entry.getValue().getClass();
//不是controller不需要进行关系映射
if (!clzz.isAnnotationPresent(CDController.class))
continue;
//类上的url
String baseUrl = "";
//将类上的requestMapping和方法上的requestMapping进行拼接,得到url
if(clzz.isAnnotationPresent(CDRequestMapping.class)) {
CDRequestMapping annotation = clzz.getAnnotation(CDRequestMapping.class);
baseUrl = annotation.value();
}
Method[] methods = clzz.getMethods();
for (Method method : methods) {
//方法没有此注解,跳过此次循环/
if(!method.isAnnotationPresent(CDRequestMapping.class))
continue;
CDRequestMapping annotation = method.getAnnotation(CDRequestMapping.class);
//将连续的/替换成单个的/
String regex = ("/" + baseUrl + annotation.value()).replaceAll("/+", "/");
//得到正则
Pattern compile = Pattern.compile(regex);
//把url和method的关系再重新分配一次
handlerMapping.add(new Handler(compile,entry.getValue(),method));
}
}
}
等待用户请求,执行映射关系的对应
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
doDispath(request,response);
} catch (Exception e) {
response.getWriter().write("request 500 error,details is "+Arrays.toString(e.getStackTrace()));
}
}
private void doDispath(HttpServletRequest request, HttpServletResponse response) throws Exception{
Handler handler = getHandler(request);
if(handler == null) {
//找不到对应的映射,则提示请求404
response.getWriter().write("request 404 not found");
return;
}
//获取自定义方法的参数列表
Class<?>[] paramTypes = handler.method.getParameterTypes();
//保存所有需要自动赋值的参数
Object[] paramValues = new Object[paramTypes.length];
//获得request的参数列表
Map<String,String[]> params = request.getParameterMap();
for (Entry<String,String[]> param : params.entrySet()) {
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
//如果找到匹配的对象,开始填充值
if(!handler.paramIndexMapping.containsKey(param.getKey()))
continue;
Integer index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index],value);
}
//设置方法中的requset和response对象
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = request;
int repIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[repIndex] = response;
try {
//利用反射执行目标方法
Method method = handler.method;
method.invoke(handler.controller, paramValues);
}catch(Exception e) {
e.printStackTrace();
}
}
这里用到一个自定义的内部类,用于存放目标方法、目标对象以及映射关系。
private class Handler{
protected Object controller; //保存方法对应的示例
protected Method method; //保存映射的方法
protected Pattern pattern; //Spring的URL是支持正则的
protected Map<String,Integer> paramIndexMapping; //参数顺序
protected Handler(Pattern pattern,Object controller,Method method) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
paramIndexMapping = new HashMap<String,Integer>();
putParamIndexMapping(method);
}
private void putParamIndexMapping(Method method2) {
//提取方法中加了注解的参数
Annotation[][] pa = method.getParameterAnnotations();
for (int index = 0; index < pa.length; index++) {
for (Annotation annotation : pa[index]) {
if(annotation instanceof CDRequestParam) {
String paramName = ((CDRequestParam)annotation).value();
if(!"".equals(paramName.trim()))
paramIndexMapping.put(paramName, index);
}
}
}
//提取方法中的request和response参数
Class<?>[] paramTypes = method.getParameterTypes();
for (int index = 0; index < paramTypes.length; index++) {
Class<?> type = paramTypes[index];
if(type == HttpServletRequest.class || type == HttpServletResponse.class)
paramIndexMapping.put(type.getName(), index);
}
}
}
可能大家分开看代码看不懂,下面附上这个DisPatherServlet的完整代码
package com.chongdong.servlet;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
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 com.chongdong.annotation.CDAutowried;
import com.chongdong.annotation.CDController;
import com.chongdong.annotation.CDRequestMapping;
import com.chongdong.annotation.CDRequestParam;
import com.chongdong.annotation.CDService;
/**
* 手写一个springMVC框架
* @author yanxh
*
*/
public class CDDispatherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**用于读取配置文件**/
private Properties prop = new Properties();
/**存放当前扫描包下面的所有类名**/
private List<String> classNameList = new ArrayList<String>();
/**IOC**/
private Map<String,Object> IOC = new HashMap<String,Object>();
/**mapping url和method的映射关系map**/
private List<Handler> handlerMapping = new ArrayList<Handler>();
public CDDispatherServlet() {
}
@Override
public void init(ServletConfig config) {
//1、加载配置文件
initConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关类
doScanner(prop.getProperty("basePackage"));
//3、初始化所有相关类,加载到IOC(自定义)中
doInstance();
//4、实现依赖注入
doAutowried();
//5、构造一个HandlerMapping,将构造好的url和method建立关系
initHandlerMapping();
//6、等待请求,调用doPost或doGet,根据匹配的url去动态地调用对应的方法
//如果加载没问题
System.out.println("----------------CDSpringMVC is starting!-----------");
}
/***
* 等待请求,调用doPost或doGet,根据匹配的url去动态地调用对应的方法
*/
private void doDispath(HttpServletRequest request, HttpServletResponse response) throws Exception{
Handler handler = getHandler(request);
if(handler == null) {
//找不到对应的映射,则提示请求404
response.getWriter().write("request 404 not found");
return;
}
//获取自定义方法的参数列表
Class<?>[] paramTypes = handler.method.getParameterTypes();
//保存所有需要自动赋值的参数
Object[] paramValues = new Object[paramTypes.length];
//获得request的参数列表
Map<String,String[]> params = request.getParameterMap();
for (Entry<String,String[]> param : params.entrySet()) {
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
//如果找到匹配的对象,开始填充值
if(!handler.paramIndexMapping.containsKey(param.getKey()))
continue;
Integer index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index],value);
}
//设置方法中的requset和response对象
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = request;
int repIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[repIndex] = response;
try {
//利用反射执行目标方法
Method method = handler.method;
method.invoke(handler.controller, paramValues);
}catch(Exception e) {
e.printStackTrace();
}
}
private Object convert(Class<?> clazz, String value) {
if(Long.class == clazz) {
return Long.valueOf(value);
}
return value;
}
/**
* 构造一个HandlerMapping,将构造好的url和method建立关系(最关键的步骤)
*/
private void initHandlerMapping() {
if(IOC.isEmpty())
return;
for (Entry<String,Object> entry : IOC.entrySet()) {
//扫描所有的方法
Class<? extends Object> clzz = entry.getValue().getClass();
//不是controller不需要进行关系映射
if (!clzz.isAnnotationPresent(CDController.class))
continue;
//类上的url
String baseUrl = "";
//将类上的requestMapping和方法上的requestMapping进行拼接,得到url
if(clzz.isAnnotationPresent(CDRequestMapping.class)) {
CDRequestMapping annotation = clzz.getAnnotation(CDRequestMapping.class);
baseUrl = annotation.value();
}
Method[] methods = clzz.getMethods();
for (Method method : methods) {
//方法没有此注解,跳过此次循环/
if(!method.isAnnotationPresent(CDRequestMapping.class))
continue;
CDRequestMapping annotation = method.getAnnotation(CDRequestMapping.class);
//将连续的/替换成单个的/
String regex = ("/" + baseUrl + annotation.value()).replaceAll("/+", "/");
//得到正则
Pattern compile = Pattern.compile(regex);
//把url和method的关系再重新分配一次
handlerMapping.add(new Handler(compile,entry.getValue(),method));
}
}
}
/**
* 实现依赖注入
*/
private void doAutowried() {
if(IOC.isEmpty())
return;
/**注入就是对类中定义的一些属性进行赋值,只要加了autowried注解就注入**/
for (Entry<String, Object> enrty : IOC.entrySet()) {
Field[] fields = enrty.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
/**属性没有此注解,跳过**/
if(!field.isAnnotationPresent(CDAutowried.class))
continue;
CDAutowried annotation = field.getAnnotation(CDAutowried.class);
String beanName = annotation.value().trim();
if("".equals(beanName)) {
//默认属性名称
beanName = field.getType().getSimpleName();
}
//通过反射机制强制赋值
field.setAccessible(true);//强制访问
try {
field.set(enrty.getValue(), IOC.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 初始化所有相关类,加载到IOC(自定义)中
*/
private void doInstance() {
/**类名集合为空,直接返回*/
if(classNameList.size()==0)
return;
try {
/**遍历类名集合,进行循环初始化,放入IOC**/
for (String className : classNameList) {
/**利用反射得到当前类**/
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(CDController.class)) {
/**当前类上加了这个注解,认为其是一个controller,可以初始化**/
/**当前类名**/
String beanName = clazz.getSimpleName();
/**类名首字母小写,作为ID**/
beanName = getName(beanName);
/**反射初始化**/
IOC.put(beanName, clazz.newInstance());
}else if(clazz.isAnnotationPresent(CDService.class)) {
/**当前类上加了这个注解,也可以初始化**/
CDService annotation = clazz.getAnnotation(CDService.class);
String beanName = annotation.value();
if(!"".equals(beanName.trim())) {
//1、如果注解中自定义名称,使用自定义
IOC.put(beanName, clazz.newInstance());
}else {
//2、如果没有自定义,默认首字母小写
/**类名首字母小写,作为ID**/
beanName = getName(beanName);
/**反射初始化**/
IOC.put(beanName, clazz.newInstance());
}
//3、对方注入的对象是接口,采用其实现接口的全程作为beanName
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> c : interfaces) {
IOC.put(c.getSimpleName(), clazz.newInstance());
}
}
}
} catch (Exception e) {
}
}
/**
* 类名首字母小写
* @param beanName
* @return
*/
private String getName(String beanName) {
char[] charArray = beanName.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
}
/***
* 扫描相关类
*/
private void doScanner(String packageName) {
/**得到包的路径**/
URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
/**得到包的文件夹**/
File dir = new File(url.getFile());
/**遍历**/
for (File file : dir.listFiles()) {
if(file.isDirectory()) {
/**递归**/
doScanner(packageName + "." + file.getName());
}else {
/**加入类名集合**/
classNameList.add(packageName +"."+file.getName().replaceAll(".class", ""));
}
}
}
/**
* 加载配置文件
*/
private void initConfig(String path) {
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(path);
try {
prop.load(stream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
doDispath(request,response);
} catch (Exception e) {
response.getWriter().write("request 500 error,details is "+Arrays.toString(e.getStackTrace()));
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
/**
* Handler记录Controlelr中的RequestMapping和Method的映射关系
* @author yanxh
*
*/
private class Handler{
protected Object controller; //保存方法对应的示例
protected Method method; //保存映射的方法
protected Pattern pattern; //Spring的URL是支持正则的
protected Map<String,Integer> paramIndexMapping; //参数顺序
protected Handler(Pattern pattern,Object controller,Method method) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
paramIndexMapping = new HashMap<String,Integer>();
putParamIndexMapping(method);
}
private void putParamIndexMapping(Method method2) {
//提取方法中加了注解的参数
Annotation[][] pa = method.getParameterAnnotations();
for (int index = 0; index < pa.length; index++) {
for (Annotation annotation : pa[index]) {
if(annotation instanceof CDRequestParam) {
String paramName = ((CDRequestParam)annotation).value();
if(!"".equals(paramName.trim()))
paramIndexMapping.put(paramName, index);
}
}
}
//提取方法中的request和response参数
Class<?>[] paramTypes = method.getParameterTypes();
for (int index = 0; index < paramTypes.length; index++) {
Class<?> type = paramTypes[index];
if(type == HttpServletRequest.class || type == HttpServletResponse.class)
paramIndexMapping.put(type.getName(), index);
}
}
}
/**
* 根据请求的url和已有的handler进行匹配
* @param request
* @return
*/
private Handler getHandler(HttpServletRequest request) {
if(handlerMapping.isEmpty())
return null;
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
requestURI.replace(contextPath, "").replaceAll("/+", "/");
for (Handler handler : handlerMapping) {
Matcher matcher = handler.pattern.matcher(requestURI);
if(matcher.matches())
return handler;
}
return null;
}
}
这样的话,启动tomcat进行访问url就能找到对应的method进行信息输出。
谢谢观看!
您的肯定是对我最大的鼓励!