Blade源码深入探索1--注册路由之ioc容器
来源:互联网 发布:死亡扳机2机枪 mac 编辑:程序博客网 时间:2024/05/29 11:56
最近在知乎看到一个GitHub项目,觉得适合自己学习,就打算深入研究,这里是GitHub的地址:https://github.com/biezhi/blade
附带作者大大自己写的说明:https://www.gitbook.com/book/biezhi/blade-in-action/details
依照作者写的教程,可以搭建一个简单的页面,现在开始探索blade源码,从开始注册路由开始解析。。。
根据作者自己的介绍:The Blade is the core operating class of the framework,which can be used to register routes,modify the template engine, set the file list display,static resource directory, and so on.可以知道Blade类是这个框架的核心类,可以注册路由、修改文件引擎、设置静态资源目录之类的操作。其类图大致如下:
先用me()方法创建一个新的Blade对象,blade对象里,有几个重要的属性:
/** * Copyright (c) 2017, biezhi 王爵 (biezhi.me@gmail.com) * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.blade;import com.blade.event.BeanProcessor;import com.blade.event.EventListener;import com.blade.event.EventManager;import com.blade.event.EventType;import com.blade.exception.BladeException;import com.blade.ioc.Ioc;import com.blade.ioc.SimpleIoc;import com.blade.kit.Assert;import com.blade.kit.BladeKit;import com.blade.kit.IOKit;import com.blade.kit.StringKit;import com.blade.mvc.SessionManager;import com.blade.mvc.handler.DefaultExceptionHandler;import com.blade.mvc.handler.ExceptionHandler;import com.blade.mvc.handler.RouteHandler;import com.blade.mvc.handler.WebSocketHandler;import com.blade.mvc.hook.WebHook;import com.blade.mvc.http.HttpMethod;import com.blade.mvc.http.HttpSession;import com.blade.mvc.http.Session;import com.blade.mvc.route.RouteMatcher;import com.blade.mvc.ui.template.DefaultEngine;import com.blade.mvc.ui.template.TemplateEngine;import com.blade.server.Server;import com.blade.server.netty.NettyServer;import lombok.AccessLevel;import lombok.NoArgsConstructor;import lombok.NonNull;import lombok.extern.slf4j.Slf4j;import java.nio.file.Files;import java.nio.file.Paths;import java.util.*;import java.util.concurrent.CountDownLatch;import java.util.function.Consumer;import static com.blade.mvc.Const.*;/** * Blade Core * <p> * The Blade is the core operating class of the framework, * which can be used to register routes, * modify the template engine, set the file list display, * static resource directory, and so on. * * @author biezhi 2017/5/31 */@Slf4j@NoArgsConstructor(access = AccessLevel.PRIVATE)public class Blade { /** * Project middleware list, * the default is empty, when you use the time you can call the use of methods to add. * <p> * Blade provide you with BasicAuthMiddleware, CsrfMiddleware, * you can customize the implementation of some middleware */ private List<WebHook> middleware = new ArrayList<>(); /** * BeanProcessor list, which stores all the actions that were performed before the project was started */ private List<BeanProcessor> processors = new ArrayList<>(); /** * All need to be scanned by the package, when you do not set the time will scan com.blade.plugin package */ private Set<String> packages = new LinkedHashSet<>(PLUGIN_PACKAGE_NAME); /** * All static resource URL prefixes, * defaults to "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/", * which are located under classpath */ private Set<String> statics = new HashSet<>(DEFAULT_STATICS); /** * The default IOC container implementation */ private Ioc ioc = new SimpleIoc(); /** * The default template engine implementation, this is a very simple, generally not put into production */ private TemplateEngine templateEngine = new DefaultEngine(); /** * Event manager, which manages all the guys that will trigger events */ private EventManager eventManager = new EventManager(); /** * Session manager, which manages session when you enable session */ private SessionManager sessionManager = new SessionManager(); /** * Used to wait for the start to complete the lock */ private CountDownLatch latch = new CountDownLatch(1); /** * Web server implementation, currently only netty */ private Server server = new NettyServer(); /** * A route matcher that matches whether a route exists */ private RouteMatcher routeMatcher = new RouteMatcher(); /** * Blade environment, which stores the parameters of the app.properties configuration file */ private Environment environment = Environment.empty(); /** * Exception handling, it will output some logs when the error is initiated */ private Consumer<Exception> startupExceptionHandler = (e) -> log.error("Start blade failed", e); /** * Exception handler, default is DefaultExceptionHandler. * <p> * When you need to customize the handling of exceptions can be inherited from DefaultExceptionHandler */ private ExceptionHandler exceptionHandler = new DefaultExceptionHandler(); /** * Used to identify whether the web server has started */ private boolean started = false; /** * Project main class, the main category is located in the root directory of the basic package, * all the features will be in the sub-package below */ private Class<?> bootClass = null; /** * Session implementation type, the default is HttpSession. * <p> * When you need to be able to achieve similar RedisSession */ private Class<? extends Session> sessionImplType = HttpSession.class; /** * WebSocket path */ private String webSocketPath; /** * Blade app start banner, default is Const.BANNER */ private String bannerText; /** * Blade app start thread name, default is Const.DEFAULT_THREAD_NAME */ private String threadName; /** * WebSocket Handler */ private WebSocketHandler webSocketHandler; /** * Give your blade instance, from then on will get the energy * * @return return blade instance */ public static Blade me() { return new Blade(); } /** * Get blade ioc container, default is SimpleIoc implement. * <p> * IOC container will help you hosting Bean or component, it is actually a Map inside. * In the blade in a single way to make objects reuse, * you can save resources, to avoid the terrible memory leak * * @return return ioc container */ public Ioc ioc() { return ioc; } /** * Add a get route to routes * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade get(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.GET); return this; } /** * Add a post route to routes * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade post(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.POST); return this; } /** * Add a put route to routes * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade put(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.PUT); return this; } /** * Add a delete route to routes * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade delete(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.DELETE); return this; } /** * Add a before route to routes, the before route will be executed before matching route * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade before(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.BEFORE); return this; } /** * Add a after route to routes, the before route will be executed after matching route * * @param path your route path * @param handler route implement * @return return blade instance */ public Blade after(@NonNull String path, @NonNull RouteHandler handler) { routeMatcher.addRoute(path, handler, HttpMethod.AFTER); return this; } /** * Setting blade mvc default templateEngine * * @param templateEngine TemplateEngine object * @return blade */ public Blade templateEngine(@NonNull TemplateEngine templateEngine) { this.templateEngine = templateEngine; return this; } /** * Get TemplateEngine, default is DefaultEngine * * @return return TemplateEngine */ public TemplateEngine templateEngine() { return templateEngine; } /** * Get RouteMatcher * * @return return RouteMatcher */ public RouteMatcher routeMatcher() { return routeMatcher; } /** * Register bean to ioc container * * @param bean bean object * @return blade */ public Blade register(@NonNull Object bean) { ioc.addBean(bean); return this; } /** * Register bean to ioc container * * @param cls bean class, the class must provide a no args constructor * @return blade */ public Blade register(@NonNull Class<?> cls) { ioc.addBean(cls); return this; } /** * Add multiple static resource file * the default provides the static, upload * * @param folders static resource directory * @return blade */ public Blade addStatics(@NonNull String... folders) { statics.addAll(Arrays.asList(folders)); return this; } /** * Set whether to show the file directory, default doesn't show * * @param fileList show the file directory * @return blade */ public Blade showFileList(boolean fileList) { this.environment(ENV_KEY_STATIC_LIST, fileList); return this; } /** * Set whether open gzip, default disabled * * @param gzipEnable enabled gzip * @return blade */ public Blade gzip(boolean gzipEnable) { this.environment(ENV_KEY_GZIP_ENABLE, gzipEnable); return this; } /** * Get ioc bean * * @param cls bean class type * @return return bean instance */ public Object getBean(@NonNull Class<?> cls) { return ioc.getBean(cls); } /** * Get ExceptionHandler * * @return return ExceptionHandler */ public ExceptionHandler exceptionHandler() { return exceptionHandler; } /** * Set ExceptionHandler, when you need a custom exception handling * * @param exceptionHandler your ExceptionHandler instance * @return return blade instance */ public Blade exceptionHandler(ExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; return this; } /** * Get current is developer mode * * @return return true is developer mode, else not. */ public boolean devMode() { return environment.getBoolean(ENV_KEY_DEV_MODE, true); } /** * Whether encoding setting mode for developers * The default mode is developers * * @param devMode developer mode * @return blade */ public Blade devMode(boolean devMode) { this.environment(ENV_KEY_DEV_MODE, devMode); return this; } public Class<?> bootClass() { return this.bootClass; } /** * Set whether to enable cors * * @param enableCors enable cors * @return blade */ public Blade enableCors(boolean enableCors) { this.environment(ENV_KEY_CORS_ENABLE, enableCors); return this; } /** * Get blade statics list. * e.g: "/favicon.ico", "/robots.txt", "/static/", "/upload/", "/webjars/" * * @return return statics */ public Set<String> getStatics() { return statics; } /** * When set to start blade scan packages * * @param packages package name * @return blade */ public Blade scanPackages(@NonNull String... packages) { this.packages.addAll(Arrays.asList(packages)); return this; } /** * Get scan the package set. * * @return return packages set */ public Set<String> scanPackages() { return packages; } /** * Set to start blade configuration file by default * Boot config properties file in classpath directory. * <p> * Without setting will read the classpath -> app.properties * * @param bootConf boot config file name * @return blade */ public Blade bootConf(@NonNull String bootConf) { this.environment(ENV_KEY_BOOT_CONF, bootConf); return this; } /** * Set the environment variable for global use here * * @param key environment key * @param value environment value * @return blade */ public Blade environment(@NonNull String key, @NonNull Object value) { environment.set(key, value); return this; } public Environment environment() { return environment; } /** * Set to start the web server to monitor port, the default is 9000 * * @param port web server port * @return blade */ public Blade listen(int port) { Assert.greaterThan(port, 0, "server port not is negative number."); this.environment(ENV_KEY_SERVER_PORT, port); return this; } /** * Set to start the web server to listen the IP address and port * The default will listen 0.0.0.0:9000 * * @param address ip address * @param port web server port * @return blade */ public Blade listen(@NonNull String address, int port) { Assert.greaterThan(port, 0, "server port not is negative number."); this.environment(ENV_KEY_SERVER_ADDRESS, address); this.environment(ENV_KEY_SERVER_PORT, port); return this; } /** * The use of multiple middleware, if any * * @param middleware middleware object array * @return blade */ public Blade use(@NonNull WebHook... middleware) { if (!BladeKit.isEmpty(middleware)) { this.middleware.addAll(Arrays.asList(middleware)); } return this; } /** * Get middleware list * * @return return middleware list */ public List<WebHook> middleware() { return this.middleware; } /** * Set in the name of the app blade application * * @param appName application name * @return blade */ public Blade appName(@NonNull String appName) { this.environment(ENV_KEY_APP_NAME, appName); return this; } /** * Add a event listener * When the trigger event is executed eventListener * * @param eventType event type * @param eventListener event listener * @return blade */ public Blade event(@NonNull EventType eventType, @NonNull EventListener eventListener) { eventManager.addEventListener(eventType, eventListener); return this; } /** * Get session implements Class Type * * @return return blade Session Type */ public Class<? extends Session> sessionType() { return this.sessionImplType; } /** * Set session implements Class Type, e.g: RedisSession * * @param sessionImplType Session Type implement * @return return blade instance */ public Blade sessionType(Class<? extends Session> sessionImplType) { this.sessionImplType = sessionImplType; return this; } /** * Event on started * * @param processor bean processor * @return return blade instance */ public Blade onStarted(@NonNull BeanProcessor processor) { processors.add(processor); return this; } /** * Get processors * * @return return processors */ public List<BeanProcessor> processors() { return processors; } /** * Get EventManager * * @return return EventManager */ public EventManager eventManager() { return eventManager; } /** * Get SessionManager * * @return return SessionManager */ public SessionManager sessionManager() { return sessionManager; } /** * Disable session, default is open * * @return return blade instance */ public Blade disableSession() { this.sessionManager = null; return this; } /** * Start blade application. * <p> * When all the routing in the main function of situations you can use, * Otherwise please do not call this method. * * @return return blade instance */ public Blade start() { return this.start(null, null); } /** * Start blade application * * @param mainCls main Class, the main class bag is basic package * @param args command arguments * @return return blade instance */ public Blade start(Class<?> mainCls, String... args) { return this.start(mainCls, DEFAULT_SERVER_ADDRESS, DEFAULT_SERVER_PORT, args); } /** * Start the blade web server * * @param bootClass Start the boot class, used to scan the class in all of the packages * @param address web server bind ip address * @param port web server bind port * @param args launch parameters * @return blade */ public Blade start(Class<?> bootClass, @NonNull String address, int port, String... args) { try { environment.set(ENV_KEY_SERVER_ADDRESS, address); Assert.greaterThan(port, 0, "server port not is negative number."); this.bootClass = bootClass; eventManager.fireEvent(EventType.SERVER_STARTING, this); Thread thread = new Thread(() -> { try { server.start(Blade.this, args); latch.countDown(); server.join(); } catch (Exception e) { startupExceptionHandler.accept(e); } }); String threadName = null != this.threadName ? this.threadName : environment.get(ENV_KEY_APP_THREAD_NAME, null); threadName = null != threadName ? threadName : DEFAULT_THREAD_NAME; thread.setName(threadName); thread.start(); started = true; } catch (Exception e) { startupExceptionHandler.accept(e); } return this; } /** * Await web server started * * @return return blade instance */ public Blade await() { if (!started) { throw new IllegalStateException("Server hasn't been started. Call start() before calling this method."); } try { latch.await(); } catch (Exception e) { log.error("await error", e); Thread.currentThread().interrupt(); } return this; } /** * Stop current blade application * <p> * Will stop synchronization waiting netty service */ public void stop() { eventManager.fireEvent(EventType.SERVER_STOPPING, this); server.stopAndWait(); eventManager.fireEvent(EventType.SERVER_STOPPED, this); } /** * Register WebSocket path * * @param path websocket path * @param handler websocket handler * @return return blade instance */ public Blade webSocket(@NonNull String path, @NonNull WebSocketHandler handler) { if (null != this.webSocketHandler) { throw new BladeException(500, "There is already a WebSocket path."); } this.webSocketPath = path; this.webSocketHandler = handler; System.out.println(String.format("\n\t\t\t\t\t\t\t\t\t\t\t\t\t" + "\t\t\t\t\t Register WebSocket Path: %s\n", path)); return this; } /** * Get webSocket path * * @return return websocket path */ public String webSocketPath() { return webSocketPath; } /** * Set blade start banner text * * @param bannerText banner text * @return return blade instance */ public Blade bannerText(String bannerText) { this.bannerText = bannerText; return this; } /** * Get banner text * * @return return blade start banner text */ public String bannerText() { if (null != bannerText) return bannerText; String bannerPath = environment.get(ENV_KEY_BANNER_PATH, null); if (StringKit.isNotBlank(bannerPath) && Files.exists(Paths.get(bannerPath))) { try { bannerText = IOKit.readToString(bannerPath); } catch (Exception e) { } return bannerText; } return null; } /** * Set blade start thread name * * @param threadName thread name * @return return blade instance */ public Blade threadName(String threadName) { this.threadName = threadName; return this; } /** * Get WebSocket Handler * * @return return websocket handler */ public WebSocketHandler webSocketHandler() { return webSocketHandler; }}
先分析Blade中构建Ioc容器的过程。在代码第99行中,有一句 private Ioc ioc = new SimpleIoc();
Blade里实现一个默认的ioc容器,将一些默认的对象存储到ioc容器中,再新建一个SimpleIoc对象,其类图如下:
SimpleIoc类
package com.blade.ioc;import com.blade.ioc.bean.BeanDefine;import lombok.extern.slf4j.Slf4j;import java.util.*;/** * The default IOC container implementation * * @author <a href="mailto:biezhi.me@gmail.com" target="_blank">biezhi</a> * @since 1.5 */@Slf4jpublic class SimpleIoc implements Ioc { private final Map<String, BeanDefine> pool = new HashMap<>(32); /** * Add user-defined objects */ @Override public void addBean(Object bean) { addBean(bean.getClass().getName(), bean); } /** * Add user-defined objects */ @Override public void addBean(String name, Object bean) { BeanDefine beanDefine = new BeanDefine(bean); addBean(name, beanDefine); // add interface Class<?>[] interfaces = beanDefine.getType().getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { this.addBean(interfaceClazz.getName(), beanDefine); } } } /** * Update BeanDefine */ @Override public void setBean(Class<?> type, Object proxyBean) { BeanDefine beanDefine = pool.get(type.getName()); if (beanDefine != null) { beanDefine.setBean(proxyBean); } else { beanDefine = new BeanDefine(proxyBean, type); } pool.put(type.getName(), beanDefine); } /** * Register @Bean marked objects */ @Override public <T> T addBean(Class<T> type) { Object bean = addBean(type, true); return type.cast(bean); } @Override public <T> T getBean(Class<T> type) { Object bean = this.getBean(type.getName()); try { return type.cast(bean); } catch (Exception e) { e.printStackTrace(); } return null; } @Override public Object getBean(String name) { BeanDefine beanDefine = pool.get(name); if (beanDefine == null) { return null; } return beanDefine.getBean(); } @Override public List<BeanDefine> getBeanDefines() { return new ArrayList<>(pool.values()); } @Override public BeanDefine getBeanDefine(Class<?> type) { return this.getBeanDefine(type, true); } @Override public List<Object> getBeans() { Set<String> beanNames = this.getBeanNames(); List<Object> beans = new ArrayList<>(beanNames.size()); for (String beanName : beanNames) { Object bean = this.getBean(beanName); if (null != bean) { beans.add(bean); } } return beans; } @Override public Set<String> getBeanNames() { return pool.keySet(); } @Override public void remove(String beanName) { pool.remove(beanName); } @Override public void remove(Class<?> type) { pool.remove(type.getSimpleName()); } @Override public void clearAll() { pool.clear(); } /** * Add user-defined objects */ private void addBean(String name, BeanDefine beanDefine) { if (pool.put(name, beanDefine) != null) { log.warn("Duplicated Bean: {}", name); } } /** * Register @Bean marked objects */ private Object addBean(Class<?> type, boolean singleton) { return addBean(type.getName(), type, singleton); } /** * Register @Bean marked objects */ private Object addBean(String name, Class<?> beanClass, boolean singleton) { BeanDefine beanDefine = this.getBeanDefine(beanClass, singleton); if (pool.put(name, beanDefine) != null) { log.warn("Duplicated Bean: {}", name); } // add interface Class<?>[] interfaces = beanClass.getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { if (null != this.getBean(interfaceClazz)) { break; } this.addBean(interfaceClazz.getName(), beanDefine); } } return beanDefine.getBean(); } private BeanDefine getBeanDefine(Class<?> beanClass, boolean singleton) { try { Object object = beanClass.newInstance(); return new BeanDefine(object, beanClass, singleton); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; }}
其中BeanDefine类定义了一个ioc的目标
package com.blade.ioc.bean;/** * Bean Define, IOC to define a target * * @author <a href="mailto:biezhi.me@gmail.com" target="_blank">biezhi</a> * @since 1.5 */public class BeanDefine { private Object bean; private Class<?> type; private boolean isSingle; public BeanDefine(Object bean) { this(bean, bean.getClass()); } public BeanDefine(Object bean, Class<?> type) { this.bean = bean; this.type = type; this.isSingle = true; } public BeanDefine(Object bean, Class<?> type, boolean isSingle) { this.bean = bean; this.type = type; this.isSingle = isSingle; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public Class<?> getType() { return type; } public void setType(Class<?> type) { this.type = type; } public boolean isSingle() { return isSingle; } public void setSignle(boolean isSingle) { this.isSingle = isSingle; }}
又因为SimpleIoc类中,私有属性:
private final Map<String, BeanDefine> pool = new HashMap<>(32);
所以在启动的时候,SimpleIoc对象会先创建一个ioc的池子pool,用来实现ioc的存储Bean对象。每个pool里的对象,有三个属性:bean的名称、class类型的type、还是一个布尔值。
接下来再回到Blade中看代码,框架中先注册一个Object对象到ioc容器中,调用SimpleIoc中的addBean(Object bean)方法:
public void addBean(Object bean) { addBean(bean.getClass().getName(), bean); }
再调用addBean(String name, Object bean)方法:public void addBean(String name, Object bean) { BeanDefine beanDefine = new BeanDefine(bean); addBean(name, beanDefine); // add interface Class<?>[] interfaces = beanDefine.getType().getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { this.addBean(interfaceClazz.getName(), beanDefine); } } }
先创建一个BeanDefine对象,然后调用addBean(String name, BeanDefine beanDefine)方法,向一开始定义的IOC容器pool池子中添加用户定义的对象。再通过for循环获得这个对象所有接口,然后把这些再次用addBean(String name, BeanDefine beanDefine)方法添加进去。
那么这个方法就完成了,继续
接下来又注册一个任意类型的class,调用addBean(Class<T> type)方法:
public <T> T addBean(Class<T> type) { Object bean = addBean(type, true); return type.cast(bean); }返回通过此Class对象所表示的类或接口,调用addBean(Class<?> type, boolean singleton)方法:
private Object addBean(Class<?> type, boolean singleton) { return addBean(type.getName(), type, singleton); }再通过addBean(String name, Class<?> beanClass, boolean singleton)方法:
private Object addBean(String name, Class<?> beanClass, boolean singleton) { BeanDefine beanDefine = this.getBeanDefine(beanClass, singleton); if (pool.put(name, beanDefine) != null) { log.warn("Duplicated Bean: {}", name); } // add interface Class<?>[] interfaces = beanClass.getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { if (null != this.getBean(interfaceClazz)) { break; } this.addBean(interfaceClazz.getName(), beanDefine); } } return beanDefine.getBean(); }此方法中先调用getBeanDefine(Class<?> beanClass, boolean singleton)方法:
private BeanDefine getBeanDefine(Class<?> beanClass, boolean singleton) { try { Object object = beanClass.newInstance(); return new BeanDefine(object, beanClass, singleton); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; }先将一开始从Blade传递过来的Class<?>对象,创建此Class对象所表示的类的新实例。再返回一个BeanDefine对象,
再在addBean()方法中,先检验pool中有没有这个bean,然后获取Blade传递过来的Class<?>所有的实例,然后把这些再次用addBean(String name, BeanDefine beanDefine)方法添加进IOC容器的pool池子中去。
这样这个IOC容器就可以完成存储对象的功能,其他的下次再进行分析了。。。
不过从代码中看出作者使用Java反射机制完成设计ioc,改天去研究jvm的反射机制吧。。。
- Blade源码深入探索1--注册路由之ioc容器
- Blade源码深入探索2--server
- IOC容器构造之注册BeanDefinition源码分析
- Spring源码解读-Spring IoC容器初始化之资源注册
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册
- 深入探索IOC容器的实质
- Spring源码阅读之IoC容器初始化1 -- Resource定位
- Spring源码解析-BeanDefinition在IOC容器中的注册(三)
- Spring源码阅读--BeanDefinition 在 IOC 容器中的注册
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- 【Spring源码解析】之IOC容器
- Spring IOC源码详解之容器初始化
- Spring IOC源码详解之容器初始化
- Spring IOC源码详解之容器初始化
- 深入学习Spring源码---基本IOC容器初始化
- 深入研究Spring-IoC:源码分析容器创建
- 深入Spring IOC源码之ResourceLoader
- 深入Spring IOC源码之Resource
- python面向对象编程
- vim结合findstr实现windows下快速在文件中查找
- QTableWidget实现鼠标移动整行颜色变化
- javaweb在阿里云centos7下搭建mysql5.6简单的主从结构
- spring 事务管理 mysql默认事务
- Blade源码深入探索1--注册路由之ioc容器
- 贪心算法
- 使用psql命令基于udp(unix-domain-socket)协议连接postgresql数据库
- JVM GC收集器及其收集算法
- 微信小程序-gulp构建
- 如何选择一款合适的核心板进行开发
- Java 对象的生命周期
- 计算总价 商品总数 以及BigDecimal转int
- Centos7.1搭建openstack私有云