github 地址 :https://github.com/yjy91913/jerry-mvcframework
只是闲来无事写的简化版,仅供大家理解SpringMvc的运作原理)
了解了springMVC的源码,写一个功能简单可以实现的springMVC,只是为了深入了解spring
加入依赖 -servlet
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.0.1version>
dependency>
<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_3_0.xsd"
metadata-complete="true" version="3.0">
<display-name>
Jerry web application
display-name>
<servlet>
<servlet-name>jmvcservlet-name>
<servlet-class>com.jerry.mvcframework.servlet.JDispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>application.propertiesparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>jmvcservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
web-app>
这一步简化了,只有读取扫描包的位置
resources 目录下建立一个配置文件application.properties
scanPackage=com.jerry.demo
这个只写一下常用的注解
@Target({ElementType.FIELD}) //字段、枚举的常量
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在javadoc中
public @interface JAutowired {
String value() default "";
}
@Target({ElementType.TYPE}) //接口、类、枚举、注解
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在javadoc中
public @interface JController {
String value() default "";
}
@Target({ElementType.METHOD,ElementType.TYPE}) //字段、枚举的常量 方法
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在javadoc中
public @interface JRequestMapping {
String value() default "";
}
@Target({ElementType.PARAMETER}) //方法参数
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在javadoc中
public @interface JRequestParam {
String value() default "";
}
@Target({ElementType.TYPE}) //接口、类、枚举、注解
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented //说明该注解将被包含在javadoc中
public @interface JService {
String value() default "";
}
我们所要的做的事情,就是实现这个类
原声Servlet主要方法我们要处理就两个 一个:init doPost doGet 后面两个其实是一个方法
主要需要实现的功能都在注释里
@Override
public void init(ServletConfig config) throws ServletException {
//1.加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2.扫描所有的相关的类
doScanner(properties.getProperty("scanPackage"));
//3.初始化所有相关的类Class的实例,并且将其保存到IOC容器
doInstance();
//4.自动化的依赖注入
doAutowired();
//5.初始化HandlerMapping
initHandlerMapping();
System.out.println("Jerry MVC Framework is init");
}
private void doLoadConfig(String configLocation){
//获取一个inputStream
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
//加载配置文件
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这部是扫描我们指定的目录
//初始化一个List,用于存放.class的地址
private List classNames = new ArrayList();
private void doScanner(String packageName){
URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
//获取所有的字节码文件
File classDir = new File(url.getFile());
for (File file : classDir.listFiles()) {
//如果是文件夹递归执行
if(file.isDirectory()){
doScanner(packageName + "." + file.getName());
} else {
//输入是.class,加入list
classNames.add(packageName + "." + file.getName().replace(".class",""));
}
}
}
初始化所有相关的类Class的实例,并且将其保存到IOC容器
//初始化ioc容器
private Map<String,Object> ioc = new HashMap<String, Object>();
//写一个讲第一个字母变成小写的方法
private String lowerFirst(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
//编写doInstance()
private void doInstance(){
if(classNames.isEmpty()){
return;
}
for (String className : classNames) {
try {
Class> clazz = Class.forName(className);
//进行实例化
//判断,是否有Controller等注解
if(clazz.isAnnotationPresent(JController.class)){
String beanName = lowerFirst(clazz.getSimpleName());
ioc.put(beanName,clazz.newInstance());
}else if (clazz.isAnnotationPresent(JService.class)){
JService jService = clazz.getAnnotation(JService.class);
String beanName = jService.value();
//1.默认采用类名首字母小写 beanId
//2.如果自定义名字,优先使用自己定义的名字
if("".equals(beanName.trim())){
beanName = lowerFirst(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName,instance);
//3.根据类型匹配,利用实现类的接口名字作为key,实现类的类做为value
Class>[] interfaces = clazz.getInterfaces();
for (Class> i : interfaces) {
ioc.put(lowerFirst(i.getSimpleName()),instance);
}
}else {
continue;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
自动化的依赖注入,扫描所有带autowired注解
private void doAutowired(){
if(ioc.isEmpty()){
return;
}
for (Map.Entry entry : ioc.entrySet()) {
//在Spring中,没有隐私
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
//找autowired
if(!field.isAnnotationPresent(JAutowired.class)){
continue;
}
JAutowired jAutowired = field.getAnnotation(JAutowired.class);
String beanName = jAutowired.value().trim();
if("".equals(beanName)){
beanName = lowerFirst(field.getType().getSimpleName());
}
//暴力反射
field.setAccessible(true);
try {
field.set(entry.getValue(),ioc.get(beanName));
System.out.println(entry.getValue()+" is autowired ,object is "+ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
初始化一个HandlerMapping
private Map<String,Handler> handlerMapping = new HashMap<String, Handler>();
写一个handler类
@Data
@ToString
private class Handler {
protected Object controller;
protected Method method;
protected Pattern pattern;
protected Map paramIndexMapping;
}
编写initHandlerMapping()方法
private void initHandlerMapping() {
if(ioc.isEmpty()){
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
//HandlerMapping 只认JController
if(!clazz.isAnnotationPresent(JController.class)){
continue;
}
String url = "";
if(clazz.isAnnotationPresent(JRequestMapping.class)){
JRequestMapping jRequestMapping = clazz.getAnnotation(JRequestMapping.class);
url = jRequestMapping.value().trim();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//如果没有JRequestMapping ,直接跳过
if(!method.isAnnotationPresent(JRequestMapping.class)){
continue;
}
JRequestMapping jRequestMapping = method.getAnnotation(JRequestMapping.class);
String murl = url + jRequestMapping.value().trim();
Handler handler = new Handler();
handler.setController(entry.getValue());
handler.setMethod(method);
handlerMapping.put(murl,handler);
System.out.println("Mapping : "+ murl + " " +handler);
}
}
}
到这里springMVC初始化方法已经写完了,下面就是
doDispatch()方法了 ,下次在写