• java web框架


    框架结构说明

    框架采用MVC模式,Ioc控制反转技术。通过框架来对应用进行初始化和管理,提高开发效率。

    若使用传统的Servlet来开发Java Web,Servlet的数量会随着业务功能的扩展而不断增加,系统变得庞大,然以维护,有必要减少Servlet数量,
    将某类业务交给Controller来处理,Service负责给Controller提供服务。Service不是通过new方式来创建的,而是通过"依赖注入"的方式,由框架来创建所需要的对象。

    框架结构图:

     


    DispatherServlet: 请求转发器,通过service()方法转发所有的请求。

    Loader: 在DispatherServlet的init()方法调用,进行应用的初始化工作。

    ClassHelper:通过ClassUtil类加载应用基础包下所有的类。

    BeanContainer:通过BeanFactory将ClassHelper加载获取的所有带有Controller,Service注解等需要容器管理的类进行实例化并保存在容器中。

    IocHelper:Controller中定义Service成员变量,需要通过框架自身来实例化。IocHelper将Controller中定义 Service成员变量进行依赖注入。

    ControllerHelper:将Controller中带有RequestMapping注解的方法与其要处理的请求路径和请求方法建立映射关系。

    创建Maven Web工程 framework-diy,在pom.xml文件引入需要的包。servlet-api,jsp-api,jstl,commons-lang3,commons-collections4,jackson等。

    <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.2</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.3.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-collections4</artifactId>
                <version>4.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.4.5</version>
            </dependency>

    加载配置项

    首先我们来看下Spring框架,Spring框架定义了一个spring-config.xml配置文件,可以在配置文件定义基础包名,JSP基础路径,静态资源文件的路径等等。

    现在我们来仿造一下,为框架定义一个简单的配置文件config.properties(默认配置文件为config.properties),放在/src/main/resources目录下。框架需要根据这些配置项来初始化应用。
    //config.properties

    #基础包名
    app.base_package = com.hubwiz.web
    #jsp路径
    app.jsp_path = /jsp/
    #静态资源路径
    app.asset_path = /asset/

    有了配置文件,我们需要编写一个PropsUtil类来加载配置文件config.properties。获取当前线程的类加载器,getContextClassLoader().getResourceAsStream(fileName)根据文件名称来加载配置文件。

    //加载配置文件
    public class PropsUtil {
     
        /**
         * 加载配置文件
         *
         * @param fileName 配置文件名
         * @return
         */
        public static Properties loadProps(String fileName) {
            Properties props = null;
            InputStream is = null;
            try {
                is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
                if (is == null) {
                    throw new FileNotFoundException(fileName + " file is not found");
                }
                props = new Properties();
                props.load(is);
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException ioe) {
                        ioe.printStackTrace();
                    }
                }
            }
            return props;
        }
     
        /**
         * 获取字符型属性(默认值为空字符串)
         *
         * @param props
         * @param key
         * @return
         */
        public static String getString(Properties props, String key) {
            return getString(props, key, "");
        }
     
        /**
         * 获取字符型属性(可指定默认值)
         *
         * @param props
         * @param key
         * @param defaultValue
         * @return
         */
        public static String getString(Properties props, String key, String defaultValue) {
            String value = defaultValue;
            if (props.containsKey(key)) {
                value = props.getProperty(key);
            }
            return value;
        }
     
    }


    获取配置文件属性

    加载了配置文件,需要编写一个类ConfigHelper来获取配置文件属性。框架初始化需要根据配置文件来初始化应用。框架默认配置文件为config.properties,只有创建了名为config.properties的配置文件,框架才能运行。

    /**
     * 获取配置文件属性
     */
    public class ConfigHelper {
     
        private static final Properties CONFIG_PROPS = PropsUtil.loadProps("config.properties");//默认配置文件为config.properties
     
        /**
         * 获取应用基础包名
         *
         * @return
         */
        public static String getAppBasePackage() {
            return PropsUtil.getString(CONFIG_PROPS, Constant.APP_BASE_PACKAGE);
        }
     
        /**
         * 获取JSP路径
         *
         * @return
         */
        public static String getAppJspPath() {
            return PropsUtil.getString(CONFIG_PROPS, Constant.APP_JSP_PACKAGE);
    
        }
     
        /**
         * 获取应用静态资源路径
         *
         * @return
         */
        public static String getAppAssetPath() {
            return PropsUtil.getString(CONFIG_PROPS, Constant.APP_ASSET_PATH, "/asset/");
        }
     
    }


    类加载器

    实现一个类加载器ClassUtil来加载基础包下的所有的类。只有加载了这些类,框架才能对其进行初始化。获取当前线程的ClassLoader通过这个类加载器来加载类,获取指定包名下的所有的类,需要指定根据包名并将其转换为文件路径,读取class文件或jar包,获取指定的类名去加载类。

     /**
         * 获取类加载器
         *
         * @return
         */
        public static ClassLoader getClassLoader() {
            return Thread.currentThread().getContextClassLoader();
        }
     
        /**
         * 加载类
         *
         * @param className 类名称
         * @param isInitialized 是否执行类的静态代码块
         * @return
         */
        public static Class<?> loadClass(String className, boolean isInitialized) {
            Class<?> clazz;
            try {
                clazz = Class.forName(className, isInitialized, getClassLoader());
            } catch (ClassNotFoundException cnfe) {
                cnfe.printStackTrace();
                throw new RuntimeException(cnfe);
            }
            return clazz;
        }
     
        /**
         * 获取指定包下所有类
         *
         * @param packageName
         * @return
         */
        public static ArrayList<Class<?>> getClasses(String packageName) {
            ArrayList<Class<?>> classes = new ArrayList<>();
            try {
                Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if (url != null) {
                        String protocol = url.getProtocol();
                        if (protocol.equals("file")) {
                            String packagePath = url.getPath().replaceAll("%20", " ");
                            addClass(classes, packagePath, packageName);
                        } else if (protocol.equals("jar")) {
                            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                            if (jarURLConnection != null) {
                                JarFile jarFile = jarURLConnection.getJarFile();
                                if (jarFile != null) {
                                    Enumeration<JarEntry> jarEntries = jarFile.entries();
                                    while (jarEntries.hasMoreElements()) {
                                        JarEntry jarEntry = jarEntries.nextElement();
                                        String jarEntryName = jarEntry.getName();
                                        if (jarEntryName.equals(".class")) {
                                            String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                            doAddClass(classes, className);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
               e.printStackTrace();
                throw new RuntimeException(e);
            }
            return classes;
        }
        ...
        ...
        ...
    }


    定义注解

    本框架采用注解来标识Controller,Service类。所以需要定义注解来标识那些类是Controller,Controller类中那些方法响应url请求等。

    控制器类上使用Controller注解,在控制器类的方法上使用RequestMapping注解,使用Autowired注解将服务类依赖注入进来。服务类使用Service注解。

    /**
     * 控制器注解
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Controller {
    }
     
    /**
     * 请求方法注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequestMapping {
     
        //请求类型路径
        String path();
        //请求方法
        String method();
    }
     
    /**
     * 服务类注解
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Service {
    }
     
    /**
     * 依赖注入注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Autowired {
    }


    获取类

    Controller,Service类是框架需要管理的类,把他们统称为Bean类。实现一个类可以获取应用所有的Controller,Service类。首先调用ConfigHelper.getAppBasePackage()获取基础包名,然后调用ClassUtil.getClasses(basePackage)加载该基础包名下所有的类,保存在变量ArrayList> classes中;再遍历ArrayList> classes获取Controller,Service等类。

    //获取类
    public class ClassHelper {
     
        //基础包名下所有的类
        private static final ArrayList<Class<?>> classes;
     
        static {
            String basePackage = ConfigHelper.getAppBasePackage();
            classes = ClassUtil.getClasses(basePackage);
        }
     
        /**
         * 获取基础包名下所有的类
         *
         * @return
         */
        public static ArrayList<Class<?>> getClasses() {
            return classes;
        }
     
        /**
         * 获取所有Service类
         *
         * @return
         */
        public static ArrayList<Class<?>> getServiceClasses() {
            ArrayList<Class<?>> sc = new ArrayList<>();
            //补全代码
     
            return sc;
        }
     
        /**
         * 获取所有Controller类
         *
         * @return
         */
        public static ArrayList<Class<?>> getControllerClasses() {
            ArrayList<Class<?>> cc = new ArrayList<>();
            for (Class<?> c : classes) {
                if (c.isAnnotationPresent(Controller.class)) {
                    cc.add(c);
                }
            }
            return cc;
        }
     
        /**
         * 框架Bean容器主要管理Service,Controller类
         *
         * @return
         */
        public static ArrayList<Class<?>> getBeanClasses() {
            ArrayList<Class<?>> bc = new ArrayList<>();
            bc.addAll(getServiceClasses());
            bc.addAll(getControllerClasses());
            return bc;
        }
    }
    现在来测试类加载器和获取类是否正确,配置文件将基础包名设为app.base_package=com.hubwiz.web.controller,在这个包下,实现了三个类HomeController类(带Controller注解),PersonService类(带Service注解),Person类。
    
    public static void main(String[] args) {
            ArrayList<Class<?>> ces = ClassHelper.getClasses();
            for (Class<?> c : ces) {
                System.out.println(c.getSimpleName());
            }
        }


    Bean工厂

    回顾下前面的知识,通过加载配置文件获取应用基础包名,加载基础包名下所有的类,获取Controller,Service类。到目前为止,我们只是加载了类,但是无法通过获取的类来实例化对象。因此需要一个反射工具,来实例化类。

    创建一个Bena工厂,来生产(实例化Bean类对象)Bean。newInstance()方法,实例化目标类;invokeMethod()通过反射机制来调用类中的方法;setField()通过反射机制为类成员遍历赋值。

    //Bean工厂
    public class BeanFactory {
     
        /**
         * 创建实例
         *
         * @param clazz
         * @return
         */
        public static Object newInstance(Class<?> clazz) {
            Object instance;
            try {
                instance = clazz.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return instance;
        }
     
        /**
         * 方法调用
         *
         * @param obj
         * @param method
         * @param args
         * @return
         */
        public static Object invokeMethod(Object obj, Method method, Object... args) {
            Object result;
            try {
                method.setAccessible(true);
                result = method.invoke(obj, args);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return result;
        }
     
        /**
         * 设置成员变量值
         *
         * @param obj
         * @param field
         * @param value
         */
        public static void setField(Object obj, Field field, Object value) {
            try {
                field.setAccessible(true);
                field.set(obj, value);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }


    Bean容器

    前面在ClassHelper定义了方法getBeanClasses()来获取Bean容器需要管理的所有Controller,Service类,获取这些类以后,调用BeanFactory.newInstance(Class clazz)方法来实例化类的对象,缓存在Map beanContainer中,需要随时获取。Map说明:使用类全名称作为key,类的实例对象作为value值。

    BeanContainer初始化:首先调用ClassHelper.getBeanClasses()获取所有的Bean类,调用Bean工厂方法newInstance()方法来实例化Bean类,保存在beanContainer中。通过类全名称从beanContainer获取需要的Bean实例对象。

    /**
     * Bean容器
     */
    public class BeanContainer {
     
        /**
         * 存放Bean类名称和Bean实例的映射关系
         */
        private static final Map<String, Object> beanContainer = new HashMap<>();
     
        static {
            ArrayList<Class<?>> beanClasses = ClassHelper.getBeanClasses();
            for (Class<?> beanClass : beanClasses) {
                Object obj = BeanFactory.newInstance(beanClass);
                beanContainer.put(beanClass.getName(), obj);
            }
        }
     
        /**
         * 获取Bean映射
         *
         * @return
         */
        public static Map<String, Object> getBeanContainer() {
            return beanContainer;
        }
     
        /**
         * 获取Bean实例
         */
        public static <T> T getBean(String className) {
            if (!beanContainer.containsKey(className)) {
                throw new RuntimeException("can not get bean by className: " + className);
            }
            return (T) beanContainer.get(className);
        }
     
        /**
         * 设置Bean实例
         */
        public static void setBean(String className, Object obj) {
            //补全代码
        }
    }


    依赖注入

    首先来了解下Spring框架的核心机制:依赖注入。当某个Java实例(调用者)需要另一个Java实例(被调用者)时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。在依赖注入模式下,创建调用者的工作不再由调用者来完成,因此称为控制反转(Ioc);创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也成为依赖注入(DI)。

    Controller中定义Service成员变量,需要通过框架自身来实例化。

    首先通过beanContainer获取所有的Bean类全名称和Bean实例,遍历获取所有的Controller类,通过反射获取类中的成员变量,遍历这些成员变量看是否有Autowired注解,若有,从beanContainer中获取Bean实例,然后调用Bean工厂setField()方法将获取的Bean实例赋值给成员变量。

    /**
     * 依赖注入
     */
    public final class IocHelper {
     
        static {
            Map<String, Object> beanContainer = BeanContainer.getBeanContainer();
            if (CollectionUtil.isNotEmpty(beanContainer)) {
                initIOC(beanContainer);
            }
        }
     
        private static void initIOC( Map<String, Object> beanContainer) {
            for (Map.Entry<String, Object> beanEntry : beanContainer.entrySet()) {
                String className = beanEntry.getKey();
                Object beanInstance = beanEntry.getValue();
                Class<?> beanClass = null;
                try {
                    beanClass = Class.forName(className);
                    System.out.println(className);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                //Controller类中定义的属性
                Field[] beanFields = beanClass.getDeclaredFields();
                if (ArrayUtil.isNotEmpty(beanFields)) {
                    for (Field beanField : beanFields) {
                        //带有Autowired注解的成员变量
                        if (beanField.isAnnotationPresent(Autowired.class)) {
                            //成员变量的类
                            Class<?> beanFieldClass = beanField.getType();
                            Object beanFieldInstance = beanContainer.get(beanFieldClass.getName());
                            if (beanFieldInstance != null) {
                                //依赖注入
                                BeanFactory.setField(beanInstance, beanField, beanFieldInstance);
                            }
                        }
                    }
                }
            }
        }
    }


    Request

    Controller类中带有RequestMapping注解的方法处理特定URL请求,如何判断当前请求 URL&Method 对应那个Controller & method,这是接下来要实现的。通过反射可以获取Controller中带有RequestMapping注解的方法,进而获得RequestMapping注解中请求方法和请求路径。封装一个请求对象request与处理request对象handler,将request与handler建立一个映射关系。

    请求对象request:包括请求路径path;请求方法method两个属性。

    处理request对象handler:包括Controller类;带有RequestMapping注解的方法method。

    /**
     * 封装请求信息
     */
    public class Request {
        //请求方法
        private String requestMethod;
        //请求路径
        private String requestPath;
     
        public Request(String requestMethod,String requestPath) {
            this.requestMethod = requestMethod;
            this.requestPath = requestPath;
        }
     
        public String getRequestMethod() {
            return requestMethod;
        }
     
        public String getRequestPath() {
            return requestPath;
        }
     
        @Override
        public int hashCode() {
            return HashCodeBuilder.reflectionHashCode(this);
        }
     
        @Override
        public boolean equals(Object obj) {
            return EqualsBuilder.reflectionEquals(this,obj);
        }
    }
    /**
     * 处理Request请求对应Controller & method
     */
    public class Handler {
     
        private Class<?> controllerClass;
     
        private Method method;
     
        public Handler(Class<?> controllerClass,Method method) {
            this.controllerClass = controllerClass;
            this.method = method;
        }
     
        public Class<?> getControllerClass() {
            return controllerClass;
        }
     
        public Method getMethod() {
            return method;
        }
    }


    RequestMapping
    编写一个类ControllerHelper,将Controller类中定义处理Requet的方法,与Handler绑定。通过请求路径与请求方法我们能方便找到处理这个请求的Controller类和method。

    public class ControllerHelper {
     
        //请求request与处理请求handler映射关系
        private static final Map<Request, Handler> RequestMap = new HashMap<>();
     
        static {
            ArrayList<Class<?>> controllerClasses = ClassHelper.getControllerClasses();
            if (CollectionUtil.isNotEmpty(controllerClasses)) {
                initRequestMapp(controllerClasses);
            }
        }
     
        private static void initRequestMapp(ArrayList<Class<?>> controllerClasses) {
            for (Class<?> controllerClass : controllerClasses) {
                Method[] methods = controllerClass.getDeclaredMethods();
                if (ArrayUtil.isNotEmpty(methods)) {
                    for (Method method : methods) {
                        //带有RequestMapping注解的方法
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping rm = method.getAnnotation(RequestMapping.class);
                            //请求路径与请求方法
                            Request request = new Request(rm.method(), rm.path());
                            //对应请求路径与请求方法的Controller和method
                            Handler handler = new Handler(controllerClass, method);
                            RequestMap.put(request, handler);
                        }
                    }
                }
            }
        }
     
        /**
         * 获取handler
         *
         * @param requestMethod
         * @param requestPath
         * @return
         */
        public static Handler getHandler(String requestMethod, String requestPath) {
            Request request = new Request(requestMethod, requestPath);
            return RequestMap.get(request);
        }
    }


    初始化类

    框架基本搭建起来了,现在需要编写一个类来初始化应用。

    初始化步骤:

    1、加载ClassHelper类,通过这个类加载基础包名下所有的类。

    2、加载BeanContainer类,将基础包名下所有Bean类,通过Bean工厂实例化保存在Bean容器。

    3、加载IocHelper类,实例化Bean类,需要为Controller类中带有Autowired注解的属性赋值。

    4、加载ControllerHelper类,将Controller类中带有RequestMapping注解的方法,建立与请求路径和请求方法的映射关系,这样框架才能找到处理请求对应的方法。

    /**
     * 初始化框架
     */
    public class Loader {
     
        public static void init() {
            Class<?>[] cs = {ClassHelper.class,
                    BeanContainer.class,
                    IocHelper.class,
                    ControllerHelper.class};
            for (Class<?> c: cs) {
                ClassUtil.loadClass(c.getName(),true);
            }
        }
    }


    请求参数

    通常前端通过form表格的形式向后台发送数据,需要一个类封装从HttpServletRequest请求对象中获取所有的参数,然后传递给处理方法。首先解析请求参数(form表单数据),将其封装成Param类中,传递给Controller的方法处理。

    通过request.getParameterNames()将发送请求页面中form表单所具有name属性的表单对象获取,request.getParameterValues(name)获取其值,生成FormParam保存在Param中。

    /**
     * 解析请求参数,form表单数据
     */
    public class ParameterUtil {
     
        public static Param createParam(HttpServletRequest request) throws IOException {
            return new Param(parseParameterNames(request));
        }
     
        private static Map<String,Object> parseParameterNames(HttpServletRequest request) {
            Map<String,Object> formParams = new HashMap<>();
            //将发送请求页面中form表单所具有name属性的表单对象获取
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String fieldName = paramNames.nextElement();
                String[] fieldValues = request.getParameterValues(fieldName);
                if (ArrayUtil.isNotEmpty(fieldValues)) {
                    Object fieldValue;
                    if (fieldValues.length == 1) {
                        fieldValue = fieldValues[0];
                    } else {
                        StringBuilder sb = new StringBuilder("");
                        for (int i = 0; i < fieldValues.length; ++i) {
                            sb.append(fieldValues[i]);
                            if (i != fieldValues.length - 1) {
                                sb.append(StringUtil.separator);
                            }
                        }
                        fieldValue = sb.toString();
                    }
                    formParams.put(fieldName,fieldValue);
                }
            }
            return formParams;
        }
     
    }


    Param请求参数对象,封装了前端提交的form表格数据。

    /**
     * 请求参数对象
     */
    public class Param {
     
        private Map<String,Object> formParams;
     
        public Param(Map<String,Object> formParams) {
            this.formParams = formParams;
        }
     
        /**
         * 判断参数是否为空
         *
         * @return boolean
         */
        public boolean isEmpty() {
            return CollectionUtil.isEmpty(formParams);
        }
     
        public Map<String, Object> getFormParams() {
            return formParams;
        }
     
        /**
         * 根据参数名取String型参数值
         *
         * @param name
         * @return
         */
        public String getString(String name) {
            return CastUtil.castString(formParams.get(name));
        }
     
        public double getDouble(String name) {
            return CastUtil.castDouble(formParams.get(name));
        }
     
        public long getLong(String name) {
            return CastUtil.castLong(formParams.get(name));
        }
     
        public int getInt(String name) {
            return CastUtil.castInt(formParams.get(name));
        }
     
        public Boolean getBoolean(String name) {
            return CastUtil.castBoolean(formParams.get(name));
        }
     
    }


    模型数据与视图

    在处理请求时,通常会返回视图JSP页面和数据。所以现在需要将视图JSP路径和数据封装在一起返回。如果只返回数据,则返回JSON格式数据。

    返回视图JSP,视图中包含视图JSP路径和视图中所需的数据:

    public class ModelAndView {
     
        //返回JSP路径
        private String path;
     
        //模型数据
        private Map<String,Object> mData;
     
        public ModelAndView(String path) {
            this.path = path;
            mData = new HashMap<>();
        }
     
        public ModelAndView addmData(String key, Object obj) {
            mData.put(key,obj);
            return this;
        }
     
        public String getPath() {
            return path;
        }
     
     
        public Map<String, Object> getmData() {
            return mData;
        }
     
    }


    返回数据,框架将其写入HttpServletRespone对象中,输出到客户端浏览器。

    /**
     * 返回数据
     */
    public class Data<T> {
     
     
        private T datas;
     
        public Data(T datas) {
            this.datas = datas;
        }
     
        public T getDatas() {
            return datas;
        }
    }


    数据格式转换

    由于返回的数据格式为JSON,现在需要将POJO转换成JSON格式。采用Jackson框架。

    /**
     * POJO转换为JSON
     */
    public class JsonUtil {
     
        /**
         * 将POJO转化为JSON
         *
         * @param obj
         * @param <T>
         * @return
         */
        public static <T> String toJSON(T obj) {
            String json = null;
            try {
                ObjectMapper mapper = new ObjectMapper();
                json = mapper.writeValueAsString(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return json;
        }
     
        /**
         * 将JSON转化为POJO
         *
         * @param json
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T fromJSON(String json, Class<T> clazz) {
            T pojo = null;
            try {
                ObjectMapper mapper = new ObjectMapper();
                pojo = mapper.readValue(json, clazz);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return pojo;
        }
    }


    请求转发器

    请求转发器是框架的核心。请求转发器转发器用来处理所有的请求。DispatherServlet继承HttpServlet,在init()方法调用Loader.init()来初始化框架和应用。service()方法响应所有的请求。

    请求转发过程:

    1、通过req.getMethod().toLowerCase()获取请求方法(get,post,put delete);req.getContextPath()获取请求路径,根据请求路径和请求方法调用ControllerHelper.getHandler()方法获取处理这个请求对应的handler。

    2、Hanler封装处理请求的Controller和方法,所有获取的handler,就可以获取处理请求的Controller和方法method。

    3、获取了处理这个请求的Controller类,现在需要在Bean获取这个Controller类的实例对象,调用BeanContainer.getBean()来获取Controller类的实例对象。

    4、调用ParameterUtil.createParam(req)来解析请求参数。

    5、调用handler.getMethod()获取处理这个请求的方法,通过反射机制BeanFactory.invokeMethod()来调用这个方法,处理这个请求的方法就会来处理这个请求。

    6、根据返回的结果是ModelAndView还是Data来处理返回问题。返回结果为ModelAndView,调用req.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(req, resp)将页面响应转发到ConfigHelper.getAppJspPath() + path;返回结果为Data,将返回结果POJO转换为JSON格式,写入HttpServletRespone对象中,输出到客户端浏览器。

    /**
     * 请求转发器
     */
    @WebServlet(urlPatterns = "/",loadOnStartup = 0)
    public class DispatherServlet extends HttpServlet {
     
        @Override
        public void init(ServletConfig config) throws ServletException {
            Loader.init();//初始化框架&应用
            ServletContext sc = config.getServletContext();
            //注册JSP的Servlet
            ServletRegistration jspServlet = sc.getServletRegistration("jsp");
            jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
            //注册处理静态资源的Servlet
            ServletRegistration defaultServlet = sc.getServletRegistration("default");
            defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
        }
     
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取请求方法
            String requestMethod = req.getMethod().toLowerCase();
            //请求路径url
            String url = req.getRequestURI();
            String contextPath = req.getContextPath();
            String requestPath = null;
            if (contextPath != null && contextPath.length() > 0) {
                requestPath = url.substring(contextPath.length());
            }
            //获取处理这个请求的handler
            Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
           // System.out.println(requestMethod + "  " + requestPath);
            if (handler != null) {
                Class<?> controllerClass = handler.getControllerClass();
                Object controllerBean =    BeanContainer.getBean(controllerClass.getName());
                //解析请求参数
                Param param = ParameterUtil.createParam(req);
                Object result;//请求返回对象
                Method method = handler.getMethod();//处理请求的方法
                if (param.isEmpty()) {
                    result = BeanFactory.invokeMethod(controllerBean, method);
                } else {
                    result = BeanFactory.invokeMethod(controllerBean, method, param);
                }
                if (result instanceof ModelAndView) {
                    handleViewResult((ModelAndView) result, req, resp);
                } else {
                    handleDataResult((Data) result, resp);
                }
            }
        }
     
        //返回为JSP页面
        private static void handleViewResult(ModelAndView view, HttpServletRequest req, HttpServletResponse resp)
                throws IOException, ServletException {
            String path = view.getPath();
            if (StringUtil.isNotEmpty(path)) {
                if (path.startsWith("/")) {
                    resp.sendRedirect(req.getContextPath() + path);
                } else {
                    Map<String, Object> data = view.getmData();
                    for (Map.Entry<String, Object> entry : data.entrySet()) {
                        req.setAttribute(entry.getKey(), entry.getValue());
                    }
                    //forward将页面响应转发到ConfigHelper.getAppJspPath() + path
                    //补全代码
     
                }
            }
        }
     
        //返回JSON数据
        private static void handleDataResult(Data data, HttpServletResponse resp)
                throws IOException {
            Object model = data.getData();
            if (model != null) {
                resp.setContentType("application/json");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter writer = resp.getWriter();
                String json = JsonUtil.toJSON(model);
                writer.write(json);
                writer.flush();
                writer.close();
            }
        }
     
    }

    ---汇智网  

  • 相关阅读:
    系统分析与设计——作业9
    系统分析与设计——作业8
    系统分析与设计——作业7
    系统分析与设计——作业6
    系统分析与设计——作业5
    系统分析与设计——作业4
    从循环添加事件谈起对JS闭包的理解
    对JS prototype的理解
    系统分析与设计——作业2
    JavaScript 预编译(变量提升和函数提升的原理)
  • 原文地址:https://www.cnblogs.com/lely/p/10364128.html
Copyright © 2020-2023  润新知