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();
        }
    }
 
}
复制代码

 

---汇智网  

posted @   yangly  阅读(1427)  评论(1编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示