spring源码刨析笔记

1.概述

spring就是 spring Framework
Ioc Inversion of Control(控制反转/反转控制)
DI Dependancy Injection(依赖注入)
Aop Aspect oriented Programming 面向切面编程(OOP的延续)

2.Ioc与DI

Ioc与Aop的区别

Ioc在对象角度将对象实例化以及管理的权力交给了容器
DI在容器角度将对象依赖注入到其他对象

3.Aop

3.1横切逻辑代码

多个纵向流程中出现相同子流程代码

出现的问题:
横切代码重复问题,与业务逻辑代码混在一起,臃肿,维护不方便
Aop不改变业务逻辑情况下,增强横切逻辑代码,根本上解耦合代码

4.自定义的Spring框架

//将private AccountDao accountDao = new JdbcAccountDaoImpl();转换为 private AccountDao accountDao;// 构造函数传值/set方法传值public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}

4.1思路

public class BeanFactory {//集合的定义放在类的最上面来,因为后面的使用时在static静态代码块中,否则在使用时集合对象会为nullstatic List<String> classNames = new ArrayList<>();    // 缓存扫描到的class全限定类名static List<String> fieldsAlreayProcessed = new ArrayList<>(); // 缓存已经进行过依赖注入的信息/*** 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)* 任务二:对外提供获取实例对象的接口(根据id获取)*/private static Map<String,Object> map = new HashMap<>();  // 存储对象
}

1.遍历扫描路径

//获取扫描包的全限定类名

<beans><component-scan base-package="com.lagou.edu"></component-scan>
</beans>
  // 加载xmlInputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");// 解析xmlSAXReader saxReader = new SAXReader();Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();Element scanElement = (Element) rootElement.selectSingleNode("//component-scan");String scanPackage = scanElement.attributeValue("base-package");/*** 扫描指定包下的注解*/private static void doScan(String scanPackage) {String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");File pack = new File(scanPackagePath);File[] files = pack.listFiles();for(File file: files) {if(file.isDirectory()) { // 子package// 递归doScan(scanPackage + "." + file.getName());  // com.lagou.demo.controller}else if(file.getName().endsWith(".class")) {String className = scanPackage + "." + file.getName().replaceAll(".class", "");classNames.add(className);}}}

