由于Spring官方就是选择gradle作为自动化构建工具,所以我们在本次尝试中就按照spring的选择也是用gradle
在整个项目中,我们一共包含两个模块framework模块用于首先实现我们springmvc的常见功能,test模块则是用来测试我们手写的模块是否正确
项目链接:https://github.com/ZhangJia97/Mini-Spring
下面是项目结构,只保留了我们用到的文件结构1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42├── build.gradle
├── framework
│ ├── build.gradle
│ └── src
│ ├── main
│ ├── java
│ └── xyz
│ └── suiwo
│ └── imooc
│ ├── beans
│ │ ├── Autowired.java
│ │ ├── Bean.java
│ │ └── BeanFactory.java
│ ├── core
│ │ └── ClassScanner.java
│ ├── starter
│ │ └── MiniApplication.java
│ └── web
│ ├── handler
│ │ ├── HandlerManager.java
│ │ └── MappingHandler.java
│ ├── mvc
│ │ ├── Controller.java
│ │ ├── RequestMapping.java
│ │ └── RequestParam.java
│ ├── server
│ │ └── TomcatServer.java
│ └── servlet
│ └── DispatcherServlet.java
└── test
├── build.gradle
└── src
├── main
├── java
└── xyz
└── suiwo
└── imooc
├── Application.java
├── controller
│ └── SalaryController.java
└── service
└── SalaryService.java
首先我们需要在framework的依赖中添加tomcat的依赖,因为springboot就是通过加入tomcat依赖来实现的1
2
3
4
5dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
// https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.23'
}
接下来让我们看看如何去创建一个tomcat服务1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39public class TomcatServer {
private Tomcat tomcat;
private String[] args;
public TomcatServer(String[] args) {
this.args = args;
}
public void startServer() throws LifecycleException {
tomcat = new Tomcat();
tomcat.setPort(8080);
Context context = new StandardContext();
context.setPath("");
context.addLifecycleListener(new Tomcat.FixContextListener());
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// servlet注册到tomcat容器内并开启异步支持
Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet).setAsyncSupported(true);
context.addServletMappingDecoded("/", "dispatcherServlet");
// 注册到默认host容器
tomcat.getHost().addChild(context);
tomcat.start();
Thread awaitThread = new Thread("tomcat_await_thread"){
public void run() {
TomcatServer.this.tomcat.getServer().await();
}
};
//设置成非守护线程
awaitThread.setDaemon(false);
awaitThread.start();
}
}
然后我们可以看到上述代码向tomcat中set了一个dispatchServlet用于处理请求,我们看看DispatchServlet如何去处理请求
1 | public class DispatcherServlet implements Servlet { |
我们现在已经成功创建了一个Tomcat的服务类,下面我们就可以在主类中启动tomcat服务了
然后我们看一些framework的主类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class MiniApplication {
public static void run(Class<?> cls, String[] args){
System.out.println("Hello Mini-Spring!");
// 创建一个Tomcat服务
TomcatServer tomcatServer = new TomcatServer(args);
try {
// 启动tomcat
tomcatServer.startServer();
// 扫描项目中当前cls目录下的所有包
List<Class<?>> classList = ClassScanner.scannerClass(cls.getPackage().getName());
// 初始化所有bean
BeanFactory.init(classList);
// 初始化所有的MappingHandler
HandlerManager.resolveMappingHandler(classList);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我们再创建三个mvc相关的注解1
2
3
4
5
(RetentionPolicy.RUNTIME)
(ElementType.TYPE)
public Controller {
}
1 |
|
1 |
|
然后我们看一下ClassScanner类,这个类主要用于扫描包1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50public class ClassScanner {
public static List<Class<?>> scannerClass(String packageName) throws IOException, ClassNotFoundException {
List<Class<?>> classList= new ArrayList<>();
String path = packageName.replaceAll("\\.", "/");
// 获取默认类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 获取资源文件的路径
Enumeration<URL> resources = classLoader.getResources(path);
while(resources.hasMoreElements()){
URL resource = resources.nextElement();
// 判断资源类型
if(resource.getProtocol().contains("jar")){
// 如果资源类型是jar包,则我们先获取jar包的绝对路径
JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
String jarFilePath = jarURLConnection.getJarFile().getName();
// 获取这个jar包下所有的类
classList.addAll(getClassesFromJar(jarFilePath, path));
}else {
// todo 处理非jar包的情况
}
}
return classList;
}
private static List<Class<?>> getClassesFromJar(String jarFilePath, String path) throws IOException, ClassNotFoundException {
//初始化一个容器用于存储类
List<Class<?>> classes = new ArrayList<>();
// 通过路径获取JarFile实例
JarFile jarFile = new JarFile(jarFilePath);
// 遍历jar包,每个jarEntry都是jar包里的一个文件
Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
while(jarEntryEnumeration.hasMoreElements()){
JarEntry jarEntry = jarEntryEnumeration.nextElement();
String entryName = jarEntry.getName(); // xyz/suiwo/imooc/test/Test.class
if(entryName.startsWith(path) && entryName.endsWith(".class")){
// 把分隔符换成点,并去除.class后缀
String classFullName = entryName.replace("/", ".").substring(0, entryName.length() - 6);
classes.add(Class.forName(classFullName));
}
}
return classes;
}
}
作为spring的经典ioc思想,初始化创建bean是重中之重,下面让我们看看如何实现吧
对于常见与Bean相关的注解就是@Bean
还有@Autowired
所以我们首先创建两个注解1
2
3
4
5
6
(RetentionPolicy.RUNTIME)
(ElementType.FIELD)
public Autowired {
String value() default "";
}
1 |
|
下面我们看看如何去初始化bean吧1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52public class BeanFactory {
private static Map<Class<?>, Object> classToBean = new ConcurrentHashMap<>();
public static Object getBean(Class<?> cls){
return classToBean.get(cls);
}
public static void init(List<Class<?>> classList) throws Exception {
List<Class<?>> toCreate = new ArrayList<>(classList);
while (toCreate.size() > 0){
int remainSize = toCreate.size();
for(int i = 0; i < toCreate.size(); i++){
// 返回true则说明创建成功或者说当前类不是一个bean
// 返回false则此时可能存存在当前需要创建的bean的依赖还没有创建所以暂时先跳过
if(finishCreate(toCreate.get(i))){
toCreate.remove(i);
}
}
// 如果数量没有改变则说明出现了死循环,抛出异常
if(toCreate.size() == remainSize){
throw new Exception("死循环");
}
}
}
private static boolean finishCreate(Class<?> cls) throws IllegalAccessException, InstantiationException {
// 如果没有满足的注解,则直接返回true
if(!cls.isAnnotationPresent(Bean.class) && !cls.isAnnotationPresent(Controller.class)){
return true;
}
Object bean = cls.newInstance();
for(Field field : cls.getDeclaredFields()){
if(field.isAnnotationPresent(Autowired.class)){
Class<?> fieldType = field.getType();
Object reliantBean = BeanFactory.getBean(fieldType);
// 如果为空,则说明当前类中的字段所依赖的类还没有注入,所以返回false,先跳过,等到所需要依赖注入之后再创建
if(reliantBean == null){
return false;
}
field.setAccessible(true);
field.set(bean, reliantBean);
}
}
// 将创建好的bean放入容器中
classToBean.put(cls, bean);
return true;
}
}
然后我们来看一下控制器,每一个MappingHandler都是一个请求映射器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class MappingHandler {
// 需要处理的uri
private String uri;
// 所对应的方法
private Method method;
// 所对应的方法
private Class<?> controller;
// 所需要的参数
private String[] args;
public MappingHandler(String uri, Method method, Class<?> controller, String[] args) {
this.uri = uri;
this.method = method;
this.controller = controller;
this.args = args;
}
// 若与MappingHandler匹配成功,执行方法
public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
String requestUri = ((HttpServletRequest)req).getRequestURI();
if(!uri.equals(requestUri)){
return false;
}
Object[] parameters = new Object[args.length];
for(int i = 0; i < args.length; i++){
parameters[i] = req.getParameter(args[i]);
}
Object ctl = BeanFactory.getBean(controller);
Object response = method.invoke(ctl, parameters);
res.getWriter().println(response.toString());
return true;
}
}
我们在创建一个管理器去管理这些MappingHandler1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public class HandlerManager {
public static List<MappingHandler> mappingHandlerList = new ArrayList<>();
// 把Controller类挑选出来,并将类中的带有@RequestMapping方法初始化成MappingHandler
public static void resolveMappingHandler(List<Class<?>> classList){
for(Class<?> cls : classList){
if(cls.isAnnotationPresent(Controller.class)){
parseHandlerFromController(cls);
}
}
}
// 解析controller类
private static void parseHandlerFromController(Class<?> cls) {
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
List<String> paramNameList = new ArrayList<>();
for(Parameter parameter : method.getParameters()){
if(parameter.isAnnotationPresent(RequestParam.class)){
paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
}
}
String[] params = paramNameList.toArray(new String[paramNameList.size()]);
MappingHandler mappingHandler = new MappingHandler(uri, method, cls, params);
HandlerManager.mappingHandlerList.add(mappingHandler);
}
}
}
至此,我们就已经成功的将整个框架大致完成了,对于test模块中的代码,我就不在这里在书写了,因为和我们日常写springboot业务相同只是为了测试我们手写框架的几个功能。