java mvc返回js_猿进化系列13——一文搞懂MVC相关框架套路

看完上一个章节,相信你已经掌握了JDBC访问数据库的基本操作,也学会了使用数据源和数据库连接池,还学会了一个小框架——SpringJbdcTemplete,回过头来看看,似乎你已经掌握了,不少东西了,java,jsp,servlet,jstl,jdbc……看起来已经有足够的实力去开发一个动态web站点了。不过在这之前,似乎有一些东西还需要思考一下。

猿进化是一个原创系列文章,帮助你从一只小白快速进化为一个猿人,更多精彩内容,由于文字篇幅有限,讲得不明白的地方,有疑问,还是私信我吧,黑机构太多,老是举报我,大号都被封号了(由此可见对这些黑机构的伤害有多深)。

cde1f3c90c50b49068da53d1e8d4e36e.png

4be6be2d733af8dbc83f1839de853588.png

698415bb670f35d2dadbcd33daa82a08.png

a96f4831a7fe78b1dc079ba01671591d.png

d761b27038bfb9a75a114c12fec4521d.png

9ecd760809d9b5ad54d5e6ec34b9cab0.png

be76e0318e895f64de63f91508d3c924.png

f7c50682a0e73d81e4e12dc737621056.png

在聊web框架之前,我们先来思考一个事情——为什么会有JSP?我们知道JSP运行在服务端,可以在页面中编写java代码,甚至可以在页面中访问数据库,然后生成一段HTML代码,然后发给客户端,大大的简化了远古时期的应用开发问题——在servlet中使用out输出HTML代码。后来有的人认为这样真的很爽,JSP页面相互就能完成跳转,servlet几乎用不上,还专门给起了个名字——JSP Model1.

16f946e48bb4b106abc20e5a89c0499d.png

0ec697e9abe4e26c739ca6eda0ed5b0d.png

JSP Model1刚开始的时候,谁用谁爽,简单直接敞开撸,可是慢慢的系统越来越庞大,java代码,html代码,css代码,js代码……都在一个页面里,各种标签还满天飞,没法维护了。最后,大家发现各种代码还是分开写比较好。于是servlet就被再次利用了起来——好歹是个堆代码的地方,代码和标签不用放在一起,于是就搞出了下面这种模式——MVC.

291e388298d761870f9061a5db39f8f6.png

MVC是Model ViewController(模型-视图-控制器)的缩写,被广泛的应用到开发工作中。

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。

View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。

Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

MVC模式对软件开发有着极为重要的意义:有助于程序的分层开发,可以让人专注于某一方面的逻辑,比如不依赖业务逻辑也能开发视图层面的东西。对程序来说也是一个解耦的过程,各类程序分开编写,有助于代码的维护。更可贵的是,正是因为有了这样的分层,让职业更加细分,最早开发程序,大家都是一个人从头撸到尾,现在可以让擅长展示层面的人发挥特长,做前端工程师,让擅长数据处理,逻辑处理的人做后端工程师,干活的人更加专业、高效。在人力资源充足的情况下,做一个完整的项目,时间上大大缩短,质量还更加可靠。

JavaBean充当了模型的概念,有些人误认为模型就是JavaBean,JavaBean是什么?JavaBean只包含了构造方法、私有成员变量、公共的getter和setter方法。如何能够负担程序的数据逻辑呢?如果硬要说承担,也只能是数据的载体。准确的说法就是Model,因为它不止有属性,还包含了数据的访问程序。

Servlet作为控制器的角色,承接了和用户的交互,获取数据,并向模型发送数据。这倒是没什么毛病,虽然有很多web框架,但是几乎所有的web框架都实际上是对Servlet进行封装,提供了很多框架性的工具,完成框架的职责。

JSP作为视图层处理处理数据显示,但是实际上在视图这个层面来讲,处理展示JSP到现在几乎没什么用武之地了。因为用户看到的永远都是HTML.而渲染HTML出来的技术有很多,JSP只是其中一种,现在用得相对较多的是模板渲染技术,比如velocity、free marker等等。JSP也好,模板技术也好,使用它们做web开发时的作用绝大多数只有一个——在服务端渲染出相应的视图(最多的时候是HTML)返回给客户端,客户端拿到HTML之后再进行渲染,从而呈现出多姿多彩的界面。

