自己动手实现springmvc
IOC功能:就是一个hashmap里面放着各种类,俗称bean
Tomcat:tomcat启动时将需要要用到的类放到IOC容器里面去,就是那些带注解的(Controller,Autowired,Service,RequestMapping)
注解:就是自己定义几个注解,包括什么时候用(在tomcat启动时)
本节内容有一小部分内容没有实现,对于spring和springmvc源码有个大概了解
开始动手实现springmvc
一.项目结构:
不用解释了,很简单了
然后是pom文件,放了一个httpservlet的依赖
<dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
然后是web.xml配置,定义DispatcherServlet(自己定义的,不是spring自带的那个)最先初始化
web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>com.servlet.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>
二.自定义注解
首先看哪EnjoyController,模仿@Controller这个注解
package com.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE) //注解作用范围,比如:字段上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyController {
String value() default "";//注解后面的那个括号里可以为空
}
EnjoyService,模仿@Service
package com.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE) //注解作用范围,比如:类
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyService {
String value() default "";//注解后面的那个括号里可以为空
}
EnjoyRequestMapping
package com.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD}) //注解作用范围,比如:类,方法上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyRequestMapping {
String value() default "";//注解后面的那个括号里可以为空
}
EnjoyRequestParam
package com.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD}) //注解作用范围,比如:类,方法上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyRequestMapping {
String value() default "";//注解后面的那个括号里可以为空
}
EnjoyAutowired
package com.annotation;
import java.lang.annotation.*;
@Target(ElementType.FIELD) //注解作用范围,比如:字段上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyAutowired {
String value() default "";//注解后面的那个括号里可以为空
}
三.Controller
就这一个controller
package com.controller;
import com.annotation.EnjoyAutowired;
import com.annotation.EnjoyController;
import com.annotation.EnjoyRequestMapping;
import com.annotation.EnjoyRequestParam;
import com.service.ZhangjinheService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@EnjoyController
@EnjoyRequestMapping("/zjh")
public class ZhangjinheController {
@EnjoyAutowired("ZhangjinheServiceImpl")
private ZhangjinheService zhangjinheService;
@EnjoyRequestMapping("/query")
public void query(HttpServletRequest request, HttpServletResponse response,
@EnjoyRequestParam("name") String name, @EnjoyRequestParam("age") String age){
PrintWriter ps = null;
try {
ps = response.getWriter();
String result = zhangjinheService.query(name,age);
ps.write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
就实现了一个query函数,功能时向页面上输出name和age
包含了一个service,里面用的自定义注解,@EnjoyAutowired
四.Service类
一个接口,一个实现类
package com.service.impl;
import com.annotation.EnjoyService;
import com.service.ZhangjinheService;
@EnjoyService("ZhangjinheServiceImpl")
public class ZhangjinheServiceImpl implements ZhangjinheService{
@Override
public String query(String name, String age) {
return "name==" + name + ";age==" + age;
}
}
五.Servlet类
模仿了一个DispatcherServlet(springmvc里面的那个)
这个类里面主要有这么几个方法:都放在servlet的init函数里面其运行
//扫描带注解的包
doScan("com");
doInstance();
//实现autowired
doAutowired();
urlMapping();
doScan()主要是扫描带注解的包,spring在配置的时候也有一个扫描包的选项
doInstance()将那些带注解的包都new出实例,放在一个map里面
doAutowired()将Service里面的那个字段赋值,相当于spring里面的@autowired,主要是将new出来的实例赋值给对应的字段
urlMapping()主要是负责将从浏览器里面输入的网址对应成controller里面的方法
package com.servlet;
import com.annotation.EnjoyAutowired;
import com.annotation.EnjoyController;
import com.annotation.EnjoyRequestMapping;
import com.annotation.EnjoyService;
import com.controller.ZhangjinheController;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DispatcherServlet extends HttpServlet {
List<String> classNames = new ArrayList<>();
Map<String, Object> beans = new HashMap<>();
Map<String, Object> handlerMap = new HashMap<>();
//tomcat启动时要执行的内容
public void init(ServletConfig servletConfig){
//扫描带注解的包
doScan("com");
doInstance();
//实现autowired
doAutowired();
urlMapping();
}
public void urlMapping() {
for(Map.Entry<String, Object> entry : beans.entrySet()){
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if(clazz.isAnnotationPresent(EnjoyController.class)){
EnjoyRequestMapping reqMap = clazz.getAnnotation(EnjoyRequestMapping.class);
String classPath = reqMap.value();
Method[] methods = clazz.getMethods();
for (Method method : methods){
if(method.isAnnotationPresent(EnjoyRequestMapping.class)){
EnjoyRequestMapping reqMap1 = method.getAnnotation(EnjoyRequestMapping.class);
String methodPath = reqMap1.value();
handlerMap.put(classPath + methodPath, method);
}else{
continue;
}
}
}
}
}
public void doAutowired() {
for(Map.Entry<String, Object> entry : beans.entrySet()){
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if(clazz.isAnnotationPresent(EnjoyController.class)){
Field[] fields= clazz.getDeclaredFields();
for (Field field : fields){
if(field.isAnnotationPresent(EnjoyAutowired.class)){
EnjoyAutowired auto = field.getAnnotation(EnjoyAutowired.class);
String key = auto.value();
Object value = beans.get(key);
//因为当前变量是私有的,不能随便摄入,所以需要打开
field.setAccessible(true);
try {
field.set(key, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
public void doInstance() {
for(String className: classNames){
String cn = className.replace(".class", "");
try {
Class<?> clazz = Class.forName(cn);
if(clazz.isAnnotationPresent(EnjoyController.class)){
Object value = clazz.newInstance();
//获得类上的注解
EnjoyRequestMapping reqMap = clazz.getAnnotation(EnjoyRequestMapping.class);
String key = reqMap.value();
beans.put(key, value);
}else if(clazz.isAnnotationPresent(EnjoyService.class)){
Object value = clazz.newInstance();
EnjoyService reqMap = clazz.getAnnotation(EnjoyService.class);
String key = reqMap.value();
beans.put(key,value);
}else {
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public void doScan(String basePackage) {
URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.","/"));
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(basePackage + "." + path);
}else{
classNames.add(basePackage + "." + filePath.getName());
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();// self-spirngmvc/zjh/query
String context = req.getContextPath();//self-springmvc
String path = uri.replace(context, "");
Method method = (Method)handlerMap.get(path);
ZhangjinheController instance = (ZhangjinheController)beans.get("/" + path.split("/")[1]);
Object[] args;
method.invoke(instance, args);
}
}
这里有个方法没有实现,就是那个args[]