servlet抽取二:基于注解的方式

2021/4/12 18:32:27

本文主要是介绍servlet抽取二:基于注解的方式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

回顾

之前写过一篇博客是servlet的抽取
但是在使用的时候,每次都要加上method=xxx
现在就想基于之前的方法进行改进,实现类似于springmvc那样的效果
直接根据请求,找到对应的方法,来执行,
所以现在就是用注解的方式对之前的serlvet抽取进行改进

思想

  1. 创建一个servlet,让所有的请求走这个servlet
  2. 再在这个servlet中,通过注解找到编写了映射地址的方法的类(这个类是标注有注解)
  3. 再找到这个类中定义的方法,对标注了映射地址的方法,通过反射调用

代码与解释

  • 工具类ClassScaner:用于找到某个包下的所有类
public class ClassScanner { 
    /**
     * 获得包下面的所有的class
     * @param
     * @return List包含所有class的实例
     */
    public static List<Class<?>> getClasssFromPackage(String packageName) {
        List<Class<?>> clazzs = new ArrayList<>();
        // 是否循环搜索子包
        boolean recursive = true;
        // 包名对应的路径名称
        String packageDirName = packageName.replace('.', '/');
        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");
                    findClassInPackageByFile(packageName, filePath, recursive, clazzs);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return clazzs;
    }

    /**
     * 在package对应的路径下找到所有的class
     */
    public static void findClassInPackageByFile(String packageName, String filePath, final boolean recursive,
                                                List<Class<?>> clazzs) {
        File dir = new File(filePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        // 在给定的目录下找到所有的文件,并且进行条件过滤
        File[] dirFiles = dir.listFiles(new FileFilter() {

            public boolean accept(File file) {
                boolean acceptDir = recursive && file.isDirectory();// 接受dir目录
                boolean acceptClass = file.getName().endsWith("class");// 接受class文件
                return acceptDir || acceptClass;
            }
        });

        for (File file : dirFiles) {
            if (file.isDirectory()) {
                findClassInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, clazzs);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  • DispatherServlet :用于获取所有的*.do的请求,分发到相应的方法
@WebServlet("*.do")
public class DispatherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        //为了获取请求地址中,我们需要的部分,主要是为了取出ip端口项目名等无关信息
        String mappingPath = requestURI.substring(contextPath.length(),requestURI.length());

		//找到controller包下所有的类
        List<Class<?>> classsFromPackage = ClassScanner.getClasssFromPackage("com.kehao.controller");
		
		//对每一个类去看他是否有MyCompoent注解
        classsFromPackage.stream().filter(clz-> clz.isAnnotationPresent(MyCompoent.class)).forEach(
        		//对于符合要求的类,获取他们的被MyRequestMapping注解标注的方法,如果注解的值和请求地址相同就调用该方法
                (clz)->{
                    Arrays.stream(clz.getDeclaredMethods()).
                            filter(method -> method.isAnnotationPresent(MyRequestMapping.class)).
                            filter(method->mappingPath.equals(method.getAnnotation(MyRequestMapping.class).value())).
                            forEach(method -> {
                                try {
                                    method.invoke(clz.newInstance(),req,resp);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            });
                }
        );
    }
}
  • HelloController 实际的控制层
@MyCompoent
public class HelloController {

    @MyRequestMapping("/hello.do")
    public void hello(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        resp.getWriter().println("hello,i'm in HelloController");
    }
}
  • 注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
    String value();
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCompoent {
}

依然存在的不足

很明显,这个代码还存在着明显的不足,需要改进,首先每次调用都需要扫描类和方法,需要新建了一个对象,而这些过程是重复的,还可以继续优化



这篇关于servlet抽取二:基于注解的方式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程