zoukankan      html  css  js  c++  java
  • Servlet传统配置方式和Servlet3.0使用注解的方式

    一、Servlet的传统配置方式

      在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示:

    <servlet>
        <servlet-name>ActionServlet</servlet-name>
        <servlet-class>com.web.controller.ActionServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>ActionServlet</servlet-name>
        <url-pattern>/servlet/ActionServlet</url-pattern>
    </servlet-mapping>

    发一个Servlet,都要在web.xml中配置Servlet才能够使用,这实在是很头疼的事情,所以Servlet3.0之后提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署描述,简化开发流程。本文所讲的基于注解方式配置Servlet不是针对Servlet3.0的,而是基于Servlet2.5的,通过开发自定义注解和注解处理器来模拟实现类似于Servlet3.0的注解方式配置Servlet

    二、基于注解的方式配置Servlet

      JDK1. 5版本之后, JAVA提供了一种叫做Annotation的新数据类型,中文译为注解或标注,它的出现为铺天盖地的XML配置文件提供了一个完美的解决方案,让 JAVA EE开发更加方便快速,也更加干净了。不过Servlet2.5默认情况下是不支持注解方式的配置的,但是我们可以开发自定义注解,然后将注解标注到Servlet上,再针对我们自定义的注解写一个注解处理器.

    原理:
      首先,浏览器发出请求后,经过过滤器。过滤器执行初始init方法。工具类扫描配置文件中指定的包。扫描工具把指定包下的所有类的字节码文件存放到Set集合中,然后遍历set集合,如果set集合中的元素(包中的各个类的字节码文件)用的是WebServlet.class这个注解类型编写开发的,那么就获取当前set元素的在注解中定义的value值(URI地址),这个value值在注解接口WebServlet中定义的是Servlet的访问URI。因此过滤器的初始化init()方法就把"指定包下的所有使用WebServlet注解开发的普通类的字节码文件"作为值value,使用注解中的String value()定义了Servlet访问的URI 作为键key, 存放到 Map<String, Class<?>> classMap 这个Map集合中。map集合存放到全局对象ServeltContext中。然后在过滤器的doFilter方法中,在ServeltContext中获取到这个map。过滤器如果执行了,那么肯定是用户发出request请求了。先通过request对象获取用户的URI,在map集合中找到此使用注解的java普通类,反射创建此java类的对象。如果没有指定调用哪个方法,那么就根据用户的请求方式get还是post,调用相对应的方法。如果指定了方法,先根据URI在map集合中找到对应的字节码文件,创建java普通类的对象。根据地址中"!"后面的方法,反射执行java普通类对象的方法。当然,有了字节码文件,就能获取到java类中的所有方法,当然也能获取到注解中定义的参数值,从而作为参数执行java类的init方法。

    具体的做法如下:

    2.1、开发用于配置Servlet的相关注解

      1、开发WebServlet注解,用于标注处理请求的Servlet类

    package com.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义WebServlet注解,模拟Servlet3.0的WebServlet注解
     * @Target 注解的属性值表明了 @WebServlet注解只能用于类或接口定义声明的前面, 
     * @WebServlet注解有一个必填的属性 value 。
     * 调用方式为: @WebServlet(value="/xxxx") ,
     * 因语法规定如果属性名为 value 且只填 value属性值时,可以省略 value属性名,即也可以写作:@WebServlet("/xxxx") 
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface WebServlet {
        //Servlet的访问URL
        String value();
        //Servlet的访问URL
        String[] urlPatterns() default {""};
        //Servlet的描述
        String description() default "";
        //Servlet的显示名称
        String displayName() default "";
        //Servlet的名称
        String name() default "";
        //Servlet的init参数
        WebInitParam[] initParams() default {};
    }

    将Servlet在web.xml中的配置信息使用WebServlet注解来表示,使用注解后,只需要在相应Servlet 类的前面使用类似@WebServlet("/servlet/LoginServlet") 注解就可以达到和上述 web.xml 文件中配置信息一样的目的。注解@WebServlet中的属性值"/servlet/LoginServlet"表示了web.xml 配置文件中 <servlet-mapping> 元素的子元素 <url-pattern> 里的值。通过这样的注解能简化在 XML 文件中配置 Servlet 信息,整个配置文件将会非常简洁干净,开发人员的工作也将大大减少。

      2、开发WebInitParam注解,用于配置Servlet初始化时使用的参数

    package com.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
    * @ClassName: WebInitParam
    * @Description: 定义Servlet的初始化参数注解
    * @author: hdb
    * @date: 2017-10-1 下午3:25:53
    *
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface WebInitParam {
        //参数名
        String paramName() default "";
        //参数的值
        String paramValue() default "";
    }

    2.2、编写处理注解的处理器

      上面简要地介绍了注解的定义声明与使用方式,注解在后台需要一个处理器才能起作用,所以还得针对上面的注解编写处理器,在这里我们使用Filter作为注解的处理器,编写一个AnnotationHandleFilter,代码如下:

    package com.web.filter;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.annotation.WebInitParam;
    import com.annotation.WebServlet;
    import com.util.ScanClassUtil;
    
    /**
    * @ClassName: AnnotationHandleFilter
    * @Description: 使用Filter作为注解的处理器
    * @author: hdb
    * @date: 2017-11-12 下午10:15:19
    *
    */ 
    public class AnnotationHandleFilter implements Filter {
    
        private ServletContext servletContext = null;
        
        /* 过滤器初始化时扫描指定的包下面使用了WebServlet注解的那些类
         * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
         */
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("---AnnotationHandleFilter过滤器初始化开始---");
            servletContext = filterConfig.getServletContext();
            Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
            //获取web.xml中配置的要扫描的包
            String basePackage = filterConfig.getInitParameter("basePackage");
            //如果配置了多个包,例如:<param-value>com.web.controller,com.web.UI</param-value>
            if (basePackage.indexOf(",")>0) {
                //按逗号进行分隔
                String[] packageNameArr = basePackage.split(",");
                for (String packageName : packageNameArr) {
                    addServletClassToServletContext(packageName,classMap);
                }
            }else {
                addServletClassToServletContext(basePackage,classMap);
            }
            System.out.println("----AnnotationHandleFilter过滤器初始化结束---");
        }
        
        /**
        * @Method: addServletClassToServletContext
        * @Description:添加ServletClass到ServletContext中
        * @Anthor:hdb
        *
        * @param packageName
        * @param classMap
        */ 
        private void addServletClassToServletContext(String packageName,Map<String, Class<?>> classMap){
            Set<Class<?>> setClasses =  ScanClassUtil.getClasses(packageName);
            for (Class<?> clazz :setClasses) {
                if (clazz.isAnnotationPresent(WebServlet.class)) {
                    //获取WebServlet这个Annotation的实例
                    WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
                    //获取Annotation的实例的value属性的值
                    String annotationAttrValue = annotationInstance.value();
                    if (!annotationAttrValue.equals("")) {
                        classMap.put(annotationAttrValue, clazz);
                    }
                    //获取Annotation的实例的urlPatterns属性的值
                    String[] urlPatterns = annotationInstance.urlPatterns();
                    for (String urlPattern : urlPatterns) {
                        classMap.put(urlPattern, clazz);
                    }
                    servletContext.setAttribute("servletClassMap", classMap);
                    System.out.println("annotationAttrValue:"+annotationAttrValue);
                    String targetClassName = annotationAttrValue.substring(annotationAttrValue.lastIndexOf("/")+1);
                    System.out.println("targetClassName:"+targetClassName);
                    System.out.println(clazz);
                }
            }
        }
    
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.out.println("---进入注解处理过滤器---");
            //将ServletRequest强制转换成HttpServletRequest
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse res = (HttpServletResponse)response;
            Map<String, Class<?>> classMap = (Map<String, Class<?>>) servletContext.getAttribute("servletClassMap");
            //获取contextPath
            String contextPath = req.getContextPath();
            //获取用户请求的URI资源
            String uri = req.getRequestURI();
            //如果没有指明要调用Servlet类中的哪个方法
            if (uri.indexOf("!")==-1) {
                //获取用户使用的请求方式
                String reqMethod = req.getMethod();
                //获取要请求的servlet路径
                String requestServletName = uri.substring(contextPath.length(),uri.lastIndexOf("."));
                //获取要使用的类
                Class<?> clazz = classMap.get(requestServletName);
                //创建类的实例
                Object obj = null;
                try {
                    obj = clazz.newInstance();
                } catch (InstantiationException e1) {
                    e1.printStackTrace();
                } catch (IllegalAccessException e1) {
                    e1.printStackTrace();
                }
                Method targetMethod = null;
                if (reqMethod.equalsIgnoreCase("get")) {
                    try {
                         targetMethod = clazz.getDeclaredMethod("doGet",HttpServletRequest.class,HttpServletResponse.class);
                    } catch (SecurityException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }else {
                    try {
                        targetMethod = clazz.getDeclaredMethod("doPost",HttpServletRequest.class,HttpServletResponse.class);
                    } catch (SecurityException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }
                
                try {
                    //调用对象的方法进行处理
                    targetMethod.invoke(obj,req,res);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }else {
                //获取要请求的servlet路径
                String requestServletName = uri.substring(contextPath.length(),uri.lastIndexOf("!"));
                //获取要调用的servlet的方法
                String invokeMethodName = uri.substring(uri.lastIndexOf("!")+1,uri.lastIndexOf("."));
            
                //获取要使用的类
                Class<?> clazz = classMap.get(requestServletName);
                //创建类的实例
                Object obj = null;
                try {
                    obj = clazz.newInstance();
                } catch (InstantiationException e1) {
                    e1.printStackTrace();
                } catch (IllegalAccessException e1) {
                    e1.printStackTrace();
                }
                //获得clazz类定义的所有方法
                Method[] methods = clazz.getDeclaredMethods();
                //获取WebServlet这个Annotation的实例
                WebServlet annotationInstance = clazz.getAnnotation(WebServlet.class);
                //获取注解上配置的初始化参数数组
                WebInitParam[] initParamArr = annotationInstance.initParams();
                Map<String, String> initParamMap = new HashMap<String, String>();
                for (WebInitParam initParam : initParamArr) {
                    initParamMap.put(initParam.paramName(), initParam.paramValue());
                }
                //遍历clazz类中的方法
                for (Method method : methods) {
                    //该方法的返回类型
                    Class<?> retType = method.getReturnType();
                    //获得方法名
                    String methodName = method.getName();
                    //打印方法修饰符
                    System.out.print(Modifier.toString(method.getModifiers()));
                    System.out.print(" "+retType.getName() + " " + methodName +"(");
                    //获得一个方法参数数组(getparameterTypes用于返回一个描述参数类型的Class对象数组)
                    Class<?>[] paramTypes = method.getParameterTypes();
                    for(int j = 0 ; j < paramTypes.length ; j++){
                         //如果有多个参数,中间则用逗号隔开,否则直接打印参数
                        if (j > 0){
                            System.out.print(",");
                        }  
                        System.out.print(paramTypes[j].getName());
                    }
                    System.out.println(");");
                    if (method.getName().equalsIgnoreCase("init")) {
                        try {
                            //调用Servlet的初始化方法
                            method.invoke(obj, initParamMap);
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
                //获取WebServlet这个Annotation的实例
                System.out.println("invokeMethodName:"+invokeMethodName);
                try {
                    try {
                        //利用反射获取方法实例,方法的签名必须符合:
                        //public void 方法名(HttpServletRequest request, HttpServletResponse response)
                        //例如:public void loginHandle(HttpServletRequest request, HttpServletResponse response)
                        Method targetMethod = clazz.getDeclaredMethod(invokeMethodName,HttpServletRequest.class,HttpServletResponse.class);
                        //调用对象的方法进行处理
                        targetMethod.invoke(obj,req,res);
                    } catch (SecurityException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } 
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void destroy() {
    
        }
    }

    AnnotationHandleFilter过滤器初始化时扫描指定的包下面使用了WebServlet注解的那些类,然后将类存储到一个Map集合中,再将Map集合存储到servletContext对象中。

      

      在web.xml文件中配置AnnotationHandleFilter过滤器和需要扫描的包

    <filter>
            <description>注解处理过滤器</description>
            <filter-name>AnnotationHandleFilter</filter-name>
            <filter-class>com.web.filter.AnnotationHandleFilter</filter-class>
            <init-param>
                <description>配置要扫描包及其子包, 如果有多个包,以逗号分隔</description>
                <param-name>basePackage</param-name>
                <param-value>com.web.controller,com.web.UI</param-value>
                <!-- <param-value>com.web.controller</param-value> -->
            </init-param>
        </filter>
        
        <filter-mapping>
            <filter-name>AnnotationHandleFilter</filter-name>
            <!-- 拦截后缀是.do的请求 -->
            <url-pattern>*.do</url-pattern>
        </filter-mapping>

    AnnotationHandleFilter过滤器初始化方法init(FilterConfig filterConfig)使用到了一个用于扫描某个包下面的类的工具类ScanClassUtil,ScanClassUtil的代码如下:

    package com.util;
    
    import java.io.File;
    import java.io.FileFilter;
    import java.io.IOException;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.Enumeration;
    import java.util.LinkedHashSet;
    import java.util.Set;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    public class ScanClassUtil {
    
        /**
         * 从包package中获取所有的Class
         * 
         * @param pack
         * @return
         */
        public static Set<Class<?>> getClasses(String pack) {
    
            // 第一个class类的集合
            Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
            // 是否循环迭代
            boolean recursive = true;
            // 获取包的名字 并进行替换
            String packageName = pack;
            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)) {
                        System.err.println("file类型的扫描");
                        // 获取包的物理路径
                        String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                        // 以文件的方式扫描整个包下的文件 并添加到集合中
                        findAndAddClassesInPackageByFile(packageName, filePath,
                                recursive, classes);
                    } else if ("jar".equals(protocol)) {
                        // 如果是jar包文件
                        // 定义一个JarFile
                        System.err.println("jar类型的扫描");
                        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) {
                                                // log
                                                // .error("添加用户自定义视图类错误 找不到此类的.class文件");
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                }
                            }
                        } catch (IOException e) {
                            // log.error("在扫描用户定义视图时从jar包获取文件出错");
                            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, Set<Class<?>> classes) {
            // 获取此包的目录 建立一个File
            File dir = new File(packagePath);
            // 如果不存在或者 也不是目录就直接返回
            if (!dir.exists() || !dir.isDirectory()) {
                // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
                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));
                         //经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                        classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));  
                        } catch (ClassNotFoundException e) {
                        // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    经过以上两步,我们的自定义注解和针对注解的处理器都开发好了。

    2.3、WebServlet注解简单测试

      编写一个用于跳转到Login.jsp页面的LoginUIServlet,LoginUIServlet就是一个普通的java类,不是一个真正的Servlet,然后使用WebServlet注解标注LoginUIServlet类,代码如下:

    package com.web.UI;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.annotation.WebServlet;
    
    @WebServlet("/servlet/LoginUI")
    public class LoginUIServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException{
            request.getRequestDispatcher("/Login.jsp").forward(request, response);
        }
        
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    }

    在浏览器中输入访问地址:http://gacl-pc:8080/AnnotationConfigServlet/servlet/Login.do,根据web.xml文件的配置,所有后缀名为 .do请求,都会经过AnnotationHandleFilter过滤器的doFilter方法,在doFilter方法的实现代码中,从HttpServletRequest请求对象中得到请求的方式类型(GET/POST)和请求的URI 。如有请求http://gacl-pc:8080/AnnotationConfigServlet/servlet/LoginUI.do,此时请求方法类型为GET, URI 值为/AnnotationConfigServlet/servlet/LoginUI.do。从ServletConext对象中获取到在过滤器中保存的Map结构,根据 URI 获得一个 Key=”/servlet/LoginUI” ,从 Map 结构中根据此Key得到Value ,此时Value就是要请求调用的那个Servlet类,根据Servlet类创建对象实例,再根据前面得到的请求方法类型,能决定调用此Servlet对象实例的 doGet 或 doPost 方法。最终客户端发生的后缀为.do请求,经由AnnotationHandleFilter对请求对象(HttpServletRequest)的分析,从而调用相应某Servlet的doGet或doPost方法,完成了一次客户端请求到服务器响应的过程。

      使用注解后程序流程如下所示:

      

      运行结果如下:

      

      从运行结果中可以看到,我们的注解处理器成功调用了目标Servlet处理用户的请求,通过@WebServlet注解, Servlet不用再在web.xml 文件中进行繁冗的注册,这就是使用@WebServlet注解的方便之处。

    2.3、WebServlet注解复杂测试

      编写Login.jsp页面,代码如下:

    <%@ page language="java" pageEncoding="UTF-8"%>
    <!DOCTYPE HTML>
    <html>
      <head>
        <title>登录页面</title>
      </head>
      
      <body>
        <fieldset>
            <legend>用户登录</legend>
            <form action="${pageContext.request.contextPath}/servlet/LoginServlet!loginHandle.do" method="post">
                用户名:<input type="text" value="${param.usename}" name="usename">
                <br/>
                密码:<input type="text" value="${param.pwd}" name="pwd">
                <br/>
                <input type="submit" value="登录"/>
            </form>
        </fieldset>
        <hr/>
        <label style="color: red;">${msg}</label>
      </body>
    </html>

    form表单中的action属性的URL="${pageContext.request.contextPath}/servlet/LoginServlet!loginHandle.do",/servlet/LoginServlet表示要访问的是LoginServlet!后面的loginHandle表示要调用LoginServlet中的loginHandle方法处理此次的请求,也就是说,我们在访问Servlet时,可以在URL中指明要访问Servlet的哪个方法,AnnotationHandleFilter过滤器的doFilter方法在拦截到用户的请求之后,首先获取用户要访问的URI,根据URI判断用户要访问的Servlet,然后再判断URI中是否包含了"!",如果有,那么就说明用户显示指明了要访问Servlet的哪个方法,遍历Servlet类中定义的所有方法,如果找到了URI中的那个方法,那么就调用对应的方法处理用户请求!

      LoginServlet的代码如下:

    package com.web.controller;
    
    import java.io.IOException;
    import java.util.Map;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.annotation.WebInitParam;
    import com.annotation.WebServlet;
    
    /**
    * 
    * @ClassName: LoginServlet
    * @Description:处理用户登录的Servlet,
    * LoginServlet现在就是一个普通的java类,不是一个真正的Servlet
    * @author: hdb
    * @date: 2017-11-8 上午12:07:58
    *
    */
    //将开发好的WebServlet注解标注到LoginServlet类上
    @WebServlet(
                //Servlet的访问URL
                value="/servlet/LoginServlet",
                //Servlet的访问URL,可以使用数组的方式配置多个访问路径
                urlPatterns={"/gacl/LoginServlet","/xdp/LoginServlet"},
                //Servlet的初始化参数
                initParams={
                        @WebInitParam(paramName="gacl",paramValue="hdb"),
                        @WebInitParam(paramName="bhsh",paramValue="白虎神皇")
                },
                name="LoginServlet",
                description="处理用户登录的Servlet"
            )
    public class LoginServlet {
    
        public void loginHandle(HttpServletRequest request, HttpServletResponse response) 
                throws ServletException, IOException{
            String username = request.getParameter("usename");
            String pwd = request.getParameter("pwd");
            if (username.equals("gacl") && pwd.equals("xdp")) {
                request.getSession().setAttribute("usename", username);
                request.setAttribute("msg", "欢迎您!"+username);
                request.getRequestDispatcher("/index.jsp").forward(request, response);
            }else {
                request.setAttribute("msg", "登录失败,请检查用户名和密码是否正确!");
                request.getRequestDispatcher("/Login.jsp").forward(request, response);
            }
        }
        
        
        /**
        * @Method: init
        * @Description: Servlet初始化
        * @Anthor:hdb
        *
        * @param config
        */ 
        public void init(Map<String, String> initParamMap){
            System.out.println("--LoginServlet初始化--");
            System.out.println(initParamMap.get("gacl"));
            System.out.println(initParamMap.get("bhsh"));
        }
    }

    运行结果如下:

      

      可以看到,我们使用注解方式配置的Servlet已经成功调用了,loginHandle方法处理用户登录请求的完整处理过程如下图所示:

      

      Servlet3.0是支持采用基于注解的方式配置Servlet的,在此我使用过滤器作为注解处理器模拟模拟出了类似Servlet3.0的注解处理方式,简化了Servlet的配置。这种使用自定义注解+注解处理器的方式山寨出来的Servlet3.0大家了解一下即可,了解一下这种处理思路,在实际应用中还是不要这么做了,要真想使用注解的方式配置Servlet还是直接用Servlet3.0吧。

  • 相关阅读:
    操作系统发展、同步异步阻塞非阻塞、进程
    day27作业
    tcp粘包、解决粘包问题
    基于TCP连接的socket套接字编程
    《python》join、守护进程、锁/信号量/事件、进程队列
    《Python》并发编程
    《Python》网络编程之验证客户端连接的合法性、socketserver模块
    《Python》网络编程之客户端/服务端框架、套接字(socket)初使用
    《Python》网络编程基础
    《Python》re模块补充、异常处理
  • 原文地址:https://www.cnblogs.com/huangdabing/p/9219444.html
Copyright © 2011-2022 走看看