SpringMVC浅析 - 20181226

  • 【面试题】在浏览器输入登录地址后,浏览器做了什么?
    六步:①域名解析、②tcp握手、③建立连接、④发送报文、⑤服务器应答、⑥解析html
    域名解析:将英文的网址名转换成ip地址
    tcp连接建立:建立通信信道,经过三次握手
    建立连接
    发送报文:就是消息头,消息体那些东西
    服务器应答:(可以扩充nginx,服务器集群等等)
    解析html:
    (渲染):

  • 再来看看SpringMVC工作流程
    DispatcherServlet:在web.xml理配置的servlet,前端控制器,起到一个路由的功能。
    HandlerMapping:之后找到映射控制器,这样就可以找到对应的controller了
    HandlerAdatper:通过处理器适配器,可以找到controller中的方法
    拼装成视图再返回

手写一个迷你版的SpringMVC

  1. 依赖
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12</version>
       </dependency>
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>4.0.0</version>
       </dependency>
  1. 大致流程思路
    ①、自定义注解,模拟:@Controller/@Service/@RequestMapping/@Autowired
    ②、包扫描,类加载实例化,实现@Controller、@Service功能
    ③、依赖注入,实现@Autowired功能
    ④、映射处理,实现@RequestMapping功能

  2. 自定义注解,模拟:@Controller/@Service/@RequestMapping/@Autowired

/**
 * 自注解的Controller
 * Created by Turing on 2018/12/26 11:38
 */
@Documented //注解包含在javadoc中
@Retention(RetentionPolicy.RUNTIME) //注解保留策略,注解在哪个阶段生效【source源码保存、class字节码文件保存、runtime运行时仍获取注解信息(反射需要用到)】
@Target({ElementType.TYPE}) //注解作用的范围
public @interface Controller {

    String value() default "";  //通过反射,取出@Controller("xxx"),里的xxx
}

/**
 * Created by Turing on 2018/12/26 12:37
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE}) //注解作用的范围——类上
public @interface Service {

    String value() default "";
}

/**
 * Created by Turing on 2018/12/26 12:46
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD}) //注解作用的范围——类上,方法上
public @interface RequestMapping {

    String value() default "";
}

/**
 * Created by Turing on 2018/12/26 12:51
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD}) //注解作用的范围——成员变量上
public @interface Autowired {

    String value() default "";
}
  1. Controller 和 Service
/**
 * Created by Turing on 2018/12/26 13:43
 */
@Controller
@RequestMapping(value = "/test")
public class TestController {

    @Autowired("testService")
    TestService testService;

    @RequestMapping("/index")
    public String index(){
        System.out.println(testService.test()+": index");
        return testService.test()+"index";
    }
}

/**
 * Created by Turing on 2018/12/26 13:44
 */
@Service
public class TestService {

    public String test(){
        return "service test";
    }
}
  1. 在web.xml中配置,自定义的DispatcherServlet
<!--前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>com.mvc.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup> <!--随着容器启动,进行初始化-->
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  1. DispatcherServlet
import com.mvc.annotation.Autowired;
import com.mvc.annotation.Controller;
import com.mvc.annotation.RequestMapping;
import com.mvc.annotation.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 1、想完成Controller和Service的包扫描
 * 2、类加载并实例化
 * 3、依赖注入
 * 4、url和方法关系映射
 * Created by Turing on 2018/12/26 13:48
 */
public class DispatcherServlet extends HttpServlet {
    //扫描到的类名,存入list集合中
    private List<String> classNames = new ArrayList<String>();

    //Controller和Service的实例化,暂存缓存
    private Map<String,Object> beans=new HashMap<String,Object>();

    //找到方法的映射缓存,暂存
    private Map<String,Object> handlerMapping=new HashMap<String,Object>();


