├── README.md └── mini-spring ├── mini-spring2.iml ├── test ├── target │ └── test-1.0-SNAPSHOT.jar ├── src │ └── main │ │ └── java │ │ └── com │ │ └── shenghao │ │ ├── service │ │ └── SalaryService.java │ │ ├── Application.java │ │ └── controller │ │ └── SalaryController.java └── pom.xml ├── framework ├── src │ └── main │ │ └── java │ │ └── com │ │ └── shenghao │ │ ├── beans │ │ ├── Bean.java │ │ ├── AutoWired.java │ │ └── BeanFactory.java │ │ ├── web │ │ ├── mvc │ │ │ ├── Controller.java │ │ │ ├── RequestMapping.java │ │ │ └── RequestParam.java │ │ ├── handler │ │ │ ├── MappingHandler.java │ │ │ └── HandlerManager.java │ │ ├── servlet │ │ │ └── DispatcherServlet.java │ │ └── server │ │ │ └── TomcatServer.java │ │ ├── starter │ │ └── MiniApplication.java │ │ └── core │ │ └── ClassScanner.java └── pom.xml └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # mini-spring 2 | 3 | 本项目实现了简单的IOC,MVC功能 4 | 5 | -------------------------------------------------------------------------------- /mini-spring/mini-spring2.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mini-spring/test/target/test-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyj24/mini-spring/HEAD/mini-spring/test/target/test-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/beans/Bean.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.beans; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target(ElementType.TYPE) 8 | public @interface Bean { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/mvc/Controller.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.mvc; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target(ElementType.TYPE) 8 | public @interface Controller { 9 | } 10 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/beans/AutoWired.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.beans; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target(ElementType.FIELD) 8 | public @interface AutoWired { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /mini-spring/test/src/main/java/com/shenghao/service/SalaryService.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.service; 2 | 3 | import com.shenghao.beans.Bean; 4 | 5 | @Bean 6 | public class SalaryService { 7 | public Integer calSalary(Integer experience){ 8 | return experience * 5000; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/mvc/RequestMapping.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.mvc; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target(ElementType.METHOD) 8 | public @interface RequestMapping { 9 | String value(); 10 | } 11 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/mvc/RequestParam.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.mvc; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target(ElementType.PARAMETER) 8 | public @interface RequestParam { 9 | String value(); 10 | } 11 | -------------------------------------------------------------------------------- /mini-spring/test/src/main/java/com/shenghao/Application.java: -------------------------------------------------------------------------------- 1 | package com.shenghao; 2 | 3 | import com.shenghao.starter.MiniApplication; 4 | 5 | public class Application { 6 | 7 | public static void main(String[] args) { 8 | System.out.println("Hello world!"); 9 | MiniApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mini-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | pom 7 | com.shenghao 8 | mini-spring2 9 | 1.0-SNAPSHOT 10 | 11 | framework 12 | test 13 | 14 | 15 | -------------------------------------------------------------------------------- /mini-spring/test/src/main/java/com/shenghao/controller/SalaryController.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.controller; 2 | 3 | import com.shenghao.beans.AutoWired; 4 | import com.shenghao.service.SalaryService; 5 | import com.shenghao.web.mvc.Controller; 6 | import com.shenghao.web.mvc.RequestMapping; 7 | import com.shenghao.web.mvc.RequestParam; 8 | 9 | @Controller 10 | public class SalaryController { 11 | 12 | @AutoWired 13 | private SalaryService salaryService; 14 | 15 | @RequestMapping("/getSalary") 16 | public Integer getSalary(@RequestParam("name")String name, 17 | @RequestParam("experience")String experience){ 18 | return salaryService.calSalary(Integer.parseInt(experience)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/starter/MiniApplication.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.starter; 2 | 3 | import com.shenghao.beans.BeanFactory; 4 | import com.shenghao.core.ClassScanner; 5 | import com.shenghao.web.handler.HandlerManager; 6 | import com.shenghao.web.server.TomcatServer; 7 | 8 | import java.util.List; 9 | 10 | public class MiniApplication { 11 | 12 | public static void run(Class cls, String[] args){ 13 | System.out.println("Hello mini-spring!"); 14 | TomcatServer tomcatServer = new TomcatServer(args); 15 | try { 16 | //启动tomcat 17 | tomcatServer.startServer(); 18 | //扫描启动类下所有的.class文件 19 | System.out.println(cls.getPackage().getName()); 20 | List> classList = ClassScanner.scanClass(cls.getPackage().getName()); 21 | //初始化bean工厂 22 | BeanFactory.initBean(classList); 23 | //解析所有.class文件,获得mappingHandler集合 24 | HandlerManager.resolveMappingHandler(classList); 25 | } catch (Exception e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mini-spring/framework/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | jar 6 | 7 | mini-spring2 8 | com.shenghao 9 | 1.0-SNAPSHOT 10 | 11 | 4.0.0 12 | 13 | framework 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-compiler-plugin 19 | 20 | 8 21 | 8 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.apache.tomcat.embed 30 | tomcat-embed-core 31 | 8.5.55 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/handler/MappingHandler.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.handler; 2 | 3 | import com.shenghao.beans.BeanFactory; 4 | import org.apache.catalina.servlet4preview.http.HttpServletRequest; 5 | import sun.rmi.transport.ObjectTable; 6 | 7 | import javax.servlet.ServletRequest; 8 | import javax.servlet.ServletResponse; 9 | import java.io.IOException; 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | 13 | public class MappingHandler { 14 | 15 | private String uri; 16 | private Method method; 17 | private Class controller; 18 | private String[] args; 19 | 20 | MappingHandler(String uri, Method method, Class cls, String[] args){ 21 | this.uri = uri; 22 | this.method = method; 23 | this.controller = cls; 24 | this.args = args; 25 | } 26 | 27 | public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException { 28 | //拿到请求的uri 29 | String requestUri = ((HttpServletRequest)req).getRequestURI(); 30 | if(!uri.equals(requestUri)){//如果和自身uri不同就跳过 31 | return false; 32 | } 33 | Object[] parameters = new Object[args.length]; 34 | for(int i = 0; i < args.length; i++){ 35 | parameters[i] = req.getParameter(args[i]); 36 | } 37 | Object ctl = BeanFactory.getBean(controller); 38 | Object response = method.invoke(ctl, parameters); 39 | res.getWriter().println(response.toString()); 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/servlet/DispatcherServlet.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.servlet; 2 | 3 | import com.shenghao.web.handler.HandlerManager; 4 | import com.shenghao.web.handler.MappingHandler; 5 | 6 | import javax.servlet.*; 7 | import java.io.IOException; 8 | import java.lang.reflect.InvocationTargetException; 9 | 10 | public class DispatcherServlet implements Servlet { 11 | 12 | @Override 13 | public void init(ServletConfig servletConfig) throws ServletException { 14 | 15 | } 16 | 17 | @Override 18 | public ServletConfig getServletConfig() { 19 | return null; 20 | } 21 | 22 | @Override 23 | public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { 24 | // servletResponse.getWriter().println("test"); 25 | System.out.println("测试有多少个mappingHandler:" + HandlerManager.mappingHandlerList.size()); 26 | for(MappingHandler mappingHandler : HandlerManager.mappingHandlerList){ 27 | try{ 28 | if (mappingHandler.handle(servletRequest, servletResponse)){ 29 | return; 30 | } 31 | } catch (IllegalAccessException e) { 32 | e.printStackTrace(); 33 | } catch (InstantiationException e) { 34 | e.printStackTrace(); 35 | } catch (InvocationTargetException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | } 40 | 41 | @Override 42 | public String getServletInfo() { 43 | return null; 44 | } 45 | 46 | @Override 47 | public void destroy() { 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/server/TomcatServer.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.server; 2 | 3 | import com.shenghao.web.servlet.DispatcherServlet; 4 | import org.apache.catalina.Context; 5 | import org.apache.catalina.LifecycleException; 6 | import org.apache.catalina.core.StandardContext; 7 | import org.apache.catalina.startup.Tomcat; 8 | 9 | /** 10 | * 内嵌Tomcat服务器 11 | */ 12 | public class TomcatServer { 13 | private Tomcat tomcat;//自带一个tomcat实例 14 | private String[] args; 15 | 16 | public TomcatServer(String[] args){ 17 | this.args = args; 18 | } 19 | 20 | public void startServer() throws LifecycleException { 21 | tomcat = new Tomcat(); 22 | tomcat.setPort(6699);//设置tomcat在操作系统上监听的端口号 23 | tomcat.start();//启动tomcat 24 | 25 | Context context = new StandardContext();//tomcat中的容器是分层级的,存放servlet的是context容器,先初始化一个 26 | context.setPath("");//设置context的路径为空 27 | context.addLifecycleListener(new Tomcat.FixContextListener());//注册一个监听器 28 | 29 | DispatcherServlet servlet = new DispatcherServlet();//创建DispatcherServlet对象,所有请求都先经过此处 30 | Tomcat.addServlet(context, "dispatcherServlet", servlet).setAsyncSupported(true);//把dispatcherServlet注册进Tomcat中,并设置为异步 31 | context.addServletMappingDecoded("/", "dispatcherServlet");//并为DispatcherServlet设置请求路径,设置为根目录意味着所有请求都会到这里 32 | 33 | tomcat.getHost().addChild(context);//把context注册到host中,host是tomcat中更高一级的容器 34 | 35 | Thread awaitThread = new Thread("tomcat_await_thread"){ 36 | @Override 37 | public void run() { 38 | TomcatServer.this.tomcat.getServer().await();//设置tomcat线程一直等待,不然的话启动完就会关闭 39 | } 40 | }; 41 | awaitThread.setDaemon(false);//设置为非守护线程 42 | awaitThread.start();//启动 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mini-spring/test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | jar 6 | 7 | mini-spring2 8 | com.shenghao 9 | 1.0-SNAPSHOT 10 | 11 | 4.0.0 12 | test 13 | 14 | 15 | 16 | com.shenghao 17 | framework 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-shade-plugin 26 | 1.2.1 27 | 28 | 29 | package 30 | 31 | shade 32 | 33 | 34 | 35 | 36 | com.shenghao.Application 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/web/handler/HandlerManager.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.web.handler; 2 | 3 | import com.shenghao.web.mvc.Controller; 4 | import com.shenghao.web.mvc.RequestMapping; 5 | import com.shenghao.web.mvc.RequestParam; 6 | 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Parameter; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class HandlerManager { 13 | 14 | public static List mappingHandlerList = new ArrayList<>(); 15 | 16 | /** 17 | * 处理类文件集合,挑出MappingHandler 18 | * @param classList 19 | */ 20 | public static void resolveMappingHandler(List> classList){ 21 | System.out.println("测试有多少个.class文件:" + classList.size()); 22 | for(Class cls : classList){ 23 | if(cls.isAnnotationPresent(Controller.class)){//MappingHandler会在controller里面 24 | parseHandlerFromController(cls);//继续从controller中分离出一个个MappingHandler 25 | } 26 | } 27 | } 28 | 29 | private static void parseHandlerFromController(Class cls) { 30 | //先获取该controller中所有的方法 31 | Method[] methods = cls.getDeclaredMethods(); 32 | //从中挑选出被RequestMapping注解的方法进行封装 33 | for(Method method : methods){ 34 | if(!method.isAnnotationPresent(RequestMapping.class)){ 35 | continue; 36 | } 37 | String uri = method.getDeclaredAnnotation(RequestMapping.class).value();//拿到RequestMapping定义的uri 38 | List paramNameList = new ArrayList<>();//保存方法参数的集合 39 | for(Parameter parameter : method.getParameters()){ 40 | if(parameter.isAnnotationPresent(RequestParam.class)){//把有被RequestParam注解的参数添加入集合 41 | paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value()); 42 | } 43 | } 44 | String[] params = paramNameList.toArray(new String[paramNameList.size()]);//把参数集合转为数组,用于反射 45 | MappingHandler mappingHandler = new MappingHandler(uri, method, cls, params);//反射生成MappingHandler 46 | mappingHandlerList.add(mappingHandler);//把mappingHandler装入集合中 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/beans/BeanFactory.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.beans; 2 | 3 | import com.shenghao.web.mvc.Controller; 4 | 5 | import java.lang.reflect.Field; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 初始化bean 13 | * 获得bean 14 | */ 15 | public class BeanFactory { 16 | 17 | //保存Bean实例的映射集合 18 | private static Map, Object> classToBean = new ConcurrentHashMap<>(); 19 | 20 | /** 21 | * 根据class类型获取bean 22 | * @param cls 23 | * @return 24 | */ 25 | public static Object getBean(Class cls){ 26 | return classToBean.get(cls); 27 | } 28 | 29 | /** 30 | * 初始化bean工厂 31 | * @param classList 需要一个.class文件集合 32 | * @throws Exception 33 | */ 34 | public static void initBean(List> classList) throws Exception { 35 | //先创建一个.class文件集合的副本 36 | List> toCreate = new ArrayList<>(classList); 37 | //循环创建bean实例 38 | while(toCreate.size() != 0){ 39 | int remainSize = toCreate.size();//记录开始时集合大小,如果一轮结束后大小没有变证明有相互依赖 40 | for(int i = 0; i < toCreate.size(); i++){//遍历创建bean,如果失败就先跳过,等下一轮再创建 41 | if(finishCreate(toCreate.get(i))){ 42 | toCreate.remove(i); 43 | } 44 | } 45 | if(toCreate.size() == remainSize){//有相互依赖的情况先抛出异常 46 | throw new Exception("cycle dependency!"); 47 | } 48 | } 49 | } 50 | 51 | private static boolean finishCreate(Class cls) throws IllegalAccessException, InstantiationException { 52 | //创建的bean实例仅包括Bean和Controller注释的类 53 | if(!cls.isAnnotationPresent(Bean.class) && !cls.isAnnotationPresent(Controller.class)){ 54 | return true; 55 | } 56 | //先创建实例对象 57 | Object bean = cls.newInstance(); 58 | //看看实例对象是否需要执行依赖注入,注入其他bean 59 | for(Field field : cls.getDeclaredFields()){ 60 | if(field.isAnnotationPresent(AutoWired.class)){ 61 | Class fieldType = field.getType(); 62 | Object reliantBean = BeanFactory.getBean(fieldType); 63 | if(reliantBean == null){//如果要注入的bean还未被创建就先跳过 64 | return false; 65 | } 66 | field.setAccessible(true); 67 | field.set(bean, reliantBean); 68 | } 69 | } 70 | classToBean.put(cls, bean); 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mini-spring/framework/src/main/java/com/shenghao/core/ClassScanner.java: -------------------------------------------------------------------------------- 1 | package com.shenghao.core; 2 | 3 | import java.io.IOException; 4 | import java.net.JarURLConnection; 5 | import java.net.URL; 6 | import java.util.ArrayList; 7 | import java.util.Enumeration; 8 | import java.util.List; 9 | import java.util.jar.JarEntry; 10 | import java.util.jar.JarFile; 11 | 12 | public class ClassScanner { 13 | public static List> scanClass(String packageName) throws IOException, ClassNotFoundException { 14 | //用于保存结果的容器 15 | List> classList = new ArrayList<>(); 16 | //把文件名改为文件路径 17 | String path = packageName.replace(".", "/"); 18 | //获取默认的类加载器 19 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 20 | //通过文件路径获取该文件夹下所有资源的URL 21 | Enumeration resources = classLoader.getResources(path); 22 | 23 | int index = 0;//测试 24 | 25 | while(resources.hasMoreElements()){ 26 | System.out.println(++index);//测试 27 | 28 | //拿到下一个资源 29 | URL resource = resources.nextElement(); 30 | //先判断是否是jar包,因为默认.class文件会被打包为jar包 31 | if(resource.getProtocol().contains("jar")){ 32 | 33 | System.out.println("拿到一个jar包");//测试 34 | 35 | //把URL强转为jar包链接 36 | JarURLConnection jarURLConnection = (JarURLConnection)resource.openConnection(); 37 | //根据jar包获取jar包的路径名 38 | String jarFilePath = jarURLConnection.getJarFile().getName(); 39 | //把jar包下所有的类添加的保存结果的容器中 40 | classList.addAll(getClassFromJar(jarFilePath, path)); 41 | }else{//也有可能不是jar文件,先放下 42 | //todo 43 | } 44 | } 45 | return classList; 46 | } 47 | 48 | /** 49 | * 获取jar包中所有路径符合的类文件 50 | * @param jarFilePath 51 | * @param path 52 | * @return 53 | */ 54 | private static List> getClassFromJar(String jarFilePath, String path) throws IOException, ClassNotFoundException { 55 | List> classes = new ArrayList<>();//保存结果的集合 56 | JarFile jarFile = new JarFile(jarFilePath);//创建对应jar包的句柄 57 | Enumeration jarEntries = jarFile.entries();//拿到jar包中所有的文件 58 | while(jarEntries.hasMoreElements()){ 59 | JarEntry jarEntry = jarEntries.nextElement();//拿到一个文件 60 | String entryName = jarEntry.getName();//拿到文件名,大概是这样:com/shenghao/test/Test.class 61 | if (entryName.startsWith(path) && entryName.endsWith(".class")){//判断是否是类文件 62 | String classFullName = entryName.replace("/", ".") 63 | .substring(0, entryName.length() - 6); 64 | classes.add(Class.forName(classFullName)); 65 | } 66 | } 67 | return classes; 68 | } 69 | } 70 | --------------------------------------------------------------------------------