c08d852ae7fce9fb598ce8b8677c0858.png

4fed3aeafb8890cb17b7471a35e9aa47.png

9bbd82e23e84066e3daf8282d93f2d21.png

但实际上web开发发展到今天,由于用户的机器越来越好,带宽也越来越大,浏览器的职责也越来越多。在过去,浏览器不能做(毕竟浏览器搞太多的事情容易卡死,你家也不是1024,卡死用户就不来了)的计算,可以更多的放在浏览器去做了——服务端返回数据和HTML,浏览器根据HTML和数据进行渲染得最后的页面展示,前后端的开发也彻底分家,这样做有些好处:

1.HTML无需后端程序的过多响应,返回就好,页面内容有兜底,在极端情况下(比如后端程序错误),不会看到丑陋的500或者404,用户体验相对好。

2.JavaScript是一个动态语言,运行在浏览器(搞懂代码到底在哪里执行是需要大家长期关注的问题),使用的是用户的机器,从某种意义上讲,降低了服务器资源的开销(渲染页面最终是IO操作产生的HTML),用别人家的电费就是爽。

3.在处理文件IO这件事情上,web服务器比应用服务器强悍多了,规避应用服务器的IO操作(莫信那些JAVA IO很强的谣言,至少目前还没哪个JAVA开发的应用服务器,在处理IO上面超过C语言的)的短板。

当然,也有不足,这样做的开发代价更高(一份工给两份钱),需要用户的基本要求也更高(要求更好的机器,更好的带宽,只是现在大家的机器都比较好,网络比以前好很多,是个能上网的机器就行)。

a73c20b9c3e068ff16586b14fa7bf2b2.png

730ce3395f370d28736e2722e9660bac.png

bd8c032f7a051ffc884de758da8af141.png

f14bcaf6fffe920c051bc70286eac65f.png

e5e80c35dd1e3e111bdadbfb3370607a.png

68496c99953d53dd1dde7e02bff95bec.png

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

听起来有点绕口,简单点讲,你可以在程序运行的时候,获取一个类的属性、方法、Constructor、annotation,并且可以调用一个类的方法。java.lang.reflect包下定义了几个类分别代表对类的抽象。

java.lang.Class 抽象类的信息,通过它可以获取类的属性,方法,构造器

java.lang.Method抽象方法的信息,通过它可以直接调用某个对象的方法。

java.lang.Field 抽象属性的信息。

java.lang.Constructor抽象类的构造方法的信息。

java.lang.Annotation抽象Annotation的信息,Annotation是给程序提供元数据的东西,可以把程序的配置放在里面。

具体的API本文就不具体阐述了,自行度娘或者等猿人工厂君的后续专栏。

621537a599a1b8f1816b8566762eb1df.png

下面我就讲讲一个自己定义简单的web框架,功能很简陋基本意思要有——解决url的映射、参数简单封装、数据转发,便于大家以后的学习。

框架都有自己的配置,通常都是基于xml的,当然,现在基于元数据(annotion)的越来越流行,各有各的优点吧,这次我们采用annotion。

先定义两个annotion:

packagecom.pz.web.frame.annotation;
 
 
 
import java.lang.annotation.ElementType;
 
import java.lang.annotation.Retention;
 
import java.lang.annotation.RetentionPolicy;
 
import java.lang.annotation.Target;
 
 
 
@Retention(RetentionPolicy.RUNTIME)
 
@Target({ ElementType.METHOD })
 
public @interface RequestURI {
 
 
 
    String url() default "";
 
 
 
}

package com.pz.web.frame.annotation;
 
 
 
import java.lang.annotation.ElementType;
 
import java.lang.annotation.Retention;
 
import java.lang.annotation.RetentionPolicy;
 
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
 
@Target({ ElementType.TYPE })
 
public @interface WebController {
 
 
 
}

有WebController这个标记的就代表我们处理业务的servlet类了。

