手写Spring--初始篇
前言
未读spring源码之前手写spring的初始版本
准备
1、项目依赖与配置下载地址
https://download.csdn.net/download/shen_yun2017/12686004
2、LZYDispatchServlet类继承HttpServlet并重写init()、doPost、doGet方法。
项目结构,注解的定义参考https://blog.csdn.net/shen_yun2017/article/details/107646256
//配置文件
private Properties contextConfig = new Properties();
//存储路劲
private List<String> classPath = new ArrayList<String>();
//IoC容器
private Map<String,Object> springIoC = new HashMap<String, Object>();
//路径容器
Map<String,Method> handlerMapping = new HashMap<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatch(req,resp);
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
String properties=servletConfig.getInitParameter("contextConfigLocation");
//1、加载配置文件
doConfig(properties);
//2、获取classpath
String scanPackage=contextConfig.getProperty("scanPackage");
doClassPath(scanPackage);
//3、创建IoC容器容器,与实例化
createIoC();
//4、注入
doAutowired();
//5、初始化HandlerMapping
initHandlerMapping();
}
//首字母小写
private String toLowerFirstChar(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0]+=32;
System.out.println(chars.toString());
return String.valueOf(chars);
}
一、加载spring配置文件
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
二、获取扫描路劲得到classpath
private void doClassPath(String scanPackage) {
String resource = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.","/")).getPath();
// .replaceAll("%20"," ")
File file = new File(resource);
for (File file1: file.listFiles()) {
String filepath = scanPackage + "."+file1.getName();
if(file1.isDirectory())
doClassPath(filepath);
if (file1.getName().endsWith(".class")){
// resource.replaceAll(".class","");
classPath.add(filepath.replaceAll(".class",""));
}
}
}
三、IoC容器创建于通过反射创建实体
private void createIoC() {
for (String className:classPath)
try {
Class<?> aClass = Class.forName(className);
Object instance = null;
String springIoCKey = null;
//什么样的类需要注入,而什么类需要实例化
if (aClass.isAnnotationPresent(LZYController.class)) {
instance = aClass.newInstance();
springIoCKey=toLowerFirstChar(aClass.getSimpleName());
}else if (aClass.isAnnotationPresent(LZYService.class)){
LZYService annotation = aClass.getAnnotation(LZYService.class);
springIoCKey = annotation.value();
if("".equals(springIoCKey.trim())){
springIoCKey = toLowerFirstChar(aClass.getSimpleName());
}
instance = aClass.newInstance();
for (Class<?> interfaceClass:aClass.getInterfaces()){
if (springIoC.containsKey(interfaceClass.getSimpleName())){
throw new RuntimeException("this is "+interfaceClass.getName()+"is exit !!");
}
springIoC.put(interfaceClass.getSimpleName(),instance);
}
}else continue;
if (instance != null)
springIoC.put(springIoCKey,instance);
// aClass.getAnnotations();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
四、DI完成依赖注入
private void doAutowired() {
for (Map.Entry bean: springIoC.entrySet()) {
Object value = bean.getValue();
Field[] declaredFields = value.getClass().getDeclaredFields();
for (Field field: declaredFields) {
if (!field.isAnnotationPresent(LZYAutoWired.class)){
continue;
}
String autowiredValueName = field.getDeclaredAnnotation(LZYAutoWired.class).value().trim();
if ("".equals(autowiredValueName)){
autowiredValueName = field.getType().getName();
}
field.setAccessible(true);
try {
field.set(bean.getValue(),springIoC.get(autowiredValueName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
五、构建HandMapping(URL与方法之间映射对应关系)
/**
* @Date: 2020/8/3 20:48
* @description :
* @author :lzy
* @params: []
* @return: void
* @other: 初始化handlmapping,让url处理方法对应
*/
private void initHandlerMapping() {
for (Map.Entry bean: springIoC.entrySet()) {
Class<?> aClass = bean.getValue().getClass();
Method[] methods = aClass.getMethods();
StringBuffer url = new StringBuffer("/");
if (aClass.isAnnotationPresent(LZYRequestMapping.class)){
url.append(aClass.getAnnotation(LZYRequestMapping.class).value())
.append("/");
}
for (Method method:methods) {
if (method.isAnnotationPresent(LZYRequestMapping.class)){
url.append(method.getAnnotation(LZYRequestMapping.class).value());
handlerMapping.put(url.toString().replaceAll("/+","/"),method);
}
}
}
}
六、请求的分发
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
try {
if (!handlerMapping.containsKey(url)){
resp.getWriter().print("404 not found");
return;
}
Method method = handlerMapping.get(url);
Map<String,String[]> params = req.getParameterMap();
Class<?>[] parameterTypes = method.getParameterTypes();
Object [] parameterValues=new Object[parameterTypes.length];
for (int i=0;i<parameterTypes.length;i++) {
Class paramterType= parameterTypes[i];
if (paramterType == HttpServletRequest.class){
parameterValues[i]=req;
}if (paramterType == HttpServletResponse.class){
parameterValues[i]=resp;
}if (paramterType == String.class){
// Annotation annotationPresent = paramterType.getAnnotation(LZYRequestParam.class);
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation annotation: parameterAnnotations[i]){
if (annotation instanceof LZYRequestParam){
String value = ((LZYRequestParam) annotation).value();
if ("".equals(value)) continue;
parameterValues[i]= Arrays.toString(params.get(value))
.replaceAll("\\[|\\]","")
.replaceAll("\\s+",",");
}
}
}
}
String beanName = toLowerFirstChar(method.getDeclaringClass().getSimpleName());
method.invoke(springIoC.get(beanName),parameterValues);
} catch (Exception e){
e.printStackTrace();
}
}
七、演示
Controller类
import com.lzy.spring.demo.service.Service;
import com.lzy.spring.framework.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ Author :lzy
* @ Date :Created in 2020/7/28 21:32
* @ Description:
*/
@LZYController
@LZYRequestMapping("/")
public class Controller {
@LZYAutoWired("service")
private Service service;
@LZYRequestMapping("hello")
public void hello(HttpServletRequest req, HttpServletResponse resp,@LZYRequestParam("name") String name){
try {
resp.getWriter().print(service.qurey(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
service类
package com.lzy.spring.demo.service.impl;
import com.lzy.spring.demo.service.Service;
import com.lzy.spring.framework.annotation.LZYService;
/**
* @ Author :lzy
* @ Date :Created in 2020/8/4 15:40
* @ Description:
*/
@LZYService("service")
public class ServiceImpl implements Service {
public String qurey(String name){
return "hello "+name;
}
}
访问
http://localhost:8080/hello?name=tom
问题记录
端口被占用
Exception in thread "Shutdown" java.lang.NoClassDefFoundError: org/apache/jasper/runtime/JspApplicationContextImpl
at org.apache.jasper.servlet.JspServlet.destroy(JspServlet.java:411)
at org.mortbay.jetty.servlet.ServletHolder.destroyInstance(ServletHolder.java:318)
at org.mortbay.jetty.servlet.ServletHolder.doStop(ServletHolder.java:289)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.servlet.ServletHandler.doStop(ServletHandler.java:185)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerWrapper.doStop(HandlerWrapper.java:142)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerWrapper.doStop(HandlerWrapper.java:142)
at org.mortbay.jetty.servlet.SessionHandler.doStop(SessionHandler.java:125)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerWrapper.doStop(HandlerWrapper.java:142)
at org.mortbay.jetty.handler.ContextHandler.doStop(ContextHandler.java:592)
at org.mortbay.jetty.webapp.WebAppContext.doStop(WebAppContext.java:537)
at org.mortbay.jetty.plugin.Jetty6PluginWebAppContext.doStop(Jetty6PluginWebAppContext.java:123)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerCollection.doStop(HandlerCollection.java:169)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerCollection.doStop(HandlerCollection.java:169)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.handler.HandlerWrapper.doStop(HandlerWrapper.java:142)
at org.mortbay.jetty.Server.doStop(Server.java:283)
at org.mortbay.component.AbstractLifeCycle.stop(AbstractLifeCycle.java:76)
at org.mortbay.jetty.Server$ShutdownHookThread.run(Server.java:561)
Caused by: java.lang.ClassNotFoundException: org.apache.jasper.runtime.JspApplicationContextImpl
at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
... 24 more