1.SringMVC的实现原理
springMVC的实现其实就是socket的上下文
其中一个Server中包含多个connector,一个connector包含多个container。
2.Client和Socket一般有两种链接方式,一种是每个socket占用一个独立的线程,另一种是建立一个Selector容器,用Handle来遍历有请求的socket
两者相比较后者更加快捷,而且阻塞时间更短,占据资源更少,实现了高并发访问。
3.参照源码,对SpringMVC进行仿写
1.创建maven项目,添加依赖
<build> <plugins> <plugin> <groupId>org.apache.maven.pluginsgroupId> <artifactId>maven-compiler-pluginartifactId>
<configuration> <source>8source> <target>8target> configuration> plugin> plugins> build> <dependencies> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webmvcartifactId> <version>5.2.1.RELEASEversion> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-contextartifactId> <version>5.2.1.RELEASEversion> dependency> <dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.18.10version> dependency> dependencies> project>
直接创建maven项目可以为以后添加依赖提供方便
2.为bean类添加注解
@Data //get 和setter方法
@NoArgsConstructor //无参构造方法
@AllArgsConstructor//有参构造方法
@ToString//toString方法
3.仿照RestController和RequestMapping来写出自己的注解--MyRestController和MyRequestMapping
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyRestController { String value() default ""; } import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyRequestMapping { String value() default ""; }
4.创建ServerTest2类
// IOC 容器 public static MapbeanMap = new HashMap<>(); // URL映射,RequestMapping public static Map methodMap = new HashMap<>(); public static void main(String[] args) throws IOException { //调用扫描包的方法 refreshBeanFactory("ydp.day01.Test"); //建立服务器端socket ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(80)); //是否为阻塞 serverSocketChannel.configureBlocking(false); //创建存放socket的容器 Selector selector = Selector.open(); //将服务器端socket注册到selector中 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { if (selector.select(3000) <= 0) { continue; } //对selector容器中每一个注册的socket进行迭代,迭代完之后并remove Iterator keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); handle(key); //迭代完之后移除 keyIterator.remove(); } }
private static void requestHandle(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
//byte字节Buffer流 -> 字节流
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
//clear:设置为写模式
byteBuffer.clear();
//从socket中读取内容到byteBuffer
if (socketChannel.read(byteBuffer) == -1) {
socketChannel.close();
return;
}
// 调用url所映射的方法
private static String methodInvoke(String url, ListurlParams) throws InvocationTargetException, IllegalAccessException {
MethodInfo methodInfo = methodMap.get(url);
// 如果url中参数个数与方法中参数个数不一致,则返回404
if (methodInfo == null) {
return "404";
}
String className = methodInfo.getClassName();
Method method = methodInfo.getMethod();
Object beanObj = beanMap.get(className);
Object[] params = new Object[urlParams.size()];
Parameter[] parameters = method.getParameters();
// 如果url中参数个数与方法中参数个数不一致,则返回404
if (params.length != parameters.length) {
return "参数个数不匹配";
}
// 按照方法中参数的属性,来进行参数转换和填充
int i = 0;
for (Parameter p : parameters) {
String type = p.getType().getSimpleName();
String pName = p.getName();
boolean flag = false;
for (String p2 : urlParams) {
String pp[] = p2.split("=");
if (pName.equals(pp[0])) {
// 根据类型进行参数转换
Object pValue = paramTranslate(type, pp[1]);
params[i++] = pValue;
flag = true;
continue;
}
}
if (!flag)
return "参数名称不匹配";
}
return (String) method.invoke(beanObj, params);
}
}
这样就能完成对基本类型的参数,包"ydp/day01/Test/UserController"下所有有MyRequestMapping的方法进行调用了。
这里只贴出了部分代码详细代码见GitHub链接:https://github.com/yao-cz/repository/tree/master