有RequestURI这个标记的就代表我们处理业务的具体方法了,使用它去定义请求和方法映射。

考虑到反射的性能问题,简单的把一个类的方法抽象出来,放在内存里,这样提升一些性能。

packagecom.pz.web.frame.config;
 
 
 
import java.lang.reflect.Method;
 
 
 
public class MethodInfo {
 
    /**
     * method对象
     */
 
    private Method method;
 
 
 
    private Class[] getParameterTypes;
 
 
 
    public Method getMethod() {
 
        return method;
 
    }
 
 
 
    public void setMethod(Method method) {
 
        this.method = method;
 
    }
 
 
 
    public Class[] getGetParameterTypes() {
 
        return getParameterTypes;
 
    }
 
 
 
    public void setGetParameterTypes(Class[] getParameterTypes) {
 
        this.getParameterTypes = getParameterTypes;
 
    }
 
}

接下来的事情就是抽象和加载我们的配置了。

package com.pz.web.frame.config;
 
 
 
import com.pz.web.frame.annotation.RequestURI;
 
import com.pz.web.frame.annotation.WebController;
 
import com.pz.web.frame.util.ClassUtils;
 
 
 
import java.io.IOException;
 
import java.io.InputStream;
 
import java.lang.reflect.Method;
 
import java.util.HashMap;
 
import java.util.List;
 
import java.util.Map;
 
import java.util.Properties;
 
 
 
public class WebConfiguration {//暂时就不定义拦截器这类的东西了
 
    private static Map<String,MethodInfo> requestMapping = new HashMap<String,MethodInfo>();
 
    private static Map<Class,Object> methodClassMapping = new HashMap<Class,Object>();
 
    public static void init() throws InstantiationException, IllegalAccessException{
 
        InputStream stream = ClassUtils.class.getClassLoader().getResourceAsStream("web-frame.properties");
 
        Properties properties = new Properties();
 
        try {
 
            properties.load(stream);
 
        } catch (IOException e) {
 
            e.printStackTrace();
 
        }
 
 
 
        String packageName=properties.getProperty("package").toString();
 
 
 
        List<String> controllerList= ClassUtils.getClassName(packageName, true);
 
 
 
        for(String controller:controllerList){
 
            System.out.println(controller);
 
            try {
 
                Class clazz=Class.forName(controller);
 
                methodClassMapping.put(clazz, clazz.newInstance());
 
                if(clazz.isAnnotationPresent(WebController.class)){
 
                    System.out.println("找到了controller...");
 
                    Method[] methods = clazz.getMethods();
 
                    if(null!=methods){
 
                        for(Method method:methods){
 
 
 
                            if(method.isAnnotationPresent(RequestURI.class)){
 
                                System.out.println("找到了method..."+method.getName());
 
 
 
                                RequestURI uriData=method.getAnnotation(RequestURI.class);
 
                                String url=uriData.url();
 
 
 
                                MethodInfo methodInfo = new MethodInfo();
 
                                methodInfo.setGetParameterTypes(method.getExceptionTypes());
 
                                methodInfo.setMethod(method);
 
                                requestMapping.put(url, methodInfo);
 
                            }
 
 
 
                        }
 
                    }
 
                }
 
            } catch (ClassNotFoundException e) {
 
                // TODO Auto-generated catch block
 
                e.printStackTrace();
 
            }
 
        }
 
 
 
        for(String url:requestMapping.keySet()){
 
            System.out.println(url+":"+requestMapping.get(url));
 
 
 
            System.out.println("Object:"+getTargetObject(url));
 
 
 
        }
 
 
 
    }
 
 
 
    /**
     * 获取请求对应的方法
     * @param uri
     */
 
    public static MethodInfo getMethod(String uri){
 
 
 
        return requestMapping.get(uri);
 
    }
 
 
 
    /**
     * 获取请求对应的方法的对象
     * @param uri
     */
 
    public static Object getTargetObject(String uri){
 
 
 
        return methodClassMapping.get(requestMapping.get(uri).getMethod().getDeclaringClass());
 
    }
 
 
 
