自己手写一个简易版Spring MVC主要是为了强化自己对Spring框架的理解
1.首先新建一个项目:
2.编辑pom.xml文件,主要是增加了servlet-api依赖和jetty插件
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.wlj.myframeworkgroupId>
<artifactId>mvcframeworkartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<name>mvcframework Maven Webappname>
<url>http://www.example.comurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
<servlet.api.version>2.4servlet.api.version>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>${servlet.api.version}version>
<scope>provided scope>
dependency>
dependencies>
<build>
<finalName>mvcframeworkfinalName>
<plugins>
<plugin>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-maven-pluginartifactId>
<version>9.3.7.v20160115version>
<configuration>
<scanIntervalSeconds>10scanIntervalSeconds>
<httpConnector>
<port>8080port>
httpConnector>
<webApp>
<contextPath>/wljcontextPath>
webApp>
configuration>
plugin>
plugins>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-pluginartifactId>
<version>3.0.0version>
plugin>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.7.0version>
plugin>
.............
plugins>
pluginManagement>
build>
project>
3.新建java、resources等目录:
demo目录下是模拟业务代码,framework是框架目录
4.在servlet目录下新建DispatherServlet类:
public class WLJDispatherServlet extends HttpServlet{
private Properties contextConfig=new Properties();
private List classNames=new ArrayList<>();
//IOC容器
private Map ioc=new HashMap<>();
private List handlerMapping=new ArrayList<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
dispatch(req, resp);
} catch (IOException e) {
e.printStackTrace();
resp.getWriter().write("500 Exception");
}
}
private void dispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Handler handler = getHandler(req);
if(handler==null){
resp.getWriter().write("404 NOT FOUND!!");
return;
}
//获取匹配的方法的参数类型列表
Class>[] parameterTypes = handler.methed.getParameterTypes();
//保存所有需要自动赋值的参数值
Object[] paramValues = new Object[parameterTypes.length];
//客户端请求的参数map
Map params = req.getParameterMap();
for (Map.Entry entry : params.entrySet()) {
System.out.print("dispatch:param[]->"+Arrays.toString(entry.getValue()));
System.out.println();
String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");
if(!handler.paramIndexMapping.containsKey(entry.getKey())){ continue;}
//若找到匹配参数则开始填充参数值
Integer index = handler.paramIndexMapping.get(entry.getKey());
paramValues[index]=convert(parameterTypes[index],value);
}
//设置方法中的request和response
Integer reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex]=req;
Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex]=resp;
try {
handler.methed.invoke(handler.controller,paramValues);
} catch (Exception e) {
e.printStackTrace();
}
}
private void doLoadConfig(String contextConfigLocation){
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doScanner(String scanPackage){
System.out.println("scanPackage:"+scanPackage);
//文件路径转化为包路径
URL url= this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
System.out.printf("url:"+url.toString());
File classDir = new File(url.getFile());
for(File file:classDir.listFiles()){
//是文件夹
if(file.isDirectory()){
doScanner(scanPackage+"."+file.getName());
}else{
//存扫描到的类名
String className=scanPackage+"."+file.getName().replace(".class","");
classNames.add(className);
}
}
}
//实例化扫描到的类
private void doInstance() throws IllegalAccessException, InstantiationException {
if(classNames.isEmpty()){
return;
}
try {
for (String name : classNames) {
Class> clazz = Class.forName(name);
//不是所有的类都要初始化,只认加了注解的类
if(clazz.isAnnotationPresent(WLJController.class)){
//key默认类名首字母小写
String beanName = lowerFirstCase(clazz.getName());
ioc.put(beanName,clazz.newInstance());
}else if(clazz.isAnnotationPresent(WLJService.class)){
//1.默认采用首字母小写
//2.如果自定义了名字,优先使用自定义的名字
//3.根据接口类型来赋值
WLJService annotation = clazz.getAnnotation(WLJService.class);
String beanName = annotation.value();
//未定义自定义名字
if(!"".equals(beanName.trim())){
beanName=lowerFirstCase(beanName);
}
//某接口实现类的entry
ioc.put(beanName,clazz.newInstance());
//某接口的entry,不过其实例就是上面的实例
for(Class> i:clazz.getInterfaces()){
ioc.put(i.getName(),clazz.newInstance());
}
}else{
continue;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void doAutowired(){
if(ioc.isEmpty()){
return;
}
//循环ioc容器,对需要注入的属性进行注入
for (Map.Entry entry : ioc.entrySet()) {
//依赖注入,无论该属性的访问规则怎样,因为是反射
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if(!field.isAnnotationPresent(WLJAutowired.class)){
continue;
}
WLJAutowired autowired = field.getAnnotation(WLJAutowired.class);
String beanName=autowired.value().trim();
if("".equals(beanName)){
beanName=field.getType().getName();
}
//暴力访问(只要加了注解,我就访问)
field.setAccessible(true);
try {
//field.set的第二个参数为: entry.getValue()set方法的参数
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
}
private void initHandlerMapping(){
if(ioc.isEmpty()){
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz=entry.getValue().getClass();
if(!clazz.isAnnotationPresent(WLJController.class)){
continue;
}
String baseUrl="";
if(clazz.isAnnotationPresent(WLJRequestMapping.class)){
WLJRequestMapping annotation = clazz.getAnnotation(WLJRequestMapping.class);
baseUrl=annotation.value().trim();
}
//扫描所有公共方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(WLJRequestMapping.class)){
continue;
}
WLJRequestMapping annotation = method.getAnnotation(WLJRequestMapping.class);
String regex=("/"+baseUrl+annotation.value()).replaceAll("/+","/");
Pattern compile = Pattern.compile(regex);
handlerMapping.add(new Handler(entry.getValue(),method,compile));
System.out.println("Mapping:"+regex+","+method);
}
}
}
private String lowerFirstCase(String str){
char[] chars = str.toCharArray();
chars[0]+=32;
return String.valueOf(chars);
}
@Override
public void init(ServletConfig config){
//从这里启动
//1.加载配置文件
System.out.println("contextConfigLocation:"+config.getInitParameter("contextConfigLocation"));
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2.扫描所有相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//3.初始化所有相关的类
try {
doInstance();
} catch (Exception e) {
e.printStackTrace();
}
//4.自动注入
doAutowired();
//================以下mvc内容======================
//5.初始化handlerMapping
initHandlerMapping();
System.out.println("WLJ Spring init completed..");
}
private Handler getHandler(HttpServletRequest req){
if(handlerMapping.isEmpty()){
return null;
}
String url=req.getRequestURI();
String contextPath = req.getContextPath();
url=url.replace(contextPath,"").replaceAll("/+","/");
for (Handler handler : handlerMapping) {
Matcher matcher = handler.pattern.matcher(url);
//没有匹配成功,下一个
if(!matcher.matches()){
continue;
}
return handler;
}
return null;
}
private Object convert(Class> type,String value){
if(type==Integer.class){
return Integer.valueOf(value);
}
return value;
}
private class Handler{
protected Object controller; //保存方法对应的实例
protected Method methed; //保存映射的方法
protected Pattern pattern;
protected Map paramIndexMapping; //参数顺序
protected Handler(Object controller, Method methed, Pattern pattern) {
this.controller=controller;
this.methed = methed;
this.pattern = pattern;
this.paramIndexMapping=new HashMap<>();
putParamIndexMapping(methed);
}
private void putParamIndexMapping(Method methed){
//提取方法中加了注解的参数
//为什么是二维数组呢?因为每个参数可以有多个注解修饰
Annotation[][] params = methed.getParameterAnnotations();
//params.length=3 虽然前两个参数没有注解修饰,但是他们的params[i]无元素
//params[0][]空 params[1][]空 params[2][0]=WLJRequestParam
for (int i=0;ifor (Annotation a : params[i]) {
if(a instanceof WLJRequestParam){
String paramName = ((WLJRequestParam) a).value();
if(!"".equals(paramName.trim())){
paramIndexMapping.put(paramName,i);
}
}
}
}
Class>[] parameterTypes = methed.getParameterTypes();
for(int i=0;i type = parameterTypes[i];
if(type==HttpServletRequest.class||type==HttpServletResponse.class){
paramIndexMapping.put(type.getName(),i);
}
}
}
}
}
5.在WEB-INF的web.xml中配置该servlet:
(红色字样不用管)
在Resources目录下新建application.properties文件:
文件内容只有 :
scanPackage=com.wlj.demo
6.自定义注解
@WLJAutowired:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJAutowired {
String value() default "";
}
@WLJController:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJController {
String value() default "";
}
@WLJRequestMapping:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestMapping {
String value() default "";
}
@WLJRequestParam:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestParam {
String value() default "";
}
@WLJService:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJService {
String value() default "";
}
==================至此结束,框架代码结束=========================
业务代码:
在action目录下新建DemoAction类:
@WLJController
@WLJRequestMapping("/demo")
public class DemoAction {
@WLJAutowired
private IDemoService demoService;
@WLJRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp,@WLJRequestParam("name") String name){
String res=demoService.get(name);
try {
resp.getWriter().write(res);
}catch (IOException e){
e.printStackTrace();
}
}
}
新建service接口以及实现类:
public interface IDemoService {
String get(String name);
}
@WLJService
public class DemoServce implements IDemoService {
@Override
public String get(String name) {
return "My Name is "+name;
}
}
================================================
启动jetty容器
OK,Mission Complete!!!!!!!!!!!!