【北京】 IT技術人員面對面試、跳槽、升職等問題,如何快速成長,得到大廠入門資格和升職加薪的籌碼?與大廠技術大牛面對面交流,解答你的疑惑。《從職場小白到技術總監成長之路:個人職場焦慮與救贖》活動連接: 碼客恭喜fpx,新王登基,lpl*b 咱們是冠軍java
在咱們的平常工做中,常常會用到Spring、Spring Boot、Spring Cloud、Struts、Mybatis、Hibernate等開源框架,有了這些框架的誕生,平時的開發工做量也是變得愈來愈輕鬆,咱們用 Spring Boot
分分鐘能夠新建一個Web項目。git
記得本身剛開始工做的時候仍是在用Servlet
寫Web
項目,本身寫數據庫鏈接池,用原生JDBC
操做數據庫,好了不發散了。回到這篇文章的主題,今天經過手寫Spring框架,幫你們深刻了解一下Spring的工做機制,文中涉及的代碼只用來幫助你們理解Spring,不會在線上使用,有不嚴謹的地方還請你們掠過。web
com.mars.demo
和 com.mars.framework
,以便隨後只掃描業務代碼。servlet-api
包,僅供編譯器使用,全部配置 scope
爲 provided
。首先新建一個 HttpServlet 的實現類 MarsDispatcherServlet
,用來接收請求。面試
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>
com.mars.framework.servlet.MarsDispatcherServlet
。load-on-startup
, 標記容器是否在啓動的時候就加載這個servlet(實例化並調用其init()方法)。servlet-mapping
, 將全部請求轉發到這個servlet處理。scanPackage=com.mars.demo
這個比較好理解,僅配置了一項內容,意思是要掃描的包,隨後咱們會獲取這個值去加載容器。spring
這裏僅列舉兩個,其餘都大同小異,須要源碼的能夠去個人代碼倉庫fork。數據庫
@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 ""; }
先列出框架在初始化的時候都要作那些事情編程
接下來咱們一步步完成上面的操做api
@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 doScanner(String basePackage) { //獲取要掃描包的url URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/")); File dir = new File(url.getFile()); //遍歷包下面全部文件 for(File file: dir.listFiles()) { if(file.isDirectory()){ //遞歸掃描 doScanner(basePackage + "." + file.getName()); } else { String className = basePackage + "." + file.getName().replace(".class", ""); classNames.add(className); System.out.println(className); } } }
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相加並返回。瀏覽器
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); } }
咱們的項目運行在Jetty中,因此添加相關插件以及配置:mvc
<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
運行項目
瀏覽器訪問: http://localhost:8080/demo/query?name=Mars
瀏覽器訪問:http://localhost:8080/demo/add?a=10&b=20
倉庫地址
(想自學習編程的小夥伴請搜索圈T社區,更多行業相關資訊更有行業相關免費視頻教程。徹底免費哦!)