    @Override
    //重写init方法,才能完成目标四步
    public void init() throws ServletException {
        System.out.println("======容器启动======");

        //1、包扫描【正常应该是xml将解析,现在是模拟】
        scanPackage("com.mvc");

        for (String str : classNames) {//打印,测试下
            System.err.println(str);
        }

        //2、类加载并实例化
        try {
            createInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        for(Map.Entry<String,Object> entry:beans.entrySet()){//打印,测试下
            System.out.println(entry.getKey()+"<=======>"+entry.getValue());
        }

        //3、依赖注入
        try {
            injection();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        //4、URL和方法的关系映射,handlerMapping作用
        handlerMapping();
    }

    //1、
    private void scanPackage(String packageName) {
        //类加载的时候,将包结构.替换成/,\\.是正则表达式中的关键字符,代表任意字符
        String path = packageName.replaceAll("\\.","/");

        //classLoader 进行资源加载,
        URL url = this.getClass().getClassLoader().getResource("/"+path);

        //递归目录,查找class文件
        File[] files = new File(url.getFile()).listFiles();

        for (File file : files) {
            //判断是否为目录
            if(file.isDirectory()){
                //遍历目录
                scanPackage(packageName+"."+file.getName());
            }else {
                //扫描到类,先缓存起来,包+类名 拼成 全限定名
                classNames.add(packageName+"."+file.getName());
            }
        }
    }

    //2、
    private void createInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //判断,没有扫描到类
        if(classNames.isEmpty()){
            System.out.println("没有扫描到类。。。");
            return;
        }
        //遍历,类名缓存,加载并初始化
        for(String name:classNames){
            //类加载时,将类名去调后缀.class
            String className=name.replace(".class","");
            //完成类加载
            Class<?> clazz = Class.forName(className);

            //只关注controller和service,判断类上是否有对应注解
            if(clazz.isAnnotationPresent(Controller.class)){
                //实例化类
                Object controller = clazz.newInstance();
                RequestMapping rm = clazz.getAnnotation(RequestMapping.class);
                String value = rm.value();  //运行时,取出括号中的值

                //保存了RequestMapping括号里的东西,到对应Controller的映射
                beans.put(value,controller);
            }

            //拿到servcie
            if(clazz.isAnnotationPresent(Service.class)){
                Object service = clazz.newInstance();
                RequestMapping rm = clazz.getAnnotation(RequestMapping.class);
                String value = rm.value();

                beans.put(value,service);
            }
        }
    }
    //3、
    private void injection() throws IllegalAccessException {
        //判断,没有实例化
        if(beans.isEmpty()){
            System.out.println("没有实例化。。。");
            return;
        }

        //遍历,找到controller,并找到所有字段
        for(Map.Entry<String,Object> entry:beans.entrySet()){
            //取出对象
            Object obj = entry.getValue();
            //取出字段
            Field[] fields=obj.getClass().getDeclaredFields();
            //遍历,找到@Autowired
            for (Field field : fields) {
                if(field.isAnnotationPresent(Autowired.class)){
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    //取出注解上的值
                    String value = autowired.value();

                    //完成注入
                    field.setAccessible(true);//打开私有,令其可以访问
                    //通过value拿到@Autowired括号里的实例
                    field.set(obj,beans.get(value));
                }
            }
        }
    }
    //4、
    private void handlerMapping() {
        //判断,没有实例化
        if(beans.isEmpty()){
            System.out.println("没有实例化。。。");
            return;
        }
        //遍历,找到RequestMapping,并完成映射
        for(Map.Entry<String,Object> entry:beans.entrySet()){
            //取出对象
            Object obj = entry.getValue();

            if(obj.getClass().isAnnotationPresent(RequestMapping.class)){
                //先拿类上的值
                RequestMapping rm = obj.getClass().getAnnotation(RequestMapping.class);
                String classValue = rm.value();

                //再拿方法上的值
                Method[] methods=obj.getClass().getMethods();

                //遍历,找到方法上的@RequestMapping
                for (Method method : methods) {

                    if(method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping mapping = method.getAnnotation(RequestMapping.class);
                        String methodValue = mapping.value();

                        //进行组合,并缓存起来,这样拿到去到方法的映射
                        handlerMapping.put(classValue+methodValue,method);
                    }
                }
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        String context = request.getContextPath();
        String path = uri.replaceAll(context, "");

        System.err.println(uri+","+context+","+path);

        //根据"类/方法"路径,获取对应的方法
        Method method = (Method) handlerMapping.get(path);
        //获取对象
        Object instance = beans.get("/" + path.split("/")[1]);  //分割拿到后边的方法

        try {
            //反射调用
            method.invoke(instance,null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doGet(request, response);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值