    public static void main(String args[]) throws InstantiationException, IllegalAccessException{
 
        init();
 
    }
 
 
 
}

web服务器给我们留的请求处理接口实际上就是servlet,我们做下简单封装,这次仅仅实现了请求的统一转发,调用对应的方法,如果需要对流程进行统一处理,简单点搞,可以定义一个叫做handler接口,提供初始化,调用前,调用后的方法,供外部实现,然后封装个集合,方到配置中,然后改造下面的执行流程,在调用业务方法前后,循环调用即可,倒不一定用反射了。我是怎么想到的?猜的吧,反正web框架啊的套路基本就那些的。翻翻MVC框架源码,struts,struts2,springMVC,大家大体这个意思。

packagecom.pz.web.frame.servlet;
 
 
 
import com.pz.web.frame.config.MethodInfo;
 
import com.pz.web.frame.config.WebConfiguration;
 
 
 
import javax.servlet.ServletException;
 
import javax.servlet.http.HttpServlet;
 
import javax.servlet.http.HttpServletRequest;
 
import javax.servlet.http.HttpServletResponse;
 
import java.io.IOException;
 
 
 
public class DispacherServlet extends HttpServlet {
 
 
 
 
 
    @Override
 
    public void init() throws ServletException {
 
        try {
 
 
 
            WebConfiguration.init();
 
        } catch (InstantiationException | IllegalAccessException e) {
 
            // TODO Auto-generated catch block
 
            e.printStackTrace();
 
        }
 
    }
 
 
 
    @Override
 
    protected void service(HttpServletRequest request, HttpServletResponse response)
 
            throws ServletException, IOException {
 
 
 
 
 
        String uri=request.getRequestURI().toString();
 
 
 
        MethodInfo targetMethodInfo= WebConfiguration.getMethod(uri);
 
        Object targetObject=WebConfiguration.getTargetObject(uri);
 
        //todo 404
 
        if(null==targetMethodInfo||null==targetObject){
 
            response.sendError(404);
 
        }
 
 
 
        try
 
        {
 
            Class[] classes =targetMethodInfo.getGetParameterTypes();
            Object[] args=handelParameter(targetMethodInfo,request,response);
            //这里可以加入统一的前置流程处理噢
            Object viewObject= targetMethodInfo.getMethod().invoke(targetObject,args);            if(null==viewObject){
 
                return ;
 
            }
 
            request.getRequestDispatcher((String) viewObject).forward( request , response );
 
 
 
        } catch (Exception e) {
 
            // TODO Auto-generated catch block
 
            e.printStackTrace();
 
        }
 
    }
 
 
 
    private Object[] handelParameter(MethodInfo methodInfo,HttpServletRequest request, HttpServletResponse response) {
 
 
 
        Class[] params=methodInfo.getGetParameterTypes();
 
 
 
        Object[] objArray=new Object[params.length];
 
 
 
        for(int i=0;i<params.length;i++){
 
            Object obj=null;
 
            try {
 
                obj = params[i].newInstance();
 
            } catch (InstantiationException e) {
 
 
 
            } catch (IllegalAccessException e) {
 
 
 
            }
 
            if(obj instanceof HttpServletRequest || obj instanceof HttpServletResponse){
 
                objArray[i]=params[i];
 
            }else{
 
               //todo 暂时就不搞对象转换了
 
            }
 
 
 
        }
 
 
 
        return objArray;
 
    }
 
 
 
 
 
 
 
}

工具类

packagecom.pz.web.frame.util;
 
 
 
import com.pz.web.frame.annotation.RequestURI;
 
import com.pz.web.frame.annotation.WebController;
 
 
 
import java.io.File;
 
import java.io.IOException;
 
import java.io.InputStream;
 
import java.lang.reflect.Method;
 
import java.net.URL;
 
import java.util.*;
 
 
 
public class ClassUtils {
 
 
 
    /**
     * 获取某包下所有类
     *
     * @param packageName
     *            包名
     * @param childPackage
     *            是否遍历子包
     * @return 类的完整名称
     */
 
