Java自定义框架初步(一)
来源:互联网 发布:扫描仪专用软件 京瓷 编辑:程序博客网 时间:2024/05/18 03:11
最近想自己学习写框架,看过一些资料,决定从JavaWeb开始。近来用到SpringBoot写过一些模拟接口,我对这种框架很感兴趣,几行代码就可以写出很实用的数据接口。
框架设计除了需要巧妙的思路之外,还需要准备三种技术:
1.反射;
2.自定义注解;
3.资源文件的读写。一般框架常用xml文件做配置,也可以使用properties属性文件。
我是用Idea做工具。
一、首先创建一个Java项目,勾选Web Application。目的是进行调试。成熟的框架应该是独立的。
二、导入几个包,包括gson和java6,java6会在创建servlet时提示下载,另外还要org.apache.commons.io包,用来读取post提交的请求数据,通过maven就可以下载。
三,创建文件结构如图:
四、实现三个自定义注解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface KFController { String value() default "/";}
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestUrl { String value() default "/";}
@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestBody { String value() default "";}
五、实现核心类
public class KfServlet extends javax.servlet.http.HttpServlet {
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //字符集处理 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); response.setHeader("Content-type", "application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); /** * 目前没有设计GET方法请求KV方式 * 严格的处理要判断请求方式,然后根据method判断是否可以使用RequestBody注解 * 这里只进行一点初步的设计 */ //获得请求字符串 String requestBody = getRequestBodyString(request); //获得请求路径 String requestURI = request.getRequestURI(); //去掉开头的"/" if (requestURI.startsWith("/")) { requestURI = requestURI.substring(1, requestURI.length()); } //去掉末尾的"/" if (requestURI.endsWith("/")) { requestURI = requestURI.substring(0, requestURI.length() - 1); } //分割剩余的路径字符串,三段为合法:项目名+控制器+方法 String[] paths = requestURI.split("/"); String controllerName = "", methodName = ""; boolean findController = false, findMethod = false;//控制器和方法的标记 if (paths.length == 3) {//长度为3才合法 controllerName = paths[1].toLowerCase();//控制器名 methodName = paths[2].toLowerCase();//方法名 String packageName = "com.kf";//基本包名,正确做法应该是在配置文件或者全局变量中设置 //通过"一条大红龙"的工具类扫描包下的所有类 //正确做法应该是在扫描的时候用解析的控制器名去匹配 List<Class<?>> classes = ClassUtil.getClasses(packageName); //遍历扫描的类 //获得类上面的KFController注解 for (Class clazz : classes) { KFController kfControllerAnno = (KFController) clazz.getAnnotation(KFController.class); //判断KFController注解是否为空 if (kfControllerAnno != null&& kfControllerAnno.value().toLowerCase().equals(controllerName)) { findController = true;//标记找到了控制器 try { //获得Controller类下的所有方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { //获取方法上的RequestUrl注解 RequestUrl requestUrlAnno = method.getAnnotation(RequestUrl.class); //判断RequestUrl注解是否为空 if (requestUrlAnno != null&&requestUrlAnno.value().toLowerCase().equals(methodName)) { findMethod = true;//标记找到了方法 //获得方法的所有参数 Parameter[] parameters = method.getParameters(); //无参 if (parameters == null || parameters.length == 0) { //执行无参数方法,返回 out.write((String) method.invoke(clazz.newInstance())); return; } else { //有参 for (Parameter parameter : parameters) { //获得参数RequestBody注解 Annotation requestBodyAnno = parameter.getAnnotation(RequestBody.class); //判断RequestBody注解是否为空 if (requestBodyAnno != null) { //判断如果使用了RequestBody注解,就把RequestBody赋值给它 //把请求body转化为对象类型 Object obj = new Gson().fromJson(requestBody, parameter.getType()); //执行带一个用TequestBody修饰过的参数的方法 out.write((String) method.invoke(clazz.newInstance(), obj)); return; } else { out.write("404错误 对不起,没有找到符合注解的相应的请求方法"); } } } } } } catch (Exception e) { e.printStackTrace(); } finally { if (!findMethod) { out.write("404错误 对不起,没有找到相应的请求方法"); } if (out != null) { out.close(); } } } } if (!findController) { out.write("404错误 对不起,没有找到相应的控制器"); } } else { out.write("404 URL不合法"); } if (out != null) { out.close(); } } public static String getRequestBodyString(HttpServletRequest request) throws IOException { String requestBody = ""; if ("POST".equals(request.getMethod())) { requestBody = IOUtils.toString(request.getInputStream(), "UTF-8"); } else { String queryString = request.getQueryString(); if (queryString != null && queryString.length() != 0) { requestBody = queryString.replace("%22", "\""); } } return requestBody; }}
/** * 取得某个接口下所有实现这个接口的类 */ public static List<Class> getAllClassByInterface(Class c) { List<Class> returnClassList = null; if (c.isInterface()) { // 获取当前的包名 String packageName = c.getPackage().getName(); // 获取当前包下以及子包下所以的类 List<Class<?>> allClass = getClasses(packageName); if (allClass != null) { returnClassList = new ArrayList<Class>(); for (Class classes : allClass) { // 判断是否是同一个接口 if (c.isAssignableFrom(classes)) { // 本身不加入进去 if (!c.equals(classes)) { returnClassList.add(classes); } } } } } return returnClassList; } /* * 取得某一类所在包的所有类名 不含迭代 */ public static String[] getPackageAllClassName(String classLocation, String packageName) { //将packageName分解 String[] packagePathSplit = packageName.split("[.]"); String realClassLocation = classLocation; int packageLength = packagePathSplit.length; for (int i = 0; i < packageLength; i++) { realClassLocation = realClassLocation + File.separator + packagePathSplit[i]; } File packeageDir = new File(realClassLocation); if (packeageDir.isDirectory()) { String[] allClassName = packeageDir.list(); return allClassName; } return null; } /** * 从包package中获取所有的Class * * @param packageName * @return */ public static List<Class<?>> getClasses(String packageName) { //第一个class类的集合 List<Class<?>> classes = new ArrayList<Class<?>>(); //是否循环迭代 boolean recursive = true; //获取包的名字 并进行替换 String packageDirName = packageName.replace('.', '/'); //定义一个枚举的集合 并进行循环来处理这个目录下的things Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); //循环迭代下去 while (dirs.hasMoreElements()) { //获取下一个元素 URL url = dirs.nextElement(); //得到协议的名称 String protocol = url.getProtocol(); //如果是以文件的形式保存在服务器上 if ("file".equals(protocol)) { //获取包的物理路径 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); //以文件的方式扫描整个包下的文件 并添加到集合中 findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)) { //如果是jar包文件 //定义一个JarFile JarFile jar; try { //获取jar jar = ((JarURLConnection) url.openConnection()).getJarFile(); //从此jar包 得到一个枚举类 Enumeration<JarEntry> entries = jar.entries(); //同样的进行循环迭代 while (entries.hasMoreElements()) { //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 JarEntry entry = entries.nextElement(); String name = entry.getName(); //如果是以/开头的 if (name.charAt(0) == '/') { //获取后面的字符串 name = name.substring(1); } //如果前半部分和定义的包名相同 if (name.startsWith(packageDirName)) { int idx = name.lastIndexOf('/'); //如果以"/"结尾 是一个包 if (idx != -1) { //获取包名 把"/"替换成"." packageName = name.substring(0, idx).replace('/', '.'); } //如果可以迭代下去 并且是一个包 if ((idx != -1) || recursive) { //如果是一个.class文件 而且不是目录 if (name.endsWith(".class") && !entry.isDirectory()) { //去掉后面的".class" 获取真正的类名 String className = name.substring(packageName.length() + 1, name.length() - 6); try { //添加到classes classes.add(Class.forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } } catch (IOException e) { e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; } /** * 以文件的形式来获取包下的所有Class * * @param packageName * @param packagePath * @param recursive * @param classes */ public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) { //获取此包的目录 建立一个File File dir = new File(packagePath); //如果不存在或者 也不是目录就直接返回 if (!dir.exists() || !dir.isDirectory()) { return; } //如果存在 就获取包下的所有文件 包括目录 File[] dirfiles = dir.listFiles(new FileFilter() { //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) public boolean accept(File file) { return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); } }); //循环所有文件 for (File file : dirfiles) { //如果是目录 则继续扫描 if (file.isDirectory()) { findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes); } else { //如果是java类文件 去掉后面的.class 只留下类名 String className = file.getName().substring(0, file.getName().length() - 6); try { //添加到集合中去 classes.add(Class.forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }}
七、创建两个model,一个是用于打包数据的UserModel,一个是用于解析请求数据的UserRequst
public class UserModel { public long id; public String name; public boolean gender; public int age; public UserModel(long id, String name, boolean gender, int age) { this.id = id; this.name = name; this.gender = gender; this.age = age; }}
public class UserRequest { public long id; public String name; public String passWord;}
八、创建两个controller,目的是进行测试区别
@KFController("test")public class TestController { @RequestUrl("test") public String test() { return "我就是test宝哥"; } @RequestUrl("gettest") public String gettest() { UserModel userModel = new UserModel(1, "Test Chen", true, 38); return new Gson().toJson(userModel); } @RequestUrl("bodyTest") public String bodyTest(@RequestBody String body) { return body; }}
@KFController("user")public class UserController { @RequestUrl("getUserInfo") public String getUserInfo() { return "我就是宝哥"; } @RequestUrl("getUser") public String getUser() { return new Gson().toJson(new UserModel(1, "Kaly Chen", true, 38)); } @RequestUrl("getUserBody") public String getUserBody(@RequestBody UserRequest body) { return new Gson().toJson(new UserModel(body.id, body.name, true, 38)); }}
九、还有web.xml需要配置,让所有的请求都从KfServlet经过,然后分发
<servlet> <servlet-name>KfServlet</servlet-name> <servlet-class>com.kf.core.KfServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>KfServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
十、测试
1.浏览器输入http://192.168.1.101:8080/kfdemo/user/getuserbody?{%22id%22:19,%22name%22:%22chenfabao%22,%22passWord%22:%22123456%22}
2.PostMan测试
下一次,我们考虑去操作数据库,并实现ORM
阅读全文
0 0
- Java自定义框架初步(一)
- 自定义框架(一)
- Java集合框架详解(一)——初步认识集合框架
- Android自定义View初步(一)
- Volley框架的初步理解(一)
- java 自定义mvc框架(一)------前端控制器的实现
- 集合论初步认识 java学习(一)
- Java反射机制 初步(一)
- java反射初步学习(一)
- Java NIO(一) 初步理解NIO
- java开发corba初步(一)
- Android:自定义View 初步之旅随记(一)
- 初步理解“单文档程序框架”(一)
- Android网络框架-Volley(一) 初步使用
- 实训ssm框架学习配置文件初步解析(一)
- Struts2 框架 自定义实现(一)
- 自定义MVC框架(一)反射机制
- Java集合框架初步(hashset treeset list hashmap)
- 170828 WarGames-Narnia(3)
- mongodb意外退出问题解决
- 仿各种客户端都具备的评分控件
- 搭建WordPress 个人博客
- Mac虚拟机安装XCode 配置opencv
- Java自定义框架初步(一)
- 链表的删除、插入、反向
- 给自己打点鸡血(装B文章)
- WlMAP:突破内网端口转发映射工具
- Java中getResourceAsStream的用法
- 第二周--线性回归问题
- Android studio-解决i5处理器仍cpu占用100%
- opencv--颜色物体识别跟踪
- 买苹果---动态规划