笔者从事开发一年,对spring仍然停留在使用阶段,近来打算深入了解spring ioc原理。下文自己实现的spring ioc是利用了servlet,但只是皮毛,真正的spring ioc有着对继承、设计模式的经典复杂的设计。但是在深入源码了解spring ioc原理之前,本文中有助于对ioc原理的理解
目录结构
1、首先引入依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
</dependencies>
2、实现自己的注解
package com.study.myspring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutoWried {
String value() default "";
}
package com.study.myspring.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
package com.study.myspring.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
package com.study.myspring.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
String value() default "";
}
3、重写servlet
package com.study.myspring.servlet;
import com.study.myspring.annotation.MyAutoWried;
import com.study.myspring.annotation.MyController;
import com.study.myspring.annotation.MyRequestMapping;
import com.study.myspring.annotation.MyService;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
//urlPatterns不能使用"/"
@WebServlet(name="myDispatchServlet",urlPatterns = {"/test/*"})
public class MyDispatchServlet extends HttpServlet {
//属性配置文件
private Properties contextConfig = new Properties();
private List<String> classNameList = new ArrayList<String>();
//ioc注册表
Map<String,Object> iocMap = new HashMap<String,Object>();
Map<String,Method> handlerMapping = new HashMap<String,Method>();
@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 {
//7、运行阶段
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception Detail:\n" + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath,"").replaceAll("/+","/");
System.out.println("[INFO-7] request url-->" + url);
if(!this.handlerMapping.containsKey(url)){
try {
resp.getWriter().write("404 NOT FOUND!");
return;
}catch (IOException e){
e.printStackTrace();
}
}
Method method = this.handlerMapping.get(url);
System.out.println("[INFO-7] method-->" + method);
String beanName = (method.getDeclaringClass().getSimpleName());
System.out.println("[INFO-7] iocMap.get(beanName)->" + iocMap.get(beanName));
// 第一个参数是获取方法,后面是参数,多个参数直接加,按顺序对应
method.invoke(iocMap.get(beanName), req, resp);
System.out.println("[INFO-7] method.invoke put {" + iocMap.get(beanName) + "}.");
}
@Override
public void init(){
//加载配置文件
System.out.println("实例化");
//扫描相关的类(偷懒了直接写死包名)
doScanner("com.study");
//3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
doInstane();
//4、依赖注入
doAutoWired();
//5、初始化 HandlerMapping
initHandlerMapping();
System.out.println("XSpring FrameWork is init.");
//6、打印数据
doTestPrintData();
}
private void doLoadConfig(String contextConfigLocation){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(inputStream);
System.out.println("[INFO-1] property file has been saved in contextConfig.");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doScanner(String scanPackage){
URL resourcePath = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
if(resourcePath == null){
return;
}
File classPath = new File(resourcePath.getFile());
for(File file : classPath.listFiles()){
if(file.isDirectory()){
System.out.println("[INFO-2] {" + file.getName() + "} is a directory.");
// 子目录递归
doScanner(scanPackage + "." + file.getName());
}else {
if (!file.getName().endsWith(".class")) {
System.out.println("[INFO-2] {" + file.getName() + "} is not a class file.");
continue;
}
String className = (scanPackage + "." + file.getName()).replace(".class", "");
// 保存在内容
classNameList.add(className);
System.out.println("[INFO-2] {" + className + "} has been saved in classNameList.");
}
}
}
private void doInstane(){
if(classNameList.isEmpty()){
return;
}
try{
for(String className : classNameList){
Class<?> aClass = Class.forName(className);
if(aClass.isAnnotationPresent(MyController.class)){
String beanName = (aClass.getSimpleName());
Object object = aClass.newInstance();
System.out.println(beanName+object.getClass().isAnnotationPresent(MyController.class));
iocMap.put(beanName,object);
System.out.println(beanName+iocMap.get(beanName).getClass().isAnnotationPresent(MyController.class));
System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");
}else if(aClass.isAnnotationPresent(MyService.class)){
MyService annotation = aClass.getAnnotation(MyService.class);
String beanName = (aClass.getSimpleName());
if(!annotation.value().equals("")){
beanName = annotation.value();
}
Object object = aClass.newInstance();
iocMap.put(beanName,object);
for(Class i : aClass.getInterfaces()){
if(iocMap.containsKey(i.getName())){
throw new Exception("The bean is exit");
}
iocMap.put(i.getName(),object);
System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap.");
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private void doAutoWired(){
if(iocMap.isEmpty()){
return;
}
for(Map.Entry<String,Object> entry : iocMap.entrySet()){
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field :fields){
//field.getAnnotation(MyAutoWried.class);
if(field.isAnnotationPresent(MyAutoWried.class)){
MyAutoWried annotation = field.getAnnotation(MyAutoWried.class);
String beanName = annotation.value().trim();
if(beanName.equals("")){
beanName = field.getType().getName();
}
field.setAccessible(true);
try {
field.set(entry.getValue(),iocMap.get(beanName));
System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + iocMap.get(beanName) + "}.");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
private void initHandlerMapping(){
if(iocMap.isEmpty()){
return;
}
for(Map.Entry<String,Object> entry : iocMap.entrySet()){
//MyService annotation1 = entry.getClass().getAnnotation(MyService.class);
System.out.println(entry.getKey()+entry.getClass().isAnnotationPresent(MyController.class));
if(!entry.getValue().getClass().isAnnotationPresent(MyController.class)){
continue;
}
String baseUrl = "";
if(entry.getValue().getClass().isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = entry.getValue().getClass().getAnnotation(MyRequestMapping.class);
baseUrl = annotation.value();
}
for(Method method : entry.getValue().getClass().getDeclaredMethods()){
if (method.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
baseUrl=baseUrl+annotation.value();
}
handlerMapping.put(baseUrl, method);
System.out.println("[INFO-5] handlerMapping put {" + baseUrl + "} - {" + method + "}.");
}
}
}
private void doTestPrintData() {
System.out.println("[INFO-6]----data------------------------");
System.out.println("contextConfig.propertyNames()-->" + contextConfig.propertyNames());
System.out.println("[classNameList]-->");
for (String str : classNameList) {
System.out.println(str);
}
System.out.println("[iocMap]-->");
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
System.out.println(entry);
}
System.out.println("[handlerMapping]-->");
for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
System.out.println(entry);
}
System.out.println("[INFO-6]----done-----------------------");
System.out.println("====启动成功====");
System.out.println("测试地址:http://localhost:8080/test/listClassName");
}
}
4、定义Service接口类
package com.study.service;
public interface ITestMyService {
String listClassName();
}
5、实现Service接口类
package com.study.service.impl;
import com.study.myspring.annotation.MyService;
import com.study.service.ITestMyService;
@MyService
public class TestMyServiceImpl implements ITestMyService {
public String listClassName() {
return "1234";
}
}
6、实现Controller类
package com.study.controller;
import com.study.myspring.annotation.MyAutoWried;
import com.study.myspring.annotation.MyController;
import com.study.myspring.annotation.MyRequestMapping;
import com.study.service.ITestMyService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@MyController
@MyRequestMapping("/test")
public class TestMyController {
@MyAutoWried
ITestMyService testMyService;
@MyRequestMapping("/listClassName")
public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
String str = testMyService.listClassName();
System.out.println("testXService----------=-=-=>" + str);
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、使用spring boot启动我们的servlet
package com.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@EnableAutoConfiguration
@SpringBootApplication
//@ServletComponentScan会扫描包下我们定义的servlet
@ServletComponentScan("com.study.myspring.servlet")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
8、运行结果