-
【面试题】在浏览器输入登录地址后,浏览器做了什么?
六步:①域名解析、②tcp握手、③建立连接、④发送报文、⑤服务器应答、⑥解析html
域名解析:将英文的网址名转换成ip地址
tcp连接建立:建立通信信道,经过三次握手
建立连接
发送报文:就是消息头,消息体那些东西
服务器应答:(可以扩充nginx,服务器集群等等)
解析html:
(渲染): -
再来看看SpringMVC工作流程
DispatcherServlet
:在web.xml理配置的servlet,前端控制器
,起到一个路由的功能。
HandlerMapping
:之后找到映射控制器,这样就可以找到对应的controller了
HandlerAdatper
:通过处理器适配器,可以找到controller中的方法
拼装成视图再返回
手写一个迷你版的SpringMVC
- 依赖
<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>
-
大致流程思路
①、自定义注解,模拟:@Controller/@Service/@RequestMapping/@Autowired
②、包扫描,类加载实例化,实现@Controller、@Service功能
③、依赖注入,实现@Autowired功能
④、映射处理,实现@RequestMapping功能 -
自定义注解,模拟:@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 "";
}
- 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";
}
}
- 在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>
- 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);
}
}