2.一个简单的Servlet容器

章前准备
    如何处理ifelse众多的问题,当然也可以说是switch case
    我们经常写这样的代码,如果这货小一点,且可预测什么的,那都不是事,问题是如果他要是可拓展的类- -!让他见鬼去吧
    我曾经被要求实现一个接口,这个接口会接受一个Scene参数用以区分不同实现方式(不用吐槽,国企喜欢超级接口),我相信你也会见过类似的代码,显然如果Scene是可确定的话,if...else并没有什么不可以的...遗憾的是设计人员会明确的告诉你,Scene是会不断增加的,而且if中的内容绝对不会像你想象的那么简单。。。
    唯一令人兴奋的是java作为面向对象语言,最喜欢解决这种问题

    呆毛:Paint类拥有print方法,他会根据传来的参数来画不同的人物
简单实现一:尝试着完成呆毛并观察

public class Paint {
    public static void main(String[] args)  {
        Paint paint = new Paint();
        paint.print("lf","", "");
        paint.print("nm","", "");
    }
    /**
     * @param Scene 场景参数
     * @param name  路人甲,实现场景什么的会使用到的参数
     * @param id    路人乙
     */
    void print(String scene,String name,String id){
        if("lf".equals(scene)){
            //好长好长的代码
            System.out.println("路飞");
        }else if("sl".equals(scene)){
            //好长好长的代码
            System.out.println("索隆");
        }else if("sz".equals(scene)){
            //好长好长的代码
            System.out.println("山治");
        }else{
            //好长好长的代码
            System.out.println("熊猫人");
        }
    }
}

这里只是用System.out.println("");来替代绘画的过程,如果真的很长的话,即使作为BI端的屌丝程序猿也知道,这时候应该封装具体的实现


简单实现二:尝试着将ifelse中具体的实现进行封装(通过接口,不是通过方法。。。)

/**
 * @author 程猿
 * 尝试着封装所谓的具体实现,print中的逻辑将会在Print的实现
 */
public class Paint {
    public static void main(String[] args)  {
        Paint paint = new Paint();
        paint.print("lf","", "");
        paint.print("nm","", "");
    }

    void print(String scene,String name,String id){
        if("lf".equals(scene)){
            new PrintLF().print(scene, name, id);
        }else if("sl".equals(scene)){
            new PrintSL().print(scene, name, id);
        }else if("sz".equals(scene)){
            new PrintSZ().print(scene, name, id);
        }else{
            new PrintXMR().print(scene, name, id);
        }
    }
}
public interface Print {
    void print(String scene,String name,String id);
}
public class PrintLF  implements Print{
    @Override
    public void print(String scene, String name, String id) {
        System.out.println("路飞");
    }
}

其他实现就不贴了
我们已用封装的方式,将处理的问题全都放在Print的实现类中,代码此处会少一节。。。当然这只是对封装的简单应用,可以看到print的方法中,是简单的判断关系与实现类的1对1的关系,如果我这里使用类似于Map这种结构的话,情况或许就不一样了....其实作为BI端的屌丝程序猿明白,我们的实现,基本上就是这样了(严格来说应该是放到方法里,而不是放到接口实现里)

简单实现三:我们要使用Map结构,key为判断,val为实现类

/**
 * @author 程猿
 * key对应判断依据,value对应实现类
 * 如果value为null,会使用默认类,这里的默认类的key为""...
 */
public class Paint2 {
    public static void main(String[] args)  {
        Paint2 paint = new Paint2();
        paint.print("lf","", "");
        paint.print("nm","", "");
    }

    Map<String,Print>  paintMap=new HashMap(){
        {
            put("lf", new PrintLF());
            put("sl", new PrintSL());
            put("sz", new PrintSZ());
            put("", new PrintXMR());
        }
    };
    
    void print(String scene,String name,String id){
        Print object = paintMap.get(scene);
        if(object==null){
            object= paintMap.get("");
        }
        if(object==null){
            return;
        }
        object.print(scene, name, id);
    }
}

main是控制台,很显然,如果给map一个get/set方法的话,我们就可以在控制台来控制Paint类方法的走向,而不需要在进入Paint进行修改,当然,主流的控制台还是配置文件,我们都是懂ioc的男人,当然因为说使用了ioc所以就结束显然太没良心了,那就试试用加点伪ioc呗

简单实现四:加入伪ioc实现

/**
 * @author 程猿 并没有一个容器去实现Print类...所以才叫伪嘛
 */
