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吧。

  • 相关阅读:
    正经学C#_循环[do while,while,for]:[c#入门经典]
    Vs 控件错位 右侧资源管理器文件夹点击也不管用,显示异常
    asp.net core 获取当前请求的url
    在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be
    用orchard core和asp.net core 3.0 快速搭建博客,解决iis 部署https无法登录后台问题
    System.Data.Entity.Core.EntityCommandExecution The data reader is incompatible with the specified
    初探Java设计模式3:行为型模式(策略,观察者等)
    MySQL教程77-CROSS JOIN 交叉连接
    MySQL教程76-HAVING 过滤分组
    MySQL教程75-使用GROUP BY分组查询
  • 原文地址:https://www.cnblogs.com/huangdabing/p/9219444.html
Copyright © 2011-2022 走看看