在咱们的平常工做中,常常会用到Spring、Spring Boot、Spring Cloud、Struts、Mybatis、Hibernate等开源框架,有了这些框架的诞生,平时的开发工做量也是变得愈来愈轻松,咱们用 Spring Boot 分分钟能够新建一个Web项目。
今天经过手写Spring框架,帮你们深刻了解一下Spring的工做机制,文中涉及的代码只用来帮助你们理解Spring,不会在线上使用,有不严谨的地方还请你们掠过。java
首先新建一个 HttpServlet 的实现类 MarsDispatcherServlet,用来接收请求。web
public class MarsDispatcherServlet extends HttpServlet { @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. 处理请求 } @Override public void init(ServletConfig config) throws ServletException { }
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Spring Mvc Education</display-name> <servlet> <servlet-name>marsmvc</servlet-name> <servlet-class>com.mars.framework.servlet.MarsDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>marsmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
配置application.properties面试
scanPackage=com.mars.demo
这个比较好理解,仅配置了一项内容,意思是要扫描的包,随后咱们会获取这个值去加载容器。spring
定义咱们经常使用的注解api
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MarsController { String value() default ""; }
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MarsRequestMapping { String value() default ""; }
先列出框架在初始化的时候都要作那些事情浏览器
接下来咱们一步步完成上面的操做架构
@Override public void init(ServletConfig config) throws ServletException { System.out.println("==================="); //1.加载配置文件 doLoadConfig(config.getInitParameter("contextConfigLocation")); //2.扫描全部相关联的类 doScanner(contextConfig.getProperty("scanPackage")); //3.初始化全部相关联的类,而且将其保存在IOC容器里面 doInstance(); //4.执行依赖注入(把加了@Autowired注解的字段赋值) doAutowired(); //Spring 和核心功能已经完成 IOC、DI //5.构造HandlerMapping,将URL和Method进行关联 initHandlerMapping(); System.out.println("Mars MVC framework initialized"); }
private Properties contextConfig = new Properties(); private void doLoadConfig(String location) { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location); try { contextConfig.load(inputStream); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
private void doInstance() { if(classNames.isEmpty()) return; for(String className: classNames) { try { Class<?> clazz = Class.forName(className); if(clazz.isAnnotationPresent(MarsController.class)) { Object instance = clazz.newInstance(); String beanName = lowerFirstCase(clazz.getSimpleName()); ioc.put(beanName, instance); } else if (clazz.isAnnotationPresent(MarsService.class)) { MarsService service = clazz.getAnnotation(MarsService.class); //2.优先使用自定义命名 String beanName = service.value(); if("".equals(beanName.trim())) { //1.默认使用类名首字母小写 beanName = lowerFirstCase(clazz.getSimpleName()); } Object instance = clazz.newInstance(); ioc.put(beanName, instance); //3.自动类型匹配(例如:将实现类赋值给接口) Class<?> [] interfaces = clazz.getInterfaces(); for(Class<?> inter: interfaces) { ioc.put(inter.getName(), instance); } } } catch (Exception e) { e.printStackTrace(); } } } //利用ASCII码的差值 private String lowerFirstCase(String str) { char[] chars = str.toCharArray(); chars[0] = 32; return String.valueOf(chars); }
private void doAutowired() { if(ioc.isEmpty()) return; for(Map.Entry<String, Object> entry: ioc.entrySet()) { //注入的意思就是把全部的IOC容器中加了@Autowired注解的字段赋值 //包含私有字段 Field[] fields = entry.getValue().getClass().getDeclaredFields(); for(Field field : fields) { //判断是否加了@Autowired注解 if(!field.isAnnotationPresent(MarsAutowired.class)) continue; MarsAutowired autowired = field.getAnnotation(MarsAutowired.class); String beanName = autowired.value(); if("".equals(beanName)) { beanName = field.getType().getName(); } //若是这个字段是私有字段的话,那么要强制访问 field.setAccessible(true); try { field.set(entry.getValue(), ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
private void initHandlerMapping() { if(ioc.isEmpty()) return; for(Map.Entry<String, Object> entry : ioc.entrySet()) { Class<?> clazz = entry.getValue().getClass(); if(!clazz.isAnnotationPresent(MarsController.class)) continue; String baseUrl = ""; if(clazz.isAnnotationPresent(MarsRequestMapping.class)) { MarsRequestMapping requestMapping = clazz.getAnnotation(MarsRequestMapping.class); baseUrl = requestMapping.value(); } Method[] methods = clazz.getMethods(); for(Method method : methods) { if(!method.isAnnotationPresent(MarsRequestMapping.class)) continue; MarsRequestMapping requestMapping = method.getAnnotation(MarsRequestMapping.class); String regex = requestMapping.value(); regex = (baseUrl regex).replaceAll("/ ", "/"); Pattern pattern = Pattern.compile(regex); handlerMapping.add(new Handler(entry.getValue(), method, pattern)); System.out.println("Mapping: " regex "," method.getName()); } } }
@MarsController @MarsRequestMapping("/demo") public class DemoApi { @MarsAutowired private DemoService demoService; @MarsRequestMapping("/query") public void query(HttpServletRequest req, HttpServletResponse resp, @MarsRequestParam("name") String name) { System.out.println("name: " name); String result = demoService.get(name); try{ resp.getWriter().write(result); } catch (IOException e) { e.printStackTrace(); } } @MarsRequestMapping("/add") public void add(HttpServletRequest req, HttpServletResponse resp, @MarsRequestParam("a") Integer a, @MarsRequestParam("b") Integer b) { try { resp.getWriter().write(String.format("%d %d=%d", a, b, (a b))); } catch (IOException e) { e.printStackTrace(); } } }
提供两个接口,一个经过请求名称返回响应的介绍内容,另外一个将请求的两个Integer相加并返回。mvc
public interface DemoService { String get(String name); } @MarsService public class DemoServiceImpl implements DemoService { public String get(String name) { return String.format("My name is %s.", name); } }
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>7.1.6.v20100715</version> <configuration> <stopPort>9988</stopPort> <stopKey>foo</stopKey> <scanIntervalSeconds>5</scanIntervalSeconds> <connectors> <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>60000</maxIdleTime> </connector> </connectors> <webAppConfig> <contextPath>/</contextPath> </webAppConfig> </configuration> </plugin>
点击 jetty:run 运行项目app
浏览器访问: http://localhost:8080/demo/query?name=Mars框架
浏览器访问:http://localhost:8080/demo/add?a=10&b=20
这样一个完整的spring框架就已经手写出来了,你们也能够关注下个人宫众浩【java开发之路】 为你们准备好了2019最新的面试教程和架构师资料,感谢你们的支持与关注!