public class Paint {
    public static void main(String[] args) {
        Paint paint = new Paint();
        paint.print("lf", "", "");
        paint.print("nm", "", "");
    }

    Paint() {
        InputStream in = this.getClass().getResourceAsStream(
                "/t3/print.properties");
        try {
            paintMap.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    Properties paintMap = new Properties();

    void print(String scene, String name, String id) {
        String string = paintMap.getProperty(scene);
        if (string == null) {
            string = paintMap.getProperty("");
        }
        if (string == null) {
            return;
        }
        Class clazz = null;
        try {
            clazz = Class.forName(string);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Print newInstance = null;
        try {
            newInstance = (Print) clazz.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        newInstance.print(scene, name, id);
    }
}

这是一种解决ifelse众多的解决方案,而且还是经理们经常念叨的可插拔可配置的,那他算不算一种神奇的设计模式呢?那我个人认为设计模式是解决继承,封装,多态使用时的一种不便利性而存在的,比如子类过多,或者方法暴露过多什么的,显然这里只是简单地封装和多态的使用(无视ioc...),当然或许这就是面向对象最有意思的地方,或者说面向接口

不在联想了,我们是在学tomcat,聊一下web服务器与网民(客户端的操作人员)的交互过程,首先,网民输入url,浏览器通过url(和自身设置)生成请求报文发送给服务器,服务器分析请求报文,并返回相应报文,通过这个过程可以发现,url应该称的上最精简的请求报文(或者说最重要的地方),也是网民唯一能修改的地方,换句话说不同的url对应不同的响应报文,那么,来一个

自制的可以相应多种URL的web服务器

/**
 * @author 程猿 通过请求报文可以观察出第一行,以空格分割,第二个就是url的路径
 */
public class Test {
    public static String getUrl(InputStream inputStream) throws IOException {
        int i;
        String str = "";
        while ((i = inputStream.read()) != '
') {
            str += (char) i;
        }
        String[] split = str.split(" ");
        if (split.length > 2) {
            return split[1];
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(8080);
        while (true) {
            Socket socket = server.accept();
            // 接受请求
            InputStream inputStream = socket.getInputStream();
            String url = getUrl(inputStream);
            System.out.println("url:" + url);
            if ("/a".equals(url)) {
                // 发送回应
                OutputStream outputStream = socket.getOutputStream();
                String str = "HTTP/1.1 200 OK
"
                        + "Server: Apache-Coyote/1.1
"
                        + "Content-Type: text/html
"
                        + "Transfer-Encoding: chunked
"
                        + "Date: Mon, 27 Oct 2014 13:38:35 GMT
"
                        + "
"
                        + "bb
"
                        + "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
"
                        + "<HTML>
"
                        + "  <HEAD><TITLE>A Servlet</TITLE></HEAD>
"
                        + "  <BODY>
" + "    this is A~
" + "  </BODY>
"
                        + "</HTML>";
                outputStream.write(str.getBytes());
                socket.close();
            } else if ("/b".equals(url)) {
                OutputStream outputStream = socket.getOutputStream();
                String str = "HTTP/1.1 200 OK
"
                        + "Server: Apache-Coyote/1.1
"
                        + "Content-Type: text/html
"
                        + "Transfer-Encoding: chunked
"
                        + "Date: Mon, 27 Oct 2014 13:38:35 GMT
"
                        + "
"
                        + "bb
"
                        + "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
"
                        + "<HTML>
"
                        + "  <HEAD><TITLE>A Servlet</TITLE></HEAD>
"
                        + "  <BODY>
" + "    BBBBBBBBBBBBBBB~
"
                        + "  </BODY>
" + "</HTML>";
                outputStream.write(str.getBytes());
                socket.close();
            }
        }

    }
}

分别输入http://localhost:8080/a和http://localhost:8080/b很显然,在浏览器中的显示的情况是不一样的,我们可以通过完成多个ifelse达到不同的url对应不同的界面的目的,而且我们也能够解决ifelse众多的问题

tomcat_2.ifelse.zip

第二章 一个简单的Servlet容器

1.java大叔提供了一种解决web服务器的调用与实现的方案,(其接口)称之为Servlet
2.因为存在2组人工作(tomcat小组和servlet实现小组),2组人可能根本就不认识(认识就好了...),为了解决继承的向下转型的暴露问题是用外观模式

原文地址:https://www.cnblogs.com/liuCy/p/4018371.html