首先我们聊聊小编目前对spring核心功能的理解。
Spring启动时最核心的功能我认为是扫描所有Bean,并将Bean注入到Bean容器中也就是我们所说的Ioc容器之一,实现依赖倒置,并且在进行类扫描时将标注有AutoWire注解的成员变量注入实例,然后再将Ioc容器中标注有Controller注解的类注入到另外一个容器中,以供外部访问某个接口时可以查找到接口的实例方法(实际处理方法)。注入到容器的过程中包含很多后置处理器对Bean进行处理。
- 我们首先应该知道Spring在启动时要获取它自己的配置文件,那么这个配置文件的路径应该在哪获取呢?路径就配置在web.xml里(笔者找了半天源码才发现的)。
- 以下是笔者自己配置的一个bean扫描路径
- 接下来我们看具体实现。在扫描之前 我们想一想,Spring实现了请求的路由 处理 返回等功能,那我们肯定是要接口前端请求的。所以我们的实现类里首先要集成HttpServlet。去实现它的 doGet 和 doPost方法。并且在init方法里去实现我们上面说的 依赖注入等逻辑。
- 我们在上面那个init方法里实现了刚刚描述的具体功能。
接下来我们主要一起看一下 ioc容器 依赖注入和HandlerMapping.
- 首先是ioc容器。
classNames是所有被扫描出来的类。
我们把所有符合要求的类放入到IOC容器中。
我们所谓的ioc容器其实就是一个Map。
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class <?> clazz = Class.forName(className);
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.toString());
}
if (clazz.isAnnotationPresent(KXYController.class)) {
//默认别名
String salis = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance();
ioc.put(salis, instance);
} else if (clazz.isAnnotationPresent(KXYService.class)) {
//默认别名
String beanName = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance();
//自定义命名
if (!"".equals(clazz.getAnnotation(KXYService.class).value())) {
beanName = clazz.getAnnotation(KXYService.class).value();
}
ioc.put(beanName, instance);
//以类型命名
for (Class <?> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("");
}
ioc.put(i.getName(), instance);
}
} else {
continue;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}**
- 接下来我们对IOC容器中的类进行处理,这里主要处理带有AutoWire注解的Field。将其注入具体实现的方法
private void doAutoWired() {
if (ioc.isEmpty()) {
return;
}
ioc.forEach((k, v) -> {
Field[] declaredFields = v.getClass().getDeclaredFields();
for (Field field : declaredFields) {
if (!field.isAnnotationPresent(KXYAutowire.class)) {
continue;
}
KXYAutowire annotation = field.getAnnotation(KXYAutowire.class);
String beanName = annotation.value().trim();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
try {
field.setAccessible(true);
field.set(v, ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
}
- 最后我们再入住HandlerMapping
private void initHandlerMaping() {
ioc.forEach((String k, Object v) -> {
Class <?> aClass = v.getClass();
if(aClass.isAnnotationPresent(KXYController.class)){
Method[] methods = aClass.getDeclaredMethods();
KXYRequestMapping mapping = aClass.getAnnotation(KXYRequestMapping.class);
String urll = "";
if(mapping != null){
urll = mapping.value() == null ? "":mapping.value();
}
for (Method method : methods) {
StringBuffer url = new StringBuffer();
if(urll.length() > 0){
url.append("/"+urll);
}
if(!method.isAnnotationPresent(KXYRequestMapping.class)){continue;}
url.append(method.getAnnotation(KXYRequestMapping.class).value());
method.setAccessible(true);
handleMapping.put(url.toString().replaceAll("//+","/"), method);
System.out.println("11111111" +url.toString()+","+method);
}
}
});
}
至此我们的初始化已完成。接下来我们看当一个访问请求来的时候,我们应该做什么?
- 当一个请求过来的时候时候首先它应该进Servlet的doGet或doPost方法。然后我们再调用doDispath()方法进行处理。具体实现方法如下。
private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
String servletPath = req.getRequestURI().replaceAll("//+","/");
String projectName = req.getServletContext().getServletContextName();
servletPath.replace(projectName+"//","");
Method method = (Method) handleMapping.get("/qq/as");
String classNameSalis = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(classNameSalis),req,resp);
}
这样我们就实现了一个简单的Spring的基础功能。
源码地址:https://github.com/magopudding/springsimp
如有错误欢迎同学们批评指正。