--------上一篇:手写MVC框架(一)-再出发-----
背景
书接上文,之前整理了实现MVC框架需要写哪些东西。这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成。周末的时间,哪会那么老实的坐在电脑前写代码呢?看电影的时候应该是老实的。为了不给自己留遗憾,所以今天就接着写了,然后就写完了。
一、主要代码结构
.
├── annotation
│ ├── XAutowired.java //用于依赖注入
│ ├── XComponent.java //资源管理
│ ├── XController.java //资源管理-controller
│ ├── XRepository.java //资源管理-资源层
│ ├── XRequestMapping.java //资源uri
│ └── XService.java //资源管理-service
├── bean
│ ├── EntityBean.java //存储实例化的资源
│ └── SystemConst.java
├── handler
│ └── XDispatcherServlet.java //核心调度类
├── mapper
│ ├── InstanceManager.java //资源实例管理
│ └── ServletMapper.java //请求路径-资源映射
└── util
├── ClazzUtil.java
├── CommonUtil.java
└── RequestResolveUtil.java
二、主要流程
1、服务启动,加载XDispatcherServlet
2、XDispatcherServlet初始化,调用InstanceManager进行对象初始化、依赖注入
3、调用ServletMapper扫描编写的各个URI
4、请求到达XDispatcherServlet时,通过ServletMapper匹配到对应的方法
5、执行匹配到的方法
三、InstanceManager实现
package com.shuimutong.gmvc.mapper; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set; import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections; import com.shuimutong.gmvc.annotation.XAutowired;
import com.shuimutong.gmvc.annotation.XComponent;
import com.shuimutong.gmvc.annotation.XController;
import com.shuimutong.gmvc.annotation.XRepository;
import com.shuimutong.gmvc.annotation.XService;
import com.shuimutong.gmvc.bean.EntityBean;
import com.shuimutong.gmvc.bean.SystemConst;
import com.shuimutong.gmvc.util.ClazzUtil; /**
* 实例管理
* @ClassName: InstanceManager
* @Description:(这里用一句话描述这个类的作用)
* @author: 水木桶
* @date: 2019年9月7日 下午10:08:27
* @Copyright: 2019 [水木桶] All rights reserved.
*/
public class InstanceManager {
/**被注解的类**/
private static Map<String, EntityBean> CLASS_ENTITY_MAP = new HashMap();
/**被XController注解的类**/
private static Set<EntityBean> CONTROLLER_CLASS_ENTITY_MAP = new HashSet(); /**
* 初始化
* @param conf
* @throws InstantiationException
* @throws IllegalAccessException
*/
public static void init(Map<String, String> conf) throws InstantiationException, IllegalAccessException {
String basePackageStr = conf.get(SystemConst.BASE_PACKAGE);
//扫描通过框架管理的资源
scanAnnotationedResources(basePackageStr);
//实例化通过框架管理的资源
generateAnnotationedEntity();
} /**
* 获取controller类
* @return
*/
public static Set<EntityBean> getControllerClazzes() {
return CONTROLLER_CLASS_ENTITY_MAP;
} /**
* 根据类(被框架管理的类)获取对应的实例对象
* @param clazz
* @return
*/
public static EntityBean getEntityByClazz(Class clazz) {
String className = ClazzUtil.getClazzName(clazz);
return CLASS_ENTITY_MAP.get(className);
} /**
* 扫描需要框架管理的类
* @param basePackageStr
*/
private static void scanAnnotationedResources(String basePackageStr) {
if(StringUtils.isBlank(basePackageStr)) {
return;
}
String[] basePackages = basePackageStr.split(",");
Reflections reflections = new Reflections(basePackages);
Class<?>[] annotations = {XController.class, XService.class, XRepository.class, XComponent.class};
for(Class<?> annotation : annotations) {
Set<Class<?>> resourceClazzes = reflections
.getTypesAnnotatedWith((Class<? extends Annotation>) annotation);
for(Class<?> resourceClazz : resourceClazzes) {
String className = ClazzUtil.getClazzName(resourceClazz);
CLASS_ENTITY_MAP.put(className, new EntityBean(className, resourceClazz));
if(resourceClazz.isAnnotationPresent(XController.class)) {
CONTROLLER_CLASS_ENTITY_MAP.add(new EntityBean(className, resourceClazz));
}
}
}
} /**
* 对通过框架管理的类进行实例化
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static void generateAnnotationedEntity() throws InstantiationException, IllegalAccessException {
//先根据构造方法初始化bean
initBeanInstance(CLASS_ENTITY_MAP.values());
Set<String> clazzNames = CLASS_ENTITY_MAP.keySet();
for(String clazzName : clazzNames) {
EntityBean entityBean = CLASS_ENTITY_MAP.get(clazzName);
initBeanAutowired(entityBean);
}
} /**
* 初始化实例对象
* @param classEntityMap
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static void initBeanInstance(Collection<EntityBean> entityBeans) throws InstantiationException, IllegalAccessException {
for(EntityBean entityBean : entityBeans) {
if(entityBean.getO() == null) {
Class<?> destClazz = entityBean.getClazz();
entityBean.setO(destClazz.newInstance());
}
}
} /**
* 初始化bean中注入的类
* @param entityBean
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static void initBeanAutowired(EntityBean entityBean) throws IllegalArgumentException, IllegalAccessException, InstantiationException {
if(entityBean.isFullAutowired()) {
return;
}
Class<?> destClazz = entityBean.getClazz();
Field[] fields = destClazz.getDeclaredFields();
Object entityInstance = entityBean.getO();
Collection<EntityBean> entityBeans = CLASS_ENTITY_MAP.values();
for(Field field : fields) {
if(!field.isAnnotationPresent(XAutowired.class)) {
continue;
}
field.setAccessible(true);
Object fieldVal = field.get(entityInstance);
if(fieldVal != null) {
continue;
}
Class<?> fieldClazz = field.getType();
EntityBean relayEntity = getEntityByClazz(fieldClazz);
//依赖的对象能够直接查到
if(relayEntity != null) {
field.set(entityInstance, relayEntity.getO());
} else {
boolean find = false;
for(EntityBean otherEntityBean : entityBeans) {
//判断子类
if(fieldClazz.isAssignableFrom(otherEntityBean.getClazz())) {
field.set(entityInstance, otherEntityBean.getO());
find = true;
break;
}
}
if(!find) {
throw new IllegalArgumentException("autowiredEntityNotFoundException");
}
}
}
entityBean.setFullAutowired(true);
} }
四、ServletMapper实现
package com.shuimutong.gmvc.mapper; import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import com.shuimutong.gmvc.annotation.XRequestMapping;
import com.shuimutong.gmvc.bean.EntityBean;
import com.shuimutong.gmvc.util.CommonUtil;
import com.shuimutong.gutil.common.GUtilCommonUtil; /**
* servlet映射
* @ClassName: ServletMapper
* @Description:(这里用一句话描述这个类的作用)
* @author: 水木桶
* @date: 2019年9月7日 下午6:22:19
* @Copyright: 2019 [水木桶] All rights reserved.
*/
public class ServletMapper {
/**uri-method映射**/
private static Map<String, Method> URI_MAP = new HashMap(); public static void init() {
generateUriMap(InstanceManager.getControllerClazzes());
StringBuilder logSb = new StringBuilder("ServletMapper,scanUriPath:\n");
for(String uri : URI_MAP.keySet()) {
logSb.append(uri).append("\n");
}
logSb.append("\n").append("---scanUriPath-----end----");
System.out.println(logSb.toString());
} /**
* 生成uri-方法映射
* @param controllerClazz
*/
private static void generateUriMap(Set<EntityBean> controllerClazzBeans) {
if(GUtilCommonUtil.checkListEmpty(controllerClazzBeans)) {
return;
}
Class<? extends Annotation> requestMappingClazz = XRequestMapping.class;
for(EntityBean eb : controllerClazzBeans) {
Class<?> controllerClazz = eb.getClazz();
String rootUri = "";
if(controllerClazz.isAnnotationPresent(requestMappingClazz)) {
XRequestMapping xrm = (XRequestMapping) controllerClazz.getAnnotation(XRequestMapping.class);
rootUri = xrm.value();
}
Method[] methods = controllerClazz.getDeclaredMethods();
for(Method method : methods) {
if(method.isAnnotationPresent(requestMappingClazz)) {
XRequestMapping xrm = (XRequestMapping) method.getAnnotation(XRequestMapping.class);
String methodUri = xrm.value();
String fullUri = rootUri + "/" + methodUri;
URI_MAP.put(CommonUtil.formatUri(fullUri), method);
}
}
}
} /**
* 获取uri对应的方法
* @param uri
* @return
*/
public static Method getMethodByUri(String uri) {
return URI_MAP.get(uri);
} }
五、XDispatcherServlet-核心调度实现
package com.shuimutong.gmvc.handler; import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.shuimutong.gmvc.bean.EntityBean;
import com.shuimutong.gmvc.bean.SystemConst;
import com.shuimutong.gmvc.mapper.InstanceManager;
import com.shuimutong.gmvc.mapper.ServletMapper; /**
* 调度servlet
* @ClassName: XDispatcherServlet
* @Description:(这里用一句话描述这个类的作用)
* @author: 水木桶
* @date: 2019年9月8日 上午11:58:37
* @Copyright: 2019 [水木桶] All rights reserved.
*/
public class XDispatcherServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(XDispatcherServlet.class); @Override
public void init() throws ServletException {
super.init();
//获取ServletConfig对象
ServletConfig config = this.getServletConfig();
//根据参数名获取参数值
// String basePackage = config.getInitParameter(SystemConst.BASE_PACKAGE);
Map<String, String> confMap = new HashMap();
confMap.put(SystemConst.BASE_PACKAGE, config.getInitParameter(SystemConst.BASE_PACKAGE));
try {
InstanceManager.init(confMap);
} catch (Exception e) {
throw new ServletException(e);
}
ServletMapper.init();
} /**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
} /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("Hello");
String requestUri = request.getRequestURI().replace(request.getContextPath(), "");
// System.out.println("requestUri:" + requestUri);
Method resolveMethod = ServletMapper.getMethodByUri(requestUri);
EntityBean entityBean = InstanceManager.getEntityByClazz(resolveMethod.getDeclaringClass());
if(entityBean == null) {
throw new ServletException("uriNotFoundException");
}
try {
resolveMethod.invoke(entityBean.getO(), request, response);
} catch (Exception e) {
log.error("execute" + resolveMethod.getName() + "Exception", e);
throw new ServletException(e);
}
}
}
六、源码分享
gmvc:https://gitee.com/simpleha/gmvc.git
依赖:https://gitee.com/simpleha/gutil.git
七、使用示例
1、编译打包gutil
https://gitee.com/simpleha/gutil.git
2、编译打包gmvc
https://gitee.com/simpleha/gmvc.git
3、新建webapp,引入pom
<dependency>
<groupId>com.shuimutong</groupId>
<artifactId>gmvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
4、修改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>gmvc</servlet-name>
<servlet-class>com.shuimutong.gmvc.handler.XDispatcherServlet</servlet-class>
<init-param>
<param-name>basePackage</param-name>
<param-value>com.shuimutong.testgmvc</param-value> //框架扫描的包名,多个路径以“,”连接
</init-param>
<load-on-startup>2</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>gmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> </web-app>
5、编写dao层
package com.shuimutong.testgmvc.dao; import com.shuimutong.testgmvc.bean.Person;
//接口
public interface TestDao {
Person findPerson();
} package com.shuimutong.testgmvc.dao.impl; import com.shuimutong.gmvc.annotation.XRepository;
import com.shuimutong.testgmvc.bean.Person;
import com.shuimutong.testgmvc.dao.TestDao;
//实现类,需加注解
@XRepository
public class TestDaoImpl implements TestDao { @Override
public Person findPerson() {
return new Person();
} }
6、编写service
package com.shuimutong.testgmvc.service;
//接口
public interface Test2Service {
void speak();
String convertString(String s);
} package com.shuimutong.testgmvc.service.impl; import com.alibaba.fastjson.JSONObject;
import com.shuimutong.gmvc.annotation.XAutowired;
import com.shuimutong.gmvc.annotation.XService;
import com.shuimutong.testgmvc.bean.Person;
import com.shuimutong.testgmvc.dao.TestDao;
import com.shuimutong.testgmvc.service.Test2Service;
//实现类
@XService
public class Test2ServiceImpl implements Test2Service {
@XAutowired
private TestDao testDao; @Override
public void speak() {
System.out.println("----Test2ServiceImpl-----speak----");
} @Override
public String convertString(String s) {
Person p = testDao.findPerson();
p.setName(p.getName() + s);
return JSONObject.toJSONString(p);
} }
7、编写controller
package com.shuimutong.testgmvc.controller; import java.util.Map; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSONObject;
import com.shuimutong.gmvc.annotation.XAutowired;
import com.shuimutong.gmvc.annotation.XController;
import com.shuimutong.gmvc.annotation.XRequestMapping;
import com.shuimutong.gmvc.util.RequestResolveUtil;
import com.shuimutong.testgmvc.service.Test2Service;
import com.shuimutong.testgmvc.service.TestService; @XController
@XRequestMapping("/test")
public class TestController {
@XAutowired
private Test2Service test2Service;
@XAutowired
private TestService testService; @XRequestMapping("/testA")
public void testA(HttpServletRequest request, HttpServletResponse reponse) {
System.out.println("Hi, this is TestA");
} @XRequestMapping("/testB")
public void testB(HttpServletRequest request, HttpServletResponse reponse) {
System.out.println("Hi, this is TestA");
JSONObject res = new JSONObject();
String tmpMsg = null;
Map<String, String[]> map = request.getParameterMap();
for(String k : map.keySet()) {
res.put(k, map.get(k));
if(tmpMsg == null) {
tmpMsg = map.get(k)[0];
}
}
System.out.println("----------testService.speak()------------");
testService.speak();
System.out.println("----------test2Service.convertString()------------");
String person = test2Service.convertString(tmpMsg);
res.put("person", person);
RequestResolveUtil.returnJson(request, reponse, res.toJSONString());
}
}
8、启动服务
9、示例代码地址
https://github.com/shuimutong/useDemo/tree/master/gmvc_demo