2.对象实例化

 /*** 通过反射实例化对象,此时暂不维护依赖注入关系*/private static void doInstance() {if (classNames.size() == 0) return;try {for (int i = 0; i < classNames.size(); i++) {String className = classNames.get(i);// 反射Class<?> aClass = Class.forName(className);// 只处理标注了注解@MyService、@MyRepository和@MyComponent的类if (aClass.isAnnotationPresent(MyService.class)|| aClass.isAnnotationPresent(MyRepository.class)|| aClass.isAnnotationPresent(MyComponent.class)) {//获取注解value值String beanName = null;if (aClass.isAnnotationPresent(MyService.class)) {beanName = aClass.getAnnotation(MyService.class).value();} else if (aClass.isAnnotationPresent(MyRepository.class)) {beanName = aClass.getAnnotation(MyRepository.class).value();} else if (aClass.isAnnotationPresent(MyComponent.class)) {beanName = aClass.getAnnotation(MyComponent.class).value();}// 如果指定了id,就以指定的为准Object o = aClass.newInstance();if ("".equals(beanName.trim())) {beanName = lowerFirst(aClass.getSimpleName());}map.put(beanName,o);// service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到容器中,便于后期根据接口类型注入Class<?>[] interfaces = aClass.getInterfaces();if(interfaces != null && interfaces.length > 0) {for (int j = 0; j < interfaces.length; j++) {Class<?> anInterface = interfaces[j];// 以接口的全限定类名作为id放入map.put(anInterface.getName(), aClass.newInstance());}}} else {continue;}}} catch (Exception e) {e.printStackTrace();}}

3.维护注入关系

  /** 实现依赖注入*/private static void doAutoWired(){if(map.isEmpty()) {return;}// 遍历ioc中所有对象,查看对象中的字段,是否有@LagouAutowired注解,如果有需要维护依赖注入关系for(Map.Entry<String,Object> entry: map.entrySet()) {try {doObjectDependancy(entry.getValue());} catch (IllegalAccessException e) {e.printStackTrace();}}}/*** A 可能依赖于 B ,B 可能依赖于 C ,C 可能又依赖于D,本方法主要维护一下嵌套依赖*/private static void doObjectDependancy(Object object) throws IllegalAccessException {Field[] declaredFields = object.getClass().getDeclaredFields();if(declaredFields == null || declaredFields.length ==0) {return;}// 遍历判断处理for (int i = 0; i < declaredFields.length; i++) {Field declaredField = declaredFields[i];if (!declaredField.isAnnotationPresent(MyAutowired.class)) {continue;}// 判断当前字段是否处理过,如果已经处理过则continue,避免嵌套处理死循环if(fieldsAlreayProcessed.contains(object.getClass().getName()  + "." + declaredField.getName())){continue;}Object dependObject = null;dependObject = map.get(declaredField.getType().getName());  //  先按照声明的是接口去获取,如果获取不到再按照首字母小写if(dependObject == null) {dependObject = map.get(lowerFirst(declaredField.getType().getSimpleName()));}// 记录下给哪个对象的哪个属性设置过,避免死循环fieldsAlreayProcessed.add(object.getClass().getName() + "." + declaredField.getName());// 迭代doObjectDependancy(dependObject);declaredField.setAccessible(true);declaredField.set(object,dependObject);}}

4.维护事务

  /** 实现事务管理,为添加了@MyTransactional注解的对象创建代理对象,并覆盖原IOC容器中的对象*/private static void doTransactional() {ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");for(Map.Entry<String,Object> entry: map.entrySet()) {String beanName = entry.getKey();Object o = entry.getValue();Class<?> aClass = entry.getValue().getClass();if(aClass.isAnnotationPresent(MyTransactional.class)) {// 需要进行事务控制// 有实现接口Class<?>[] interfaces = aClass.getInterfaces();if(interfaces != null && interfaces.length > 0) {// 使用jdk动态代理map.put(beanName,proxyFactory.getJdkProxy(o));}else{// 使用cglib动态代理map.put(beanName,proxyFactory.getCglibProxy(o));}}}}

4.2代码

创建connection的时候放到同一个线程中

public class ConnectionUtils {private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存储当前线程的连接/*** 从当前线程获取连接*/public Connection getCurrentThreadConn() throws SQLException {/*** 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取一个连接绑定到当前线程*/Connection connection = threadLocal.get();if(connection == null) {// 从连接池拿连接并绑定到线程connection = DruidUtils.getInstance().getConnection();// 绑定到当前线程threadLocal.set(connection);}return connection;}
}

5. FactoryBean 和 BeanFactory区别

BeanFactory是个bean 工厂,是一个工厂类(接口), 它负责生产和管理bean的一个工厂
是ioc 容器最底层的接口,是个ioc容器,是spring用来管理和装配普通bean的ioc容器(这些bean成为普通bean)。

FactoryBean是个bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由spring管理后,生产的对象是由getObject()方法决定的。

// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {@Nullable// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器的单例对象缓存池中MapT getObject() throws Exception;@Nullable// 返回FactoryBean创建的Bean类型Class<?> getObjectType();// 返回作⽤域是否单例default boolean isSingleton() {return true;} }public class CompanyFactoryBean implements FactoryBean<Company> {@Overridepublic Company getObject() throws Exception {// 模拟创建复杂对象CompanyCompany company = new Company();String[] strings = companyInfo.split(",");company.setName(strings[0]);company.setAddress(strings[1]);company.setScale(Integer.parseInt(strings[2]));return company;}}

6.spring的循环引用

1.构造器注入循环依赖

2.@Autowired的依赖注入

6.1流程

①:构造器的循环依赖。【这个Spring解决不了】

Spring是先将Bean对象实例化【依赖无参构造函数】—>再设置对象属性的


//一级缓存
//singletonFactories : 单例对象工厂的cache
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
//二级缓存
// earlySingletonObjects :提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥】
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
//三级缓存//singletonObjects:单例对象的cache
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Names of beans that are currently in creation. */
// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
// 它在Bean开始创建时放值,创建完成时会将其移出~
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** Names of beans that have already been created at least once. */
// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
// 至少被创建了一次的  都会放进这里~~~~
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

6.2为什么不用二级缓存要用三级缓存

可以二级缓存就可以初始化一些内容,

在将三级缓存放入二级缓存的时候,会判断是否有SmartInstantiationAwareBeanPostProcessor这样的后置处理器,换句话说这里是给用户提供接口扩展的,所以采用了三级缓存, 特殊写法中

6.3源代码中的特殊写法

 //添加到三级缓存中addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//====改写后addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {getEarlyBeanReference(beanName, mbd, bean);} });//ObjectFactory方法public interface ObjectFactory<T> {T getObject() throws BeansException;}//addSingletonFactory方法        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//添加三级缓存this.singletonFactories.put(beanName, singletonFactory);//移除二级this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}//getEarlyBeanReference方法protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 这么一大段就这句话是核心,也就是当bean要进行提前曝光时,// 给一个机会,通过重写后置处理器的getEarlyBeanReference方法,来自定义操作bean// 值得注意的是,如果提前曝光了,但是没有被提前引用,则该后置处理器并不生效!!!// 这也正式三级缓存存在的意义,否则二级缓存就可以解决循环依赖的问题exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;}

6.4文字版叙述流程

  1. 使用context.getBean(A.class),旨在获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~

  2. 实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)

  3. 初始化A:@Autowired依赖注入B(此时需要去容器内获取B)为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~

  4. 实例化B,并将其放入缓存。

  5. 初始化B,@Autowired依赖注入A(此时需要去容器内获取A)

  6. 此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回(此时B也能够被引用了)然后调用AOP的后置处理器类:getEarlyBeanReference,拿到代理后的bean(假设此处切面满足,要创建代理);

    经过上面的步骤后,B里面,field已经填充ok,其中,且填充的field是代理后的A,这里命名为proxy A。

    B 继续其他的后续处理。

  7. B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。

  8. 因为B实例已经成功返回了,因此最终A也初始化成功

  9. 到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B

6.5 spring的事务

1.四大特征

原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)

2.事务的隔离级别

Serializable(串⾏化):可避免脏读、不可重复读、虚读情况的发⽣。(串⾏化) 最⾼
Repeatable read(可重复读):可避免脏读、不可重复读情况的发⽣。(幻读有可能发⽣) 第⼆
该机制下会对要update的⾏进⾏加锁
Read committed(读已提交):可避免脏读情况发⽣。不可重复读和幻读⼀定会发⽣。 第三
Read uncommitted(读未提交):最低级别,以上情况均⽆法保证。(读未提交) 最低
注意:级别依次升⾼,效率依次降低
MySQL的默认隔离级别是:REPEATABLE READ

3.事务的传播行为

PROPAGATION_REQUIRED 如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中,
加⼊到这个事务中。这是最常⻅的选择
PROPAGATION_SUPPORTS ⽀持当前事务,如果当前没有事务,就以⾮事务⽅式执⾏。
PROPAGATION_MANDATORY 使⽤当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执⾏。如果当前没有事务,则
执⾏与PROPAGATION_REQUIRED类似的操作。

6.6资料

1.https://blog.csdn.net/chaitoudaren/article/details/104833575
2.https://zhuanlan.zhihu.com/p/84267654
3.https://blog.csdn.net/qq_36381855/article/details/79752689

java代理

1.java动态代理

实现了InvocationHandler接口

 public Object getJdkProxy(Object obj) {Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try{// 开启事务(关闭事务的自动提交)transactionManager.beginTransaction();result = method.invoke(obj,args);// 提交事务transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滚事务transactionManager.rollback();// 抛出异常便于上层servlet捕获throw e;}return result;}});
}

2.cglib动态代理

实现MethodInterceptor接口

 public Object getCglibProxy(Object obj) {//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数return  Enhancer.create(obj.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object result = null;try{// 开启事务(关闭事务的自动提交)transactionManager.beginTransaction();result = method.invoke(obj,objects);// 提交事务transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滚事务transactionManager.rollback();// 抛出异常便于上层servlet捕获throw e;}return result;}});}

3.区别

1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,
对于final类或方法,是无法继承的

链接:https://www.jianshu.com/p/46d092bb737d

补充

1.ThreadLocal是什么

// 存储当前线程的连接
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

ThreadLocal 内部维护了一个 Map ,这个 Map 是叫做 ThreadLocalMap 里卖弄有个Entey数组的table

ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。

ThreadLocal 适用于如下两种场景

  • 每个线程需要有自己单独的实例
  • 实例需要在多个方法中共享,但不希望被多线程共享

1.1源代码刨析

总结
1.每个线程持有一个ThreadLocalMap对象==》set()方法没有的时候会去CreateMap()

2.每个线程Thread持有一个ThreadLocalMap类型的实例内含Entry(可以理解为每个线程Thread都持有一个Entry型的数组table)

 //set方法public void set(T value) {//获取当前线程Thread t = Thread.currentThread();//取值ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}//创建一个ThreadLocalMap方法void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}//ThreadLocalMap方法static class ThreadLocalMap {//Entry数组private Entry[] table;//Entry对象 继承了弱引用的接口static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated(允许) with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}//ThreadLocalMap对象的实例化ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {private static final int INITIAL_CAPACITY = 16;//初始化因子为16table = new Entry[INITIAL_CAPACITY];//位运算,结果与取模相同,计算出需要存放的位int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);//给ThreadLocalMap存值       private void set(ThreadLocal<?> key, Object value) {//获取ThreadLocalMap的TableEntry[] tab = table;int len = tab.length;//将threadLocalHashCode进行一个位运算(取模)得到索引iint i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}}
}

1.2总结

1.不同线程之间访问时访问的是不同的table数组的同一位置即都为table[i],只不过这个不同线程之间的table是独立的。
2.对于同一线程的不同ThreadLocal来讲,这些ThreadLocal实例共享一个table数组,然后每个ThreadLocal实例在table中的索引i是不同的。

1.3ThreadLocal和Synchronized

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是
Synchronized是通过线程等待,牺牲时间来解决访问冲突
ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

链接:https://www.jianshu.com/p/3c5d7f09dfbd

1.4关于Spring,ioc创建的对象和new创建的对象有啥区别

Spring Bean是反射创建,先创建bean之后,有一系列的步骤(ioc 12个步骤)可以被开发中敢于修改扩展,最终把创建好的对象放在map里

读源码

今天我就给大家分享一下路神的Spring源码学习方法:(源码的学习方法是通用的)

1、通读Spring官方文档
学习Spring源码之前,首先要把Spring官方网文档系统的阅读一遍。哪怕你读不懂,也会接触到很多名词,读源码的时候大有帮助。
有人拿自己英语不好当借口,子路笑言自己的英文水平经常被人喷,这个困难要自己克服。

2、如何正确阅读Spring源码
读完源码就忘,是因为你没有理解透彻。子路建议:“不要从字面意义上去读源码,通过源码启动方式阅读。”
比如读nacos的源码,要理解作者做这个设计变量的思路、设计代码的原则、作者的想法是怎样的?
比如nacos跟Spring、Spring boot、Spring cloud这四个角色分别完成什么样的功能?Spring cloud中Spring-cloud-common这个包有什么用?Spring boot主要完成的功能?Spring又完成什么功能?
那么三者结合在一起就可以看出作者写代码的意图,一定要站在作者的角度,结合全局来看源码。
3、尽情去调试Spring吧
源码级的知识一定要自己验证!特别是Spring的扩展点!
在学习过程中,不要怕,尽量多去调试;看一下就去断点调试一下;多去写自己的注释;尽量去把Spring代码改了,把代码给删了!
多思考Spring某些地方预留的接口能干嘛?这个地方是不是可以做扩展?MyBatis是如何扩展Spring的?市面上还有哪些主流框架扩展了Spring?边看源码边思考,这样你的记忆会加深很多。
学习Spring源码目的就是为了让我们能够去对Spring做二次开发或者扩展。

实话实说,大多数人学Spring,就是为了去面试。很多人在简历上写“读过Spring源码”,这么写你连电话都接不到!
我们读过Spring源码之后,简历上该怎么写?给大家做个参考:
系统的阅读过Spring源码;
能够对Spring做二次开发;
并且熟知Spring当中的各种扩展点;
熟知主流框架对Spring源码的扩展;

单词

Injection 注射注入
current 现在,流通的
associated关联的,联系
necessary必须的,必要的
rehash重复
refresh更新,恢复
obtain获得流行

triggered 引起 触发

学习资源

spring循环依赖 https://mp.weixin.qq.com/s/RFxoVMeW5Mx9kzzFGhpyKA
阅读源码的方式:https://mp.weixin.qq.com/s/XQ5jl2pUa1W7Ueu0pWFS2Q

什么是ThreadLocal:https://mp.weixin.qq.com/s/bcH2pL06J5udBWedE1tbCA

springBean的生命周期:https://mp.weixin.qq.com/s/rz9cZRLZZnA_kahetEYjaw

spring源码刨析总结相关推荐

  1. springMvc源码刨析笔记

    springMvc源码刨析笔记 MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 We ...

  2. zookeeper笔记+源码刨析

    会不断更新!冲冲冲!跳转连接 https://blog.csdn.net/qq_35349982/category_10317485.html zookeeper 1.介绍 Zookeeper 分布式 ...

  3. Metis异常检测算法率值检测和量值检测源码刨析

    Metis异常检测算法率值检测和量值检测源码刨析 1. 测试代码 2. 率值检测 2.1 rate_predict方法(detect.py) 2.2 predict方法(statistic.py) 2 ...

  4. MapReduce源码刨析

    MapReduce编程刨析: Map map函数是对一些独立元素组成的概念列表(如单词计数中每行数据形成的列表)的每一个元素进行指定的操作(如把每行数据拆分成不同单词,并把每个单词计数为1),用户可以 ...

  5. dubbo笔记+源码刨析

    会不断更新!冲冲冲!跳转连接 https://blog.csdn.net/qq_35349982/category_10317485.html dubbo笔记 1.概念 RPC全称为remote pr ...

  6. JsonRpc源码--处理http请求源码刨析

    从jsonRpc接入http请求直至开始业务逻辑处理总体层级如下: JsonServiceExporter->handleRequest-> handle -> handleRequ ...

  7. mybatis源码刨析总结

    拉勾 mybatis 初始化 1.创建git仓库 1.新建一个目录 然后点击右键 git base here 创建git (会弹出一个窗口) 2.初始化 再窗口输入 git init 3.指定仓库 g ...

  8. JSP 九大内置对象及作用域(源码刨析,建议收藏)

    JSP内置对象及作用域 九大内置对象 PageContext 用来保存东西 Request 用来保存东西 Response Session 用来保存东西 Application[ServletCont ...

  9. JSP的基础语法和指令(源码刨析,建议收藏)

    JSP的基础语法和指令 JSP表达式 <%--JSP表达式 作用:将程序输出到客户端 <%= 变量或者表达式%> --%> <%= new java.util.Date( ...

最新文章

  1. 浅析C# Dictionary实现原理
  2. 好文转载——追求卓越之旅
  3. 电源空间辐射CDN余量低_EMI辐射整改
  4. Opencms安装和配置
  5. 【Python】 子进程创建与使用subprocess
  6. 关于考研与工作(人生规划)的个人思考
  7. 菜鸟车辆路径规划创造26项世界纪录 实际可降低10.3%配送成本
  8. PP视频如何设置默认缓存个数
  9. 本文介绍使用OpenCV-Python进行形态学处理
  10. java.util.concurrent.ExecutorService 接口 源码
  11. 云南计算机专升本数据结构_云南计算机专升本分数大全(公布分数线431分)
  12. 当systeminfo不能显示系统启动时间了--用命令行修复一下
  13. 软件配置管理的作用?软件配置包括什么?
  14. 笔记本开机循环显示:this product is covered by one or more of the following patents 解决办法
  15. wps目录怎么加一条_WPS中如何正确插入目录_WPS怎么做目录
  16. 开发一个安卓App-计算器-改色换肤(完结篇)
  17. ArchLinux Plasma 简洁优雅桌面环境设置
  18. 请更换备份电池 pos机_电签POS机实力碾压MPOS,请更换手中的蓝牙机!
  19. 【读点论文】FBNetV2:Differentiable Neural Architecture Search for Spatial and Channel D扩大搜索空间,复用featuremap
  20. 2022-2028年全球与中国车辆传感器行业发展趋势及投资战略分析

热门文章

  1. 南京信息工程大学c语言真题,南京信息工程大学C语言试题库.doc
  2. Java黑皮书课后题第10章:**10.28(实现StringBuilder类)在Java库中提供了StringBuilder类。给出你对下面方法的实现(将新类命名为MyStringBuilder2)
  3. Java黑皮书课后题第1章:1.4(打印表格)编写程序,显示以下表格
  4. 2011年吉林大学计算机研究生机试真题
  5. Java面试题之HashMap如何有效减少碰撞
  6. sqlserver2008数据库自动备份的sql脚本及使用bat命令执行脚本
  7. Web.xml配置详解之context-param(转)
  8. 读取String数组内的内容
  9. [知识图谱实战篇] 八.HTML+D3绘制时间轴线及显示实体
  10. Swift之深入解析反射Mirror与错误处理