前言
本篇文章主要是帮助初级程序员去了解spring的基本原理。
一、手写简化版spring的基本思路
-
基本配置
配置web.xml。(DispatcherServlet) 设定init-param。(配置contentConfigLocation="application.properties") 设定url-pattern。(/*) 配置Annotation。(@Controller @Service @Autowired @RequestMapping等)
-
初始化
调用 init 方法。(加载配置文件) IoC容器初始化。(使用Map<String, Object>) 扫描相关类。(scanPackage="com.itman.demo") 创建对象并实例化保存至容器中。(通过反射机制将类实例化并保存至容器中)(IoC) 依赖注入。(扫描IoC容器中的实例,给没有赋值的属性赋值)(DI) 初始化HandlerMapping。(将Url和Method进行一对一关联映射保存到Map<String, Method>中)
-
运行
调用doPost()/doGet()。(Web容器调用doPost/doGet方法,获取request/response对象) 匹配HandlerMapping。(从request对象中获取请求的路径,找到其对应的Method) 反射调用method.invoker()。(利用反射调用并返回结果) 结果返回给浏览器。
二、代码结构
1.pom.xml
2.基础业务代码结构
-
DemoController.java
-
IDemoService.java
-
DemoServiceImpl.java
3.框架原理代码实现
- 代码结构
2. web.xml
-
配置文件application.properties
-
自定义的JmDispatcherServlet.java(核心代码)
package com.itman.mvc.framework.servlet;
import com.itman.mvc.framework.annotation.JmAutowired;
import com.itman.mvc.framework.annotation.JmController;
import com.itman.mvc.framework.annotation.JmRequestMapping;
import com.itman.mvc.framework.annotation.JmService;
import javax.servlet.ServletConfig;
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.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
public class JmDispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 600112243475039630L;
private Map<String, Object> ioc = new HashMap<>();
// 配置文件对象
private Properties properties = new Properties();
// 所有类的全类名集合
private List<String> classNames = new ArrayList<>();
// 处理器映射
private 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 {
// 6.调用
try {
doDispatch(req, resp);
} catch (Exception e) {
resp.getWriter().write("500 Exception, Message: " + Arrays.toString(e.getStackTrace()));
}
}
/**
* 方法调用
* @param req 请求对象
* @param resp 返回对象
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 获取请求路径
String url = req.getRequestURI();
// 获取项目路径
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 Error, Request Method Is Not Exist, Request Url: " + url);
}
// 获取方法
Method method = this.handlerMapping.get(url);
// 获取请求参数
Map<String, String[]> parameterMap = req.getParameterMap();
// 获取方法所在的类名
String beanName = formatJavaBeanName(method.getDeclaringClass().getSimpleName());
Object[] objs = new Object[parameterMap.size() + 2];
objs[0] = req;
objs[1] = resp;
int i = 2;
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
objs[i] = entry.getValue()[0];
i++;
}
method.invoke(ioc.get(beanName), objs);
}
@Override
public void init(ServletConfig config) throws ServletException {
// 1.加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 2.扫描相关类
doScanner(properties.getProperty("scanPackage"));
// 3.实例化扫描的类,并且缓存到IoC容器中
doInstance();
// 4.完成依赖注入(DI)
doAutowired();
// 5.初始化HandlerMapping
doInitHandlerMapping();
System.out.println("Jm Spring framework is init!");
}
/**
* 加载配置文件
* @param contextConfigLocation web.xml中配置的配置文件属性
*/
private void doLoadConfig(String contextConfigLocation) {
if (null == contextConfigLocation || "".equals(contextConfigLocation) || "".equals(contextConfigLocation.replaceAll(" ", ""))
|| contextConfigLocation.length() <= 0){
throw new RuntimeException("Not Set Config Location!");
}
// 获取配置文件的文件流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
if (null == is){
throw new RuntimeException("Config File Is Not Found!");
}
// 读取配置
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 扫描相关类
* @param scanPackage 扫描的包路径
*/
private void doScanner(String scanPackage) {
// 获取包的文件夹路径
String classUrl = scanPackage.replaceAll("\\.", "/");
// 获取包URL
URL url = this.getClass().getClassLoader().getResource(classUrl);
if (null == url){
throw new RuntimeException("Get Class Url Error!");
}
// 获取包文件数据
File classFile = new File(url.getFile());
// 获取包路径下的所有文件数据
File[] files = classFile.listFiles();
if (null == files){
return;
}
for (File file : files){
if (file.isDirectory()){ // 如果是文件夹,继续获取文件夹下类的全类名
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) { // 只扫描java类
continue;
}
// 获取包文件下类的全类名
String className = scanPackage + "." + file.getName().replace(".class", "");
// 保存类的全类名
classNames.add(className);
}
}
}
/**
* 类实例化(IoC)
*/
private void doInstance() {
if (classNames.isEmpty()){
return;
}
try {
for (String className : classNames){
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(JmController.class)){ // 实例化controller
// 获取类名
String beanName = formatJavaBeanName(clazz.getSimpleName());
// 实例化类
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(JmService.class)){ // 实例化service
// 获取类名
String beanName = formatJavaBeanName(clazz.getSimpleName());
// 获取自定义的类名
JmService jmService = clazz.getAnnotation(JmService.class);
if (!"".equals(jmService.value())){
beanName = jmService.value();
}
// 实例化类
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
// 根据接口实例化
for (Class<?> c : clazz.getInterfaces()){
if (ioc.containsKey(c.getName())){
throw new RuntimeException(c.getName() + "Is Exist!");
}
ioc.put(c.getName(), instance);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 依赖注入(DI)
*/
private void doAutowired() {
if (ioc.isEmpty()){
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()){
// 获取类中所有字段
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields){
if (!field.isAnnotationPresent(JmAutowired.class)){
continue;
}
JmAutowired jmAutowired = field.getAnnotation(JmAutowired.class);
String beanName = jmAutowired.value().trim();
if ("".equals(beanName)){
beanName = field.getType().getName();
}
// 设置访问权限,暴力访问
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 初始化映射
*/
private void doInitHandlerMapping() {
if (ioc.isEmpty()){
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(JmController.class)) {
continue;
}
String baseUrl = "";
if (clazz.isAnnotationPresent(JmRequestMapping.class)) {
JmRequestMapping jmRequestMapping = clazz.getAnnotation(JmRequestMapping.class);
baseUrl = jmRequestMapping.value();
}
// 针对所有的 public 方法
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(JmRequestMapping.class)) {
continue;
}
JmRequestMapping jmRequestMapping = method.getAnnotation(JmRequestMapping.class);
String url = ("/" + baseUrl + "/" + jmRequestMapping.value()).replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Handler Mapping url: " + url + ", method: " + method);
}
}
}
/**
* 格式化 java 类名, 第一个字母小写
* @param simpleName 类名
* @return 格式化后的类名
*/
private String formatJavaBeanName(String simpleName) {
char[] charArray = simpleName.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
}
}
- 相关注解
6. 运行结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210627155521274.png)
3.面试(spring容器中的bean是不是线程安全的)
通过上面的简单版spring框架,可以了解到bean是怎么得到的。
- Spring中的Bean是从哪里得到的。(IoC容器中得到的)
- IoC容器中的Bean是从哪里得到的。(通过反射机制得到的)
- 反射机制的Bean是从哪里得到的。(通过读取配置文件得到的)
- 配置文件中的Bean是从哪里得到的。(是程序员开发的)
因此,总结得到Spring容器中的Bean与Spring容器无关,Spring容器只是负责管理,所以spring容器中的bean不一定是线程安全的