Java : JDK 1.8
IDE : IDEA 2019
构建工具 : Gradle
复制代码
1.整体思路
1.1 一些点
- 使用DispatcherServlet同一接收请求
- 自定义@Controller、@RequestMapping、@RequestParam注解来实现对应不同URI的方法调用
- 使用反射用HandlerMapping调用对应的方法
- 使用tomcat-embed-core内嵌web容器Tomcat.
- 自定义简单的BeanFactory实现依赖注入DI,实现@Bean注解和@Controller注解的Bean管理
1.2 整体调用图
1.3 启动加载顺序
2.具体实现
2.1 项目整体工程目录
- 创建项目就不说了,IDEA自行创建gradle项目就好。
2.2 具体实现
- 在web.server下创建TomcatServer类
- 简单来说就是实例化一个tomcat服务,并实例化一个DispatcherServlet加入到context中,设置支持异步,处理所有的请求
public class TomcatServer {
private Tomcat tomcat;
private String[] args;
public TomcatServer(String[] args) {
this.args = args;
}
public void startServer() throws LifecycleException {
// instantiated Tomcat
tomcat = new Tomcat();
tomcat.setPort(6699);
tomcat.start();
Context context = new StandardContext();
context.setPath("");
context.addLifecycleListener(new Tomcat.FixContextListener());
// register Servlet
DispatcherServlet dispatcherServlet = new DispatcherServlet();
Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet).setAsyncSupported(true);
context.addServletMappingDecoded("/", "dispatcherServlet");
tomcat.getHost().addChild(context);
Thread awaitThread = new Thread(() -> TomcatServer.this.tomcat.getServer().await(), "tomcat_await_thread");
awaitThread.setDaemon(false);
awaitThread.start();
}
}
复制代码
- 在web.servlet中新建DispatcherServlet实现Servlet接口.
- 因为是做一个简单的MVC,这里我直接处理所有请求,不分GET和POST,可以自行改进。
- 处理所有请求 只需要在service方法中处理即可。
- 简单的思路是,用HandlerManager通过URI在Map对象中获取到对应MappingHandler对象,然后调用handle方法。
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
try {
MappingHandler mappingHandler = HandlerManager.getMappingHandlerByURI(((HttpServletRequest) req).getRequestURI());
if (mappingHandler.handle(req, res)) {
return;
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException | ClassNotFoundException e) {
e.printStackTrace();
}
}
复制代码
- 在web.handler中分别新建MappingHandler和HandlerManager两个类。
- MappingHandler用来存储URI调用信息,像URI、Method 和 调用参数 这些。如下
public class MappingHandler {
private String uri;
private Method method;
private Class<?> controller;
private String[] args;
public MappingHandler(String uri, Method method, Class<?> controller, String[] args) {
this.uri = uri;
this.method = method;
this.controller = controller;
this.args = args;
}
}
复制代码
- 而HandlerManager则是负责把对应的URI和处理的MappingHandler对应起来
- 实现就是用自己定义的类扫描器把所有扫描到的类传进来遍历,找出带有Controller注解的类
- 然后针对每个Controller中含有RequestMapping注解的方法信息构建MappingHandler对象进行注册,放入Map中。
public class HandlerManager {
public static Map<String, MappingHandler> handleMap = new HashMap<>();
public static void resolveMappingHandler(List<Class<?>> classList) {
for (Class<?> cls : classList) {
if (cls.isAnnotationPresent(Controller.class)) {
parseHandlerFromController(cls);
}
}
}
private static void parseHandlerFromController(Class<?> cls) {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(RequestMapping.class)) {
continue;
}
String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
List<String> paramNameList = new ArrayList<>();
for (Parameter parameter : method.getParameters()) {
if (parameter.isAnnotationPresent(RequestParam.class)) {
paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
}
}
String[] params = paramNameList.toArray(new String[paramNameList.size()]);
MappingHandler mappingHandler = new MappingHandler(uri, method, cls, params);
HandlerManager.handleMap.put(uri, mappingHandler);
}
}
public static MappingHandler getMappingHandlerByURI(String uri) throws ClassNotFoundException {
MappingHandler handler = handleMap.get(uri);
if (null == handler) {
throw new ClassNotFoundException("MappingHandler was not exist!");
} else {
return handler;
}
}
}
复制代码
- 然后在MappingHandler中加入handle方法,对请求进行处理。
public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
String requestUri = ((HttpServletRequest) req).getRequestURI();
if (!uri.equals(requestUri)) {
return false;
}
// read parameters.
Object[] parameters = new Object[args.length];
for (int i = 0; i < args.length; i++) {
parameters[i] = req.getParameter(args[i]);
}
// instantiated Controller.
Object ctl = BeanFactory.getBean(controller);
// invoke method.
Object response = method.invoke(ctl, parameters);
res.getWriter().println(response.toString());
return true;
}
复制代码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
String value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface RequestParam {
String value();
}
复制代码
- 创建类扫描器ClassScanner
- 思路也简单,用Java的类加载器,把类信息读入,放到一个List中返回即可。
- 我只处理了jar包类型。
public class ClassScanner {
public static List<Class<?>> scanClasses(String packageName) throws IOException, ClassNotFoundException {
List<Class<?>> classList = new ArrayList<>();
String path = packageName.replace(".", "/");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
if (resource.getProtocol().contains("jar")) {
// get Class from jar package.
JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
String jarFilePath = jarURLConnection.getJarFile().getName();
classList.addAll(getClassesFromJar(jarFilePath, path));
} else {
// todo other way.
}
}
return classList;
}
private static List<Class<?>> getClassesFromJar(String jarFilePath, String path) throws IOException, ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
JarFile jarFile = new JarFile(jarFilePath);
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String entryName = jarEntry.getName();
if (entryName.startsWith(path) && entryName.endsWith(".class")) {
String classFullName = entryName.replace("/", ".").substring(0, entryName.length() - 6);
classes.add(Class.forName(classFullName));
}
}
return classes;
}
}
复制代码
- 在beans包下创建BeanFactory类和@Autowired @Bean注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {
}
复制代码
- 相信细心的一定看到了我MappingHandler里面的handle方法其实是用BeanFactory调用的getBean。
- BeanFactory的实现其实也很简单。就是把类扫描器扫描到的类传进来,吧带有Controller和Bean注解的类放入map中,如果内部用Autowired注解就用内部依赖注入。只有单例模式。
public class BeanFactory {
private static Map<Class<?>, Object> classToBean = new ConcurrentHashMap<>();
public static Object getBean(Class<?> cls) {
return classToBean.get(cls);
}
public static void initBean(List<Class<?>> classList) throws Exception {
List<Class<?>> toCreate = new ArrayList<>(classList);
while (toCreate.size() != 0) {
int remainSize = toCreate.size();
for (int i = 0; i < toCreate.size(); i++) {
if (finishCreate(toCreate.get(i))) {
toCreate.remove(i);
}
}
if (toCreate.size() == remainSize) {
throw new Exception("cycle dependency!");
}
}
}
private static boolean finishCreate(Class<?> cls) throws IllegalAccessException, InstantiationException {
if (!cls.isAnnotationPresent(Bean.class) && !cls.isAnnotationPresent(Controller.class)) {
return true;
}
Object bean = cls.newInstance();
for (Field field : cls.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
Class<?> fieldType = field.getType();
Object reliantBean = BeanFactory.getBean(fieldType);
if (null == reliantBean) {
return false;
}
field.setAccessible(true);
field.set(bean, reliantBean);
}
}
classToBean.put(cls, bean);
return true;
}
}
复制代码
- 启动类
public class IlssApplication {
public static void run(Class<?> cls, String[] args) {
TomcatServer tomcatServer = new TomcatServer(args);
try {
tomcatServer.startServer();
List<Class<?>> classList = ClassScanner.scanClasses(cls.getPackage().getName());
BeanFactory.initBean(classList);
HandlerManager.resolveMappingHandler(classList);
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
2.3 关于测试模块 test
- 做测试 内部打包的时候需要在test项目中的build.gradle加入下面配置
jar {
manifest {
attributes "Main-Class": "io.ilss.framework.Application"
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
复制代码
- 创建Service类
@Bean
public class NumberService {
public Integer calNumber(Integer num) {
return num;
}
}
复制代码
- 创建Controller类
@Controller
public class TestController {
@Autowired
private NumberService numberService;
@RequestMapping("/getNumber")
public String getSalary(@RequestParam("name") String name, @RequestParam("num") String num) {
return numberService.calNumber(11111) + name + num ;
}
}
复制代码
- 创建Application启动类
public class Application {
public static void main(String[] args) {
IlssApplication.run(Application.class, args);
}
}
复制代码
- 控制台
gradle clean install
java -jar mvc-test/build/libs/mvc-test-1.0-SNAPSHOT.jar
复制代码
- 访问网址
-
http://localhost:6699/getNumber?name=aaa&num=123
https://juejin.im/post/5d12dc97e51d4510803ce3e6