手写简单版spring mvc
准备工作搭建框架注解文件service和controller层
浇灌创建自己的DispatcherServlet类扫包,获取类名,都有哪些类扫class,通过上面注解MyController,MyService实例化,实例化bean扫bean实例,获得class,根据MyAutowired往类中射入所需(成员)变量将url中的地址映射到方法重写doPost方法,将请求对应相应的方法
自己写
最近要找工作了回忆一下spring,写了一个简易版的spring mvc。代码都上传到了github,请大家下载点星,感谢!!
https://github.com/kangwenzhuang/Springmvc
个人理解,观点可能片面,欢迎在评论区喷我,这样我才能成长 适合新手,高手请绕开
准备工作
maven项目 pom.xml
<dependency>
<groupId>javax
.servlet
</groupId
>
<artifactId>javax
.servlet
-api
</artifactId
>
<version>4.0.1</version
>
<scope>provided
</scope
>
</dependency
>
既然是手写mvc那就不引入spring包,这里不手写servlet,而是直接添加依赖 需要涉及的知识:web编程,反射,注解
搭建框架
注解文件
1.MyAutowired.java,作用在类的成员变量上面,实现注入的标识
package com
.kang
.annotation
;
import java
.lang
.annotation
.ElementType
;
import java
.lang
.annotation
.Retention
;
import java
.lang
.annotation
.RetentionPolicy
;
import java
.lang
.annotation
.Target
;
@Target(ElementType
.FIELD
)
@Retention(RetentionPolicy
.RUNTIME
)
public @
interface MyAutowired {
String
value() default "";
}
2.MyController.java作用在类上面,作为bean的标识
package com
.kang
.annotation
;
import java
.lang
.annotation
.ElementType
;
import java
.lang
.annotation
.Retention
;
import java
.lang
.annotation
.RetentionPolicy
;
import java
.lang
.annotation
.Target
;
@Target(ElementType
.TYPE
)
@Retention(RetentionPolicy
.RUNTIME
)
public @
interface MyController {
String
value() default "";
}
3.MyService.java作用在类上面,作为bean的标识
package com
.kang
.annotation
;
import java
.lang
.annotation
.ElementType
;
import java
.lang
.annotation
.Retention
;
import java
.lang
.annotation
.RetentionPolicy
;
import java
.lang
.annotation
.Target
;
@Target(ElementType
.TYPE
)
@Retention(RetentionPolicy
.RUNTIME
)
public @
interface MyService {
String
value() default "";
}
4.MyRequestMapping.java作用在控制层的类和方法上,作为映射的地址
package com
.kang
.annotation
;
import java
.lang
.annotation
.ElementType
;
import java
.lang
.annotation
.Retention
;
import java
.lang
.annotation
.RetentionPolicy
;
import java
.lang
.annotation
.Target
;
@Target({ElementType
.TYPE
, ElementType
.METHOD
})
@Retention(RetentionPolicy
.RUNTIME
)
public @
interface MyRequestMapping {
String
value() default "";
}
5.MyRequestParam.java作用在控制层方法上的参数,作为请求的参数
package com
.kang
.annotation
;
import java
.lang
.annotation
.ElementType
;
import java
.lang
.annotation
.Retention
;
import java
.lang
.annotation
.RetentionPolicy
;
import java
.lang
.annotation
.Target
;
@Target(ElementType
.PARAMETER
)
@Retention(RetentionPolicy
.RUNTIME
)
public @
interface MyRequestParam {
String
value() default "";
}
service和controller层
1.创建service接口
package com
.kang
.service
;
public interface UserService {
String
result(String name
,String age
);
}
2.实现service接口
package com
.kang
.service
.serviceImpl
;
import com
.kang
.annotation
.MyService
;
import com
.kang
.service
.UserService
;
@MyService("userService")
public class UserServiceImpl implements UserService {
public String
result(String name
, String age
) {
return "name:" + name
+ '\n' + "age:" + age
;
}
}
3.控制层
package com
.kang
.controller
;
import com
.kang
.annotation
.MyAutowired
;
import com
.kang
.annotation
.MyController
;
import com
.kang
.annotation
.MyRequestMapping
;
import com
.kang
.annotation
.MyRequestParam
;
import com
.kang
.service
.UserService
;
import javax
.servlet
.http
.HttpServletRequest
;
import javax
.servlet
.http
.HttpServletResponse
;
import java
.io
.IOException
;
import java
.io
.PrintWriter
;
@MyRequestMapping("/user")
@MyController("userController")
public class UserController {
@MyAutowired("userService")
private UserService userService
;
@MyRequestMapping("/hello")
public void result(HttpServletRequest request
, HttpServletResponse response
, @MyRequestParam("name") String name
, @MyRequestParam("age") String age
) {
String result
= userService
.result(name
, age
);
try {
PrintWriter pw
=response
.getWriter();
pw
.write(result
);
} catch (IOException e
) {
e
.printStackTrace();
}
}
}
这样就可以了,当然不行啊,这只是搭建了一个框架,空壳子一个,不能用的
浇灌
springmvc也只是一个web项目,肯定要有web.xml,通过url进行访问获取 web.xml
<!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
>DispatcherServlet
</servlet
-name
>
<servlet
-class>com
.kang
.DispatcherServlet
</servlet
-class>
<load
-on
-startup
>0</load
-on
-startup
>
</servlet
>
<servlet
-mapping
>
<servlet
-name
>DispatcherServlet
</servlet
-name
>
<url
-pattern
>/</url
-pattern
>
</servlet
-mapping
>
</web
-app
>
创建自己的DispatcherServlet类
package com
.kang
;
public class DispatcherServlet extends HttpServlet {
public void init(ServletConfig config
) {
}
protected void doGet(HttpServletRequest req
, HttpServletResponse res
) {
this.doPost(req
, res
);
}
protected void doPost(HttpServletRequest req
, HttpServletResponse res
) {
}
}
扫包,获取类名,都有哪些类
List
<String> classNames
= new ArrayList<String>();
void doScan(String packageName
) {
String pn
=packageName
;
URL url
= this.getClass().getClassLoader().getResource(packageName
.replace(".", "/"));
String fileStr
= url
.getFile();
File file
= new File(fileStr
);
String
[] filesStr
= file
.list();
for (String path
: filesStr
) {
File filePath
= new File(fileStr
+ path
);
if (filePath
.isDirectory()) {
doScan((packageName
+ "." + path
));
} else {
classNames
.add(packageName
+ "." + filePath
.getName());
}
}
}
我们获取包的方式受到springboot中启发,直接扫描DispatcherServlet 根目录 String packageName = this.getClass().getPackage().getName();
扫class,通过上面注解MyController,MyService实例化,实例化bean
HashMap
<String, Object> beans
= new HashMap<String, Object>();
void doInstance() {
for (String className
: classNames
) {
String cn
= className
.replace(".class", "");
try {
Class
<?> clazz
= Class
.forName(cn
);
if (clazz
.isAnnotationPresent(MyController
.class)) {
Object instance
= clazz
.newInstance();
MyController mc
= clazz
.getAnnotation(MyController
.class);
String key
= mc
.value();
beans
.put(key
, instance
);
} else if (clazz
.isAnnotationPresent(MyService
.class)) {
Object instance
= clazz
.newInstance();
MyService ms
= clazz
.getAnnotation(MyService
.class);
String key
= ms
.value();
beans
.put(key
, instance
);
} else {
continue;
}
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException e
) {
e
.printStackTrace();
}
}
}
扫bean实例,获得class,根据MyAutowired往类中射入所需(成员)变量
void doAutowired() {
for (Map
.Entry
<String, Object> m
: beans
.entrySet()) {
Class
<?> clazz
= m
.getValue().getClass();
if (clazz
.isAnnotationPresent(MyController
.class)) {
Field
[] fields
= clazz
.getDeclaredFields();
for (Field field
: fields
) {
if (field
.isAnnotationPresent(MyAutowired
.class)) {
MyAutowired ma
= field
.getAnnotation(MyAutowired
.class);
String key
= ma
.value();
Object object
= beans
.get(key
);
field
.setAccessible(true);
try {
field
.set(m
.getValue(), object
);
} catch (IllegalAccessException e
) {
e
.printStackTrace();
}
} else {
continue;
}
}
} else {
continue;
}
}
}
将url中的地址映射到方法
HashMap
<String, Object> handlerMaps
= new HashMap<String, Object>();
void urlHanding() {
for (Map
.Entry
<String, Object> m
: beans
.entrySet()) {
Object object
= m
.getValue();
Class
<?> clazz
= object
.getClass();
if (clazz
.isAnnotationPresent(MyController
.class)) {
MyRequestMapping mr
= clazz
.getAnnotation(MyRequestMapping
.class);
String path1
= mr
.value();
Method
[] methods
= clazz
.getMethods();
for (Method mt
: methods
) {
if (mt
.isAnnotationPresent(MyRequestMapping
.class)) {
MyRequestMapping q
= mt
.getAnnotation(MyRequestMapping
.class);
String path2
= q
.value();
handlerMaps
.put(path1
+ path2
+"/", mt
);
} else {
continue;
}
}
}
}
}
重写doPost方法,将请求对应相应的方法
protected void doPost(HttpServletRequest req
, HttpServletResponse res
) {
String uri
= req
.getRequestURI();
Method method
= (Method
) handlerMaps
.get(uri
);
Object object
= null
;
Here
:
for (Map
.Entry
<String, Object> map
: beans
.entrySet()) {
Class
<?> clazz
= map
.getValue().getClass();
if (clazz
.isAnnotationPresent(MyController
.class)) {
if (clazz
.isAnnotationPresent(MyRequestMapping
.class)) {
if (clazz
.getAnnotation(MyRequestMapping
.class).value().equals("/" + uri
.split("/")[1])) {
Method
[] methods
= clazz
.getMethods();
for (Method med
: methods
) {
if (med
.isAnnotationPresent(MyRequestMapping
.class)) {
MyRequestMapping ma
= med
.getAnnotation(MyRequestMapping
.class);
if (ma
.value().equals("/" + uri
.split("/")[2])) {
object
= map
.getValue();
break Here
;
}
} else {
continue;
}
}
}
}
}
}
try {
method
.invoke(object
, getArgs(req
,res
,method
));
} catch (IllegalAccessException e
) {
e
.printStackTrace();
} catch (InvocationTargetException e
) {
e
.printStackTrace();
}
}
Object
[] getArgs(HttpServletRequest req
, HttpServletResponse res
,Method method
){
Class
<?>[] paramClazzs
=method
.getParameterTypes();
Object
[] args
=new Object[paramClazzs
.length
];
int i
=0;
int index
=0;
for(Class
<?> paramclazz
:paramClazzs
){
if(ServletRequest
.class.isAssignableFrom(paramclazz
)){
args
[i
++]=req
;
}
if(ServletResponse
.class.isAssignableFrom(paramclazz
)){
args
[i
++]=res
;
}
Annotation
[] paramAns
=method
.getParameterAnnotations()[index
];
if(paramAns
.length
>0){
for(Annotation paramAn
:paramAns
){
if(MyRequestParam
.class.isAssignableFrom(paramAn
.getClass())){
MyRequestParam rp
=(MyRequestParam
) paramAn
;
args
[i
++]=req
.getParameter(rp
.value());
}
}
}
index
++;
}
return args
;
}
Done!!!
自己写
如果我想写一个Responsebody注解,返回json格式,那怎么办呢?这就交给你自己了 思路: 1.获取方法返回值 Object object=method.invoke(object, getArgs(req,res,method)); 2.使用阿里爸爸的fastjson包解析object转json,具体的自己完成 json=f(object); 3.设置res的返回格式 res.setCharacterEncoding(“utf-8”); res.setContentType(“application/json; charset=utf-8”); PrintWriter pw = resp.getWriter(); pw.write(json);