    public static List<String> getClassName(String packageName, boolean childPackage) {
 
        List<String> fileNames = null;
 
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
 
        String packagePath = packageName.replace(".", "/");
 
        URL url = loader.getResource(packagePath);
 
        if (url != null) {
 
            String type = url.getProtocol();
 
            if (type.equals("file")) {
 
                fileNames = getClassNameByFile(url.getPath(), null, childPackage);
 
            }
 
        }
 
        return fileNames;
 
    }
 
 
 
 
 
    /**
     * 从项目文件获取某包下所有类
     *
     * @param filePath
     *            文件路径
     * @param className
     *            类名集合
     * @param childPackage
     *            是否遍历子包
     * @return 类的完整名称
     */
 
    private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
 
        List<String> myClassName = new ArrayList<>();
 
        File file = new File(filePath);
 
        File[] childFiles = file.listFiles();
 
        for (File childFile : childFiles) {
 
            if (childFile.isDirectory()) {
 
                if (childPackage) {
 
                    myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
 
                }
 
            } else {
 
                String childFilePath = childFile.getPath();
 
                if (childFilePath.endsWith(".class")) {
 
                    childFilePath = childFilePath.substring(childFilePath.indexOf("/classes") + 9,
 
                            childFilePath.lastIndexOf("."));
 
                    childFilePath = childFilePath.replace("/", ".");
 
                    myClassName.add(childFilePath);
 
                }
 
            }
 
        }
 
 
 
        return myClassName;
 
    }
 
 
 
 
 
    public static void main(String args[]){
 
 
 
        Map<String,Method> requestMapping = new HashMap<String,Method>();
 
 
 
        InputStream stream = ClassUtils.class.getClassLoader().getResourceAsStream("web-frame.properties");
 
        Properties properties = new Properties();
 
        try {
 
            properties.load(stream);
 
        } catch (IOException e) {
 
            e.printStackTrace();
 
        }
 
 
 
        String packageName=properties.getProperty("package").toString();
 
 
 
        List<String> controllerList= ClassUtils.getClassName(packageName, true);
 
 
 
        for(String controller:controllerList){
 
            System.out.println(controller);
 
            try {
 
                Class clazz=Class.forName(controller);
 
                if(clazz.isAnnotationPresent(WebController.class)){
 
                    System.out.println("找到了controller...");
 
                    Method[] methods = clazz.getMethods();
 
                    if(null!=methods){
 
                        for(Method method:methods){
 
                            if(method.isAnnotationPresent(RequestURI.class)){
 
                                System.out.println("找到了method..."+method.getName());
 
                                RequestURI uriData=method.getAnnotation(RequestURI.class);
 
                                String url=uriData.url();
 
                                requestMapping.put(url, method);
 
                            }
 
 
 
                        }
 
                    }
 
                }
 
            } catch (ClassNotFoundException e) {
 
                // TODO Auto-generated catch block
 
                e.printStackTrace();
 
            }
 
        }
 
 
 
        for(String url:requestMapping.keySet()){
 
            System.out.println(url+":"+requestMapping.get(url));
 
 
 
        }
 
 
 
    }
 
 
 
}

Web.xml配置

<!DOCTYPE web-app PUBLIC

 "-//SunMicrosystems, Inc.//DTD Web Application 2.3//EN"

 "http://java.sun.com/dtd/web-app_2_3.dtd">

 

<web-app>

  <servlet>

     <servlet-name>DispacherServlet</servlet-name>

     <servlet-class>com.pz.web.frame.servlet.DispacherServlet</servlet-class>

     <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

     <servlet-name>DispacherServlet</servlet-name>

     <url-pattern>*.pz</url-pattern>

  </servlet-mapping>

</web-app>

测试一下

packagecom.pz.web.biz;
 
 
 
import com.pz.web.frame.annotation.RequestURI;
 
import com.pz.web.frame.annotation.WebController;
 
@WebController
 
public class MyTestController {
 
    @RequestURI(url="/mytest.pz")
 
    public String mytest(){
 
        return "index.jsp";
 
 
 
    }
 
}

下一次咱们可以开始实战了噢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值