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; } 复制代码
- 几个注解在web.mvc包中,定义如下:
@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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby元编程(第2版)
[意] Paolo Perrotta / 廖志刚 / 华中科技大学出版社 / 2015-8-1 / 68.80
《Ruby元编程(第2版)》在大量剖析实例代码的基础上循序渐进地介绍Ruby特有的实用编程技巧。通过分析案例、讲解例题、回顾Ruby类库的实现细节,作者不仅向读者展示了元编程的优势及其解决问题的方式,更详细列出33种发挥其优势的编程技巧。本书堪称动态语言设计模式。Ruby之父松本行弘作序推荐。一起来看看 《Ruby元编程(第2版)》 这本书的介绍吧!