文章目录
- 目标
- 最终效果展示
- 基本步骤
- 1. 解析控制器类:
- 2. 解析处理函数:
- 3. 解析处理函数变量名:
- 4. 监听TCP连接:
- 5. 实现路由函数:
- 知识点总结
目标
- 与SpringMvc定义Controller类似效果
最终效果展示
主类
package org.example;import com.zcj.Accepter;
import com.zcj.annotation.SummerApplication;import java.io.IOException;//标注控制器类
@SummerApplication("org.example.MainController")
public class App
{public static void main( String[] args ) throws IOException {Accepter accepter = new Accepter();accepter.run(App.class);}
}
控制器类
package org.example;import com.zcj.annotation.Controller;
import com.zcj.annotation.Param;
import com.zcj.entity.Response;public class MainController {//标注处理的路径@Controller("/hello")public Response hello(@Param("name") String name){return new Response(200, Response.HTML, "<h1>Hello,"+ name + "</h1>");}
}
浏览器访问:
基本步骤
1. 解析控制器类:
利用反射解析@SummerApplication
注解所包含的类,获得该类中的处理函数
2. 解析处理函数:
获取控制器类中包含@Controller
的方法,建立处理函数映射表
3. 解析处理函数变量名:
获取处理函数中包含@Param
的参数,建立处理函数变量名表用于获取请求中的参数
/*** 解析控制器类中的处理函数* @param clazz 控制器类的Class* @throws ClassNotFoundException* @throws IllegalAccessException* @throws InstantiationException*/private void analysisController(Class clazz) throws ClassNotFoundException, IllegalAccessException, InstantiationException {System.out.println("解析控制器类.....");if (!clazz.isAnnotationPresent(SummerApplication.class)) {System.out.println("ERROR: 未指定控制器类");return;}SummerApplication summerApplication = (SummerApplication) clazz.getAnnotation(SummerApplication.class);String classPack = summerApplication.value();// Class.forName 可以得到Class对象,并且如果这个类没有被加载过它将会初始化这个类。Class controllerClazz = Class.forName(classPack);controllers = (T) controllerClazz.newInstance();System.out.println("控制器类:" + controllerClazz);System.out.println("\n*************************************************************************************************************************" +"\n* 路径\t\t|参数\t\t\t\t| 处理函数");//反射解析控制器类Method[] methods = controllerClazz.getMethods();for (Method method : methods) {if (!method.isAnnotationPresent(Controller.class)) continue;Controller annotation = method.getAnnotation(Controller.class);//将函数加入映射表controllerMap.put(annotation.value(), method);Parameter[] parameterList = method.getParameters();List<String> paramNameList = null;if (parameterList.length > 0) {//获取参数变量名paramNameList = new ArrayList<>();for (Parameter param : parameterList) {//判断是否出现 @Paramif (!param.isAnnotationPresent(Param.class)) continue;Param paramAnnotation = param.getAnnotation(Param.class);paramNameList.add(paramAnnotation.value());}//处理函数变量名映射表paramNameMap.put(annotation.value(), paramNameList);}System.out.println("* " + annotation.value() + " | " + paramNameList + " | " + method);}System.out.println("*************************************************************************************************************************\n");System.out.println("解析控制器类完成");}
4. 监听TCP连接:
获取请求的方法(POST,GET),请求的路径,请求参数等
/*** 启动连接监听* @param clazz 启动类*/public void run(Class clazz) {//解析控制器类try {analysisController(clazz);} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {System.out.println("ERROR: 控制器类不存在, 请检查 @SummerApplication 注解的类名");return;}//启动监听连接System.out.println("==============================================\n\n等待远程连接,端口号为:" + serverSocket.getLocalPort() + "\n\n==============================================\n");while (true) {try {Socket client = serverSocket.accept();synchronized (System.out) {System.out.println("==============================================\n连接的远程主机地址:" + client.getRemoteSocketAddress() + "\n==============================================\n");}executorService.submit(() -> {//处理请求handlerRequest(client);});} catch (IOException ioException) {System.out.println(ioException);//关闭线程池executorService.shutdown();}}}
请求解析函数
/*** 此函数用于解析请求并分配请求到相应的处理函数* @param client 连接请求的Socket*/private void handlerRequest(Socket client) {try (InputStream input = client.getInputStream()) {try (OutputStream output = client.getOutputStream()) {try (PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) {try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {//请求头String requestHeader;int contentLength = 0;//请求的类型String requestType = null;//请求的路径String requestPath = null;//请求参数String requestParams = null;//参数类型String contentType = null;while ((requestHeader = reader.readLine()) != null && !requestHeader.isEmpty()) {synchronized (System.out) {System.out.println(requestHeader);}//获得请求的类型,路径if (requestHeader.startsWith("POST") || requestHeader.startsWith("GET")) {String[] line = requestHeader.split(" ");requestType = line[0];int end = line[1].indexOf("?");if (end == -1) requestPath = line[1];else requestPath = line[1].substring(0, end);}//获得参数类型if (requestHeader.startsWith("Content-Type")) {String[] line = requestHeader.split(": ");contentType = line[1];}//获得Get参数if (requestHeader.startsWith("GET")) {int begin = requestHeader.indexOf("?") + 1;if (begin == 0) {requestParams = "";} else {int end = requestHeader.indexOf("HTTP/") - 1;//GET的参数requestParams = requestHeader.substring(begin, end);}}//获取POST请求内容长度if (requestHeader.startsWith("Content-Length")) {int begin = requestHeader.indexOf("Content-Lengh:") + "Content-Length:".length() + 1;String postParamterLength = requestHeader.substring(begin).trim();contentLength = Integer.valueOf(postParamterLength);}}//获取POST请求参数字符串StringBuffer sb = new StringBuffer();if (contentLength > 0) {for (int i = 0; i < contentLength; i++) {sb.append((char) reader.read());}requestParams = sb.toString();}//将字符串转UTF-8requestParams = URLDecoder.decode(requestParams, "UTF-8");System.out.println(requestParams);synchronized (System.out) {System.out.println("请求类型:" + requestType);System.out.println("请求路径:" + requestPath);System.out.println("请求参数类型:" + contentType);System.out.println("请求参数:" + requestParams);}//发送回复Response resp = null;if (requestType.equals("POST") && !contentType.equals("application/x-www-form-urlencoded")) {//只接受 application/x-www-form-urlencoded 的参数resp = new Response(400, Response.HTML, "<h1>400<h4>POST请求的参数格式必须为 application/x-www-form-urlencoded</h4></h1>");} else {//将请求分配到相应的处理函数resp = dispatch(requestPath, requestParams);}writer.write(resp.toString());writer.flush();client.close();synchronized (System.out) {System.out.println("\n=============================================\nWARING:" + client.getRemoteSocketAddress() + " is disconnected.\n=============================================\n");}}}} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {client.close();System.out.println("ERROR: 分配请求失败");e.printStackTrace();}} catch (IOException e) {try {client.close();} catch (IOException ignored) {}synchronized (System.out) {System.out.println("\n=============================================\nWARING:" + client.getRemoteSocketAddress() + " is disconnected.\n=============================================\n");}}}
5. 实现路由函数:
利用处理函数变量名表获取参数,并将参数转为处理函数所需类型,利用处理函数映射表将请求转发到对应的处理函数,返回结果
/*** @param requestPath 请求的路径* @param requestParams 请求的参数字符串* @return Response http回复类* @throws NoSuchMethodException* @throws IllegalAccessException* @throws InvocationTargetException* @throws InstantiationException*/private Response dispatch(String requestPath, String requestParams) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Response resp = null;if (!controllerMap.containsKey(requestPath)) {//处理函数不存在return new Response(404, Response.HTML, "<h1>404<h4>请求的资源不存在</h4></h1>");}//调用处理函数Method method = controllerMap.get(requestPath);//获取方法的参数类型Class<?>[] paramterTypes = method.getParameterTypes();if (paramterTypes.length == 0) {//不需要参数//invoke()第一个参数为调用方法的实例return (Response) controllerMap.get(requestPath).invoke(controllers);}//检验参数//解析参数字符传LinkedHashMap<String, String> paramMap = Params.toMap(requestParams);if (paramMap == null) {//没有所需参数return new Response(400, Response.HTML, "<h1>参数错误,请检查</h1>");}//获得方法的参数变量名表List<String> paramNameList = paramNameMap.get(requestPath);//方法的参数Object[] params = new Object[paramterTypes.length];for (int i = 0; paramMap != null && i < paramterTypes.length; i++) {//参数的类型Class paramType = paramterTypes[i];if (allowType.contains(paramType) && !paramMap.containsKey(paramNameList.get(i))) {//参数缺漏return new Response(400, Response.HTML, "<h1 style=\"color:red;\">参数错误, 缺少:" + paramNameList.get(i) + "[ " + paramterTypes[i].getName() + " ]</h1>");}//将String 转为 所需的类型,只支持基本类型或自定义类(不包括集合)String param = paramMap.get(paramNameList.get(i));//将基本类型转为包装类型if (paramType.equals(int.class)) {paramType = Integer.class;} else if (paramType.equals(boolean.class)) {paramType = Boolean.class;}if (paramType.equals(String.class)) {params[i] = param;} else if (paramType.equals(Integer.class) || paramType.equals(Boolean.class)) {//将参数转为所需类型Object instance = paramType.getConstructor(String.class).newInstance(param);params[i] = instance;} else {//自定义类Object o = Params.toObject(requestParams, paramType);params[i] = o;}}//调用控制器return (Response) controllerMap.get(requestPath).invoke(controllers, params);}
知识点总结
- 将字符串转UTF-8的方法
String utf8Str = URLDecoder.decode(str, "UTF-8");
-
向反射获取的函数中传入任意数量且不同类型的参数的方法
前提知识:JAVA中的引用类型都继承于Object,通过向上转型,可以用Object类型引用各种引用类型
第一步:将基本类型转为包装类型
第二步:将所有参数放入Object数组中
第三步:调用相应的函数
//...转换步骤省略,可参照 基本步骤的5. dispatch函数
//len为参数的数量
Object[] parmas = new Object[len]
//....放入参数到 params 数组
method.invoke(instance, params)