思路
说明
* git地址: 文章目录中置顶的git文章中;
* springMVC简易版, 只是为了通过简单实现更加了解Spring体系, 功能不完备, 存在细节bug.
配置阶段
* 创建DispatcherServlet类:
* 所有请求都会访问该serlvet;
* 继承HttpServlet;
* 配置DispatcherServlet:
* web.xml中配置;
* 拦截所有请求;
* 添加变量: 配置文件地址;
* 随容器初始化;
* 配置文件:
* 配置bean目录;
* 初始化IOC容器时, 使用该目录获取所有bean;
* 编写annotation:
* @Controller, @Service, @Autowired, @RequestMapping, @RequestParams;
初始化阶段
* 装载配置文件;
* 获取需要初始化bean的目录;
* class容器:
* Map格式;
* key = class.getName; value = null;
* 这里应该使用list, 由开始设计时没考虑周全, 所以这里我使用了map
* url容器
* Map格式;
* key: request请求的url;
* value: 对应执行的Method;
* bean容器
* key: 接口路径 / class路径 / @Service自定义的名称
* value: Object
* 遍历bean目录;
* 装载目录中所有java类全名;
* 遍历class容器
* 实例化所有@Controller和@Service注解的类:
* 装载:
* 装载: 类名-Object
* 如果该类实现了接口, 另外装载: 接口名-Object;
* 如果该类使用了@Service注解并存在注解值, 另外装载: 注解值-Object
* 遍历bean容器;
* 当类为@Controller注解修时:
* 获取所有方法;
* 当方法被@RequestMapping修饰时, 添加到url容器中;
* key: @Contruller中@RequestMapping + 方法上@RequestMapping;
* value: Method
* 当bean中有@Autowired修饰的变量时:
* 获取@Autowired的值A;
* 从bean容器中, 通过值A获取到对象;
* 为bean中变量赋值;
运行阶段
* 获取request请求的url;
* 从url容器中,获取到对应Method;
* 为Method添加参数:
* 从request参数中, 找到@RequestParam修饰的对应参数;
* 执行Method;
* Object : bean容器中的Object;
* Method : url容器中的mehtod;
代码结构:
配置阶段
创建Dispatcher类型
public class BZDispatcherServlet extends HttpServlet {
}
配置DispatcherServlet
* web.xml
<servlet>
<servlet-name>BZDispatcherServlet</servlet-name>
<servlet-class>mvcFramework.dispatcherServlet.BZDispatcherServlet</servlet-class>
<init-param>
<!-- 配置文件地址 -->
<param-name>applicationContext</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>BZDispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
配置bean目录
* 在properties中, 配置bean目录;
scanPackage=mvc
编写annotation
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZController {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZService {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZAutowired {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZRequestMapping {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZRequestParam {
String value() default "";
}
初始化阶段
覆盖init()方法
Map<String, Object> BeanMap = new HashMap();
Map<String, Object> URLMap = new HashMap();
Map<String, Object> classMap = new HashMap();
@Override
public void init(ServletConfig config) {
System.out.println("bz dispatcher start!");
try {
//获取bean目录;
String scanPackage = getScanPackage(config);
System.out.println("获取到bean目录: " + scanPackage);
//装配IOC容器 及 url 映射
initIOC(scanPackage);
//装配bean
fitBean();
System.out.println("bz dispatcher init finish!");
} catch (Exception e) {
e.printStackTrace();
}
}
获取bean目录
private String getScanPackage(ServletConfig config) throws Exception {
Properties properties = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("applicationContext"));
if (is == null) {
throw new RuntimeException("Bean 目录不存在!");
}
properties.load(is);
String scanPackage = properties.getProperty("scanPackage");
if (scanPackage == null || scanPackage.equals("")) {
throw new RuntimeException("scanPackage 配置信息不存在!");
}
return scanPackage;
}
装配IOC容器
private void initIOC(String scanPackage) throws Exception {
//初始化classMap
initClassMap(scanPackage);
//初始化IOC容器
initIOCMap();
}
private void initClassMap(String scanPackage) {
//获取目录
scanPackage = scanPackage.replaceAll("\\.", "/");
URL url = this.getClass().getClassLoader().getResource(scanPackage);
if (url == null) {
System.out.println("bean 目录中无文件!");
return;
}
File rootFile = new File(url.getFile());
for (File file : rootFile.listFiles()) {
if (file.isDirectory()) { //递归目录
initClassMap(scanPackage + "/" +file.getName());
}
if (!file.getName().endsWith(".class")) {
continue;
}
String className = scanPackage + "/" +file.getName().replace(".class", "");
classMap.put(className.replaceAll("/","."), null);
}
}
private void initIOCMap() throws Exception {
for (String className : classMap.keySet()) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(BZController.class)) {
// 添加url映射
initURLMap(clazz);
//把controller添加到bean
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
BeanMap.put(clazz.getName(),constructor.newInstance());
} else if (clazz.isAnnotationPresent(BZService.class)) {
initBeanMap(clazz);
}
}
}
private void initURLMap(Class<?> clazz) {
String controllerURL = "";
if (clazz.isAnnotationPresent(BZRequestMapping.class)) {
controllerURL = clazz.getAnnotation(BZRequestMapping.class).value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(BZRequestMapping.class)) {
continue;
}
BZRequestMapping bzRequestMapping = method.getAnnotation(BZRequestMapping.class);
String url = controllerURL + "/" +bzRequestMapping.value();
url = url.replaceAll("/+", "/");
if(URLMap.containsKey(url)){
throw new RuntimeException("URLMap重复!!" + url);
}
URLMap.put(url, method);
}
}
private void initBeanMap(Class<?> clazz) throws Exception {
BZService bzService = clazz.getAnnotation(BZService.class);
String value = bzService.value();
//创建对象
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object obj = constructor.newInstance();
if (value != null && !value.equals("")) {
if(BeanMap.containsKey(value)){
throw new RuntimeException("BeanMap重复! " + value);
}
BeanMap.put(value, obj);
}
BeanMap.put(clazz.getName(), obj);
//获取接口
Class<?>[] clazzs = clazz.getInterfaces();
//BeanMap.put(initClassNameToBeanName(clazz.getName()), obj);
if (clazzs.length == 1) {
if(BeanMap.containsKey(clazzs[0].getName())){
throw new RuntimeException("BeanMap重复! " + clazzs[0].getName());
}
BeanMap.put(clazzs[0].getName(), obj);
} else if (clazzs.length > 1) {
throw new RuntimeException("Service 最多继承一个接口!");
}
}
private String initClassNameToBeanName(String name) {
String [] str = name.split("\\.");
return str[str.length-1];
}
装配Bean
private void fitBean() throws Exception {
for (String clazzName : classMap.keySet()) {
Class clazz = Class.forName(clazzName);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(BZAutowired.class)) {
continue;
}
Constructor targetCon = clazz.getDeclaredConstructor();
targetCon.setAccessible(true);
Object targetObject = targetCon.newInstance();
BZAutowired bzAutowired = field.getAnnotation(BZAutowired.class);
String value = bzAutowired.value();
field.setAccessible(true);
if (value != null && !value.equals("")) {
Object obj = BeanMap.get(value);
if(obj == null){
throw new RuntimeException("Bean 不存在: " + value);
}
field.set(targetObject, obj);
} else {
Type type = field.getGenericType();
Object obj = BeanMap.get(type.getTypeName());
if(obj == null){
throw new RuntimeException("Bean 不存在" + type.getTypeName());
}
field.set(targetObject, obj);
}
}
}
}
重写doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatcher(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//获取url
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
if(!URLMap.containsKey(url)){
resp.getWriter().write("404 Not Found!!");return;
}
Method method = (Method) URLMap.get(url);
Map<String,String[]> params = req.getParameterMap();
//获取方法的形参列表
Class<?> [] parameterTypes = method.getParameterTypes();
Object [] paramValues = new Object[parameterTypes.length];
Parameter[] methodParams = method.getParameters();
for (int i = 0; i < parameterTypes.length; i ++) {
Class parameterType = parameterTypes[i];
//不能用instanceof,parameterType它不是实参,而是形参
if(parameterType == HttpServletRequest.class){
paramValues[i] = req;
continue;
}else if(parameterType == HttpServletResponse.class){
paramValues[i] = resp;
continue;
}else if(parameterType == String.class){
BZRequestParam bzRequestParam = methodParams[i].getAnnotation(BZRequestParam.class);
if(params.containsKey(bzRequestParam.value())){
for (Map.Entry<String,String[]> param : params.entrySet()){
if(!param.getKey().equals(bzRequestParam.value())){
continue;
}
String value = Arrays.toString(param.getValue())
.replaceAll("\\[|\\]","")
.replaceAll("\\s",",");
paramValues[i] = value;
}
}
}
}
method.invoke(BeanMap.get(method.getDeclaringClass().getName()),paramValues);
}
编写service和controller
congroller
@BZController
@BZRequestMapping("/buzzered")
public class DemoController {
@BZAutowired
DemoService1 ds1;
@BZAutowired
IService is;
@BZRequestMapping("mvcDemo")
public void demo(HttpServletRequest req, HttpServletResponse resp, @BZRequestParam("name")String name, @BZRequestParam("age")String age) throws Exception {
resp.getWriter().write("My name is " + name + " age: " + age);
}
}
service
public interface IService {
public void demo();
}
@BZService
public class IServiceImpl implements IService {
@Override
public void demo() {
System.out.println("demo demo demo");
}
}
@BZService("DemoService")
public class DemoService {
public void demo(){
System.out.println("demo demo demo");
}
}
@BZService
public class DemoService1 {
@BZAutowired("DemoService")
DemoService ds;
public void demo(){
ds.demo();
}
}
部署并访问