博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java web框架
阅读量:4963 次
发布时间:2019-06-12

本文共 26340 字,大约阅读时间需要 87 分钟。

框架结构说明

框架采用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等。

javax.servlet
javax.servlet-api
3.0.1
javax.servlet.jsp
jsp-api
2.2
javax.servlet
jstl
1.2
org.apache.commons
commons-lang3
3.3.2
org.apache.commons
commons-collections4
4.0
com.fasterxml.jackson.core
jackson-databind
2.4.5

加载配置项

首先我们来看下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
> getClasses(String packageName) { ArrayList
> classes = new ArrayList<>(); try { Enumeration
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
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
> classes; static { String basePackage = ConfigHelper.getAppBasePackage(); classes = ClassUtil.getClasses(basePackage); } /** * 获取基础包名下所有的类 * * @return */ public static ArrayList
> getClasses() { return classes; } /** * 获取所有Service类 * * @return */ public static ArrayList
> getServiceClasses() { ArrayList
> sc = new ArrayList<>(); //补全代码 return sc; } /** * 获取所有Controller类 * * @return */ public static ArrayList
> getControllerClasses() { ArrayList
> cc = new ArrayList<>(); for (Class
c : classes) { if (c.isAnnotationPresent(Controller.class)) { cc.add(c); } } return cc; } /** * 框架Bean容器主要管理Service,Controller类 * * @return */ public static ArrayList
> getBeanClasses() { ArrayList
> 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
> 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
beanContainer = new HashMap<>(); static { ArrayList
> beanClasses = ClassHelper.getBeanClasses(); for (Class
beanClass : beanClasses) { Object obj = BeanFactory.newInstance(beanClass); beanContainer.put(beanClass.getName(), obj); } } /** * 获取Bean映射 * * @return */ public static Map
getBeanContainer() { return beanContainer; } /** * 获取Bean实例 */ public static
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
beanContainer = BeanContainer.getBeanContainer(); if (CollectionUtil.isNotEmpty(beanContainer)) { initIOC(beanContainer); } } private static void initIOC( Map
beanContainer) { for (Map.Entry
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
RequestMap = new HashMap<>(); static { ArrayList
> controllerClasses = ClassHelper.getControllerClasses(); if (CollectionUtil.isNotEmpty(controllerClasses)) { initRequestMapp(controllerClasses); } } private static void initRequestMapp(ArrayList
> 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
parseParameterNames(HttpServletRequest request) { Map
formParams = new HashMap<>(); //将发送请求页面中form表单所具有name属性的表单对象获取 Enumeration
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
formParams; public Param(Map
formParams) { this.formParams = formParams; } /** * 判断参数是否为空 * * @return boolean */ public boolean isEmpty() { return CollectionUtil.isEmpty(formParams); } public Map
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
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
getmData() { return mData; } }

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

/** * 返回数据 */public class Data
{ 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 
* @return */ public static
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
* @return */ public static
T fromJSON(String json, Class
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
data = view.getmData(); for (Map.Entry
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(); } } }

 

---汇智网  

转载于:https://www.cnblogs.com/lely/p/10364128.html

你可能感兴趣的文章
三元表达,匿名函数
查看>>
前端笔记-基础笔记
查看>>
【LeetCode & 剑指offer刷题】查找与排序题6:33. Search in Rotated Sorted Array(系列)
查看>>
GNU/Linux超级本ZaReason Ultralap 440体验
查看>>
将github上托管的代码 在我的域名下运行
查看>>
【Manthan, Codefest 18 (rated, Div. 1 + Div. 2) C】Equalize
查看>>
【codeforces 767A】Snacktower
查看>>
【MemSQL Start[c]UP 3.0 - Round 1 C】 Pie Rules
查看>>
Ognl中“%”、“#”、“$”详解
查看>>
我对应用软件——美团的看法
查看>>
执行了的程序,才是你的程序.
查看>>
struts2.x + Tiles2.x读取多个xml 配置文件
查看>>
表单校验之datatype
查看>>
python第六篇文件处理类型
查看>>
hdu 3183 A Magic Lamp 贪心
查看>>
ubuntu16系统磁盘空间/dev/vda1占用满的问题
查看>>
面试题14 调整数组顺序使奇数位于偶数前面
查看>>
grid网格布局
查看>>
flask简单的注册功能
查看>>
JSP常用标签
查看>>