文档章节

Springboot 随笔(1) -- 自动引入配置与启动机制

alexqdjay
 alexqdjay
发布于 2016/10/09 11:42
字数 1579
阅读 506
收藏 4
点赞 0
评论 0

为什么用SpringBoot?

同上题记。总结:快速开始,方便搭建,开发web时并不需要Tomcat或者Jetty,甚至连插件都不用(因为自带Tomcat或自配置成Jetty)。

肯定有缺点吧?

一个框架除了知道他的优点,肯定要知道他的缺点。

SpringBoot 缺点如下(暂时发现):

  1. 配置逻辑隐藏太深,所以如果有很多自定义的需要翻源码看,如配置多个Servlet
  2. 配置Bean化,替代XML。Bean和XML谁更优?一半一半,所以建议Bean和XML混用(SpringBoot提供这种方式),有时XML定义更加清晰。
  3. 文档略少,有时需要翻源码才知道用法。
  4. 默认加载的AutoConfig有点多,所以影响启动速度。网上有优化方式,基本思想就是去除@SpringBootApplication,使用自己编写@Import,但是这样SpringBoot的便捷性就没有了。

SpringBoot 的运行机制

1. 引入配置

使用 @Import  引入配置Bean,有三种方式:

1) 直接引入 configuration.class

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

DelegatingWebMvcConfiguration 就是配置的Configuration类

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    //...
}

2) 引入 ImportSelector 接口的实现

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

只要实现该接口,返回需要加载的 Configuration 类名字符串即可,见例子:

static class CacheConfigurationImportSelector implements ImportSelector {
    CacheConfigurationImportSelector() {
    }

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        CacheType[] types = CacheType.values();
        String[] imports = new String[types.length];

        for(int i = 0; i < types.length; ++i) {
            imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
        }

        return imports;
    }
}

3) 引入 ImportBeanDefinitionRegistrar  接口的实现类

public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2);
}

很好理解,手动将需要注入的bean的definition放入BeanDefinitionRegistry 

2. 以 DataSource 自动配置为例子

所有的开始都是源于 DataSourceAutoConfiguration 这个类,就是说如果你想自动化生成 DataSrouce  你只要在你的配置类引入该类:

@Configuration
@Import({DataSourceAutoConfiguration.class})
public class Application {
    // ...
}

DataSourceAutoConfiguration 中引入了其他配置类,并且使用 @Bean 来生成需要的组件:

@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class})
public class DataSourceAutoConfiguration {
}

引入了 Registrar 和 DataSourcePoolMetadataProvidersCofniguration , 这里以 Registrar 为例介绍:

static class Registrar implements ImportBeanDefinitionRegistrar {
    private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";

    Registrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if(!registry.containsBeanDefinition("dataSourceInitializerPostProcessor")) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);
            beanDefinition.setRole(2);
            beanDefinition.setSynthetic(true);
            registry.registerBeanDefinition("dataSourceInitializerPostProcessor", beanDefinition);
        }

    }
}

这是符合上面的第3种方式,目的就是注册一个PostProcessor 来处理注册进来的 DataSource, 凡是有DataSource实例,就实例化 DataSourceInitializer  (用于预跑一些初始化的SQL脚步)。

重点是下面

@Configuration
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Tomcat.class, Hikari.class, Dbcp.class, Dbcp2.class})
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

引入三个配置类,Tomcat、Hikari、Dbcp、Dbcp2,处理逻辑基本一致,都是判断是否有对于的类和配置,以Dbcp为例:

@ConditionalOnClass({org.apache.commons.dbcp.BasicDataSource.class})
@ConditionalOnProperty(
    name = {"spring.datasource.type"},
    havingValue = "org.apache.commons.dbcp.BasicDataSource",
    matchIfMissing = true
)
static class Dbcp extends DataSourceConfiguration {
    Dbcp() {
    }

    @Bean
    @ConfigurationProperties("spring.datasource.dbcp")
    public org.apache.commons.dbcp.BasicDataSource dataSource(DataSourceProperties properties) {
        org.apache.commons.dbcp.BasicDataSource dataSource = (org.apache.commons.dbcp.BasicDataSource)this.createDataSource(properties, org.apache.commons.dbcp.BasicDataSource.class);
        DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
        String validationQuery = databaseDriver.getValidationQuery();
        if(validationQuery != null) {
            dataSource.setTestOnBorrow(true);
            dataSource.setValidationQuery(validationQuery);
        }

        return dataSource;
    }
}

最终 @Bean 生成 dataSource 实例。

3. 自动化引入配置类

这个秘密就隐藏在 @SpringBootApplication

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
)}
)
public @interface SpringBootApplication

从源码中可见,@SpringBootApplication = @EnableAutoConfiguration + @SpringBootConfiguration + @ComponentScan

很明显,EnableAutoConfiguration 是自动化配置的关键

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration

EnableAutoConfigurationImportSelector implements DeferredImportSelector

符合上面第3中引入方式,EnableAutoConfigurationImportSelector 的主要功能就是将spring.factories 配置的config获取,返回出来

spring.factories 中都是些什么:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

前面分析的 DataSourceAutoConfiguration  就在上面,由此可见,这些AutoConfig都被自动引入。

这么多配置可能由于的就一半都不到,所以如果优化启动速度,那么就 手动@Import 即可,不过有点麻烦。

SpringBoot 的启动

    public static void main(String[] args) throws LifecycleException, InterruptedException {
        SpringApplication.run(Application.class, args);
    }

一切的开始都是从这段代码,所以 SpringApplication.run 是分析入口,最终追踪到源码:

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
  return (new SpringApplication(sources)).run(args);
}

实例化,然后run。

实例化中,还调用判断了是否Web环境,原理是判断是否存在两个class:

private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};    
    
    private boolean deduceWebEnvironment() {
        String[] var1 = WEB_ENVIRONMENT_CLASSES;
        int var2 = var1.length;

        for(int var3 = 0; var3 < var2; ++var3) {
            String className = var1[var3];
            if(!ClassUtils.isPresent(className, (ClassLoader)null)) {
                return false;
            }
        }

        return true;
    }

run的源码:

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.started();

    try {
        DefaultApplicationArguments ex = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex); // env配置处理, application.properties等
        Banner printedBanner = this.printBanner(environment); // 打印 Banner
        context = this.createApplicationContext(); // 关键(1)
        this.prepareContext(context, environment, listeners, ex, printedBanner);// 一些赋值,及调用initer
        this.refreshContext(context); // refresh ctx, 视为启动ctx 关键(2)
        this.afterRefresh(context, ex);
        listeners.finished(context, (Throwable)null);
        stopWatch.stop();
        if(this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        return context;
    } catch (Throwable var8) {
        this.handleRunFailure(context, listeners, var8);
        throw new IllegalStateException(var8);
    }
}

分析关键(1)的源码

    protected ConfigurableApplicationContext createApplicationContext() {
        Class contextClass = this.applicationContextClass;
        if(contextClass == null) {
            try {
                contextClass = Class.forName(this.webEnvironment?"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext":"org.springframework.context.annotation.AnnotationConfigApplicationContext");
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
    }

很简单,判断是否 web环境,如果是就是要使用 AnnotationConfigEmbeddedWebApplicationContext  这个Ctx类实例化Context。

关键(2)的源码

    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
    }

那么 AnnotationConfigEmbeddedWebApplicationContext  的 onRefresh 和普通的 context的区别在于:

    protected void onRefresh() {
        super.onRefresh();

        try {
            this.createEmbeddedServletContainer();  // 创建自带的web容器
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start embedded container", var2);
        }
    }

    private void createEmbeddedServletContainer() {
        EmbeddedServletContainer localContainer = this.embeddedServletContainer;
        ServletContext localServletContext = this.getServletContext();
        if(localContainer == null && localServletContext == null) {
            EmbeddedServletContainerFactory ex = this.getEmbeddedServletContainerFactory(); // 获取ContainerFactory
            this.embeddedServletContainer = ex.getEmbeddedServletContainer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if(localServletContext != null) {
            //...
        }

        this.initPropertySources();
    }

那么 ContainerFactory 肯定是自动引入的配置咯!

EmbeddedServletContainerAutoConfiguration  中:

@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class})
@ConditionalOnMissingBean(
    value = {EmbeddedServletContainerFactory.class},
    search = SearchStrategy.CURRENT
)
public static class EmbeddedTomcat {
    public EmbeddedTomcat() {
    }

    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
        return new TomcatEmbeddedServletContainerFactory();
    }
}

当然,该类中还有其他web容器的引入配置,形同上面 Tomcat的,逻辑也类似就是判断是否存在一些关键类:

@ConditionalOnClass({Servlet.class, Tomcat.class})

其他的,refresh 过程跟一般的spring context一致,不作分析。

总结

SpringBoot 远不止如此, 且学且记录吧!

 

© 著作权归作者所有

共有 人打赏支持
alexqdjay
粉丝 35
博文 26
码字总数 31560
作品 0
浦东
高级程序员
SpringBoot自定义starter

SpringBoot 提供了众多的starter简化我们开发,为了更好地使用这些starter,了解其原理是很重要的,今天就来自定义一个starter。 1,如何自定义 ①,我们首先要有一个意识,springboot自己适...

暗中观察 ⋅ 05/21 ⋅ 0

SpringBoot 2.0 系列006 --启动实战之注解使用

SpringBoot 2.0 系列006 --启动实战之注解使用 一些误区说明 网上很多教程默认使用SpringBootApplcation注解,且只用这个即可扫描启动类包下所有的bean。 而官方默认教程使用的是@EnableAuto...

路上有你0314 ⋅ 05/18 ⋅ 0

恒宇少年/spring-boot-chapter

简书整套文档以及源码解析 专题 专题名称 专题描述 001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件 002 Spring Cloud 核心技术 对Spring Cloud核心技术全面讲解 003 Quer...

恒宇少年 ⋅ 04/19 ⋅ 0

springboot的后台系统的初步搭建

springboot的后台系统的初步搭建 基于springboot的设备管理系统的架构搭建 使用工具:idea 数据库:mysql 相关技术:shiro mybatis activemq等 (1) 新建项目 new project ,idea有专门的sprin...

代金券优惠 ⋅ 04/15 ⋅ 0

SpringBoot 2.0 系列001 -- 入门介绍以及相关概念

SpringBoot 2.0 系列001 -- 入门介绍以及相关概念 什么是SpringBoot? 项目地址:http://projects.spring.io/spring-boot/ SpringBoot介绍 Spring Boot使开发独立的,产品级别的基于Spring的...

路上有你0314 ⋅ 05/10 ⋅ 0

第一章:Maven环境下如何配置QueryDSL环境

QueryDSL是一个通用的查询框架,框架的核心原则是创建安全类型的查询,开始QueryDSL仅支持Hibernate(HQL),在不断开源人士加入QueryDSL团队后,陆续发布了针对JPA,JDO,JDBC,Lucene,Hib...

恒宇少年 ⋅ 2017/07/01 ⋅ 0

Netty(一) SpringBoot 整合长连接心跳机制

前言 Netty 是一个高性能的 NIO 网络框架,本文基于 SpringBoot 以常见的心跳机制来认识 Netty。 最终能达到的效果: 客户端每隔 N 秒检测是否需要发送心跳。 服务端也每隔 N 秒检测是否需要...

crossoverJie ⋅ 05/28 ⋅ 0

Spring Boot干货系列: (三)启动原理解析

     前言   前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次博主就跟你们一起一步步...

后端编程嘟 ⋅ 2017/03/13 ⋅ 0

springboot入门--pom文件解读

spring-boot-starter-parent spring-boot-dependencies spring-boot-dependencies主要管理springboot应用依赖版本 导入的spring-boot-starter-web依赖 spring-boot-starter-web依赖导入(web......

sunny_92 ⋅ 04/03 ⋅ 0

springboot + shiro 权限注解、请求乱码解决、统一异常处理

springboot + shiro 权限注解、请求乱码解决、统一异常处理 前篇 后台权限管理系统 相关: spring boot + mybatis + layui + shiro后台权限管理系统 springboot + shiro之登录人数限制、登录...

wyait ⋅ 06/06 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

服务网关过滤器

过滤器作用 我们的微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了。但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都需要有一定的限制,系统并不会...

明理萝 ⋅ 22分钟前 ⋅ 1

【2018.06.21学习笔记】【linux高级知识 14.1-14.3】

14.1 NFS介绍 NFS服务全称是NetWork File System:网络文件系统,最早有sun公司开发的,4.0版本由Netapp公司开发,是基于RPC远程过程调用(Remote Procedure Call)协议的服务。 14.2 NFS服务...

lgsxp ⋅ 31分钟前 ⋅ 0

Day18 vim编辑模式、命令模式与练习

编辑模式 命令模式 :nohl 不高亮显示 :x与:wq类似,如果在更改文件之后操作,两者效果一样;如果打开文件,没有任何操作; :wq会更改mtime,但是:x不会。 练习题 扩展 vim的特殊用法 ht...

杉下 ⋅ 34分钟前 ⋅ 0

Enum、EnumMap、EnumSet

1、Enum 不带参数 public enum Car { AUDI { @Override public int getPrice() { return 25000; } }, MERCEDES { ......

职业搬砖20年 ⋅ 35分钟前 ⋅ 0

Java中的锁使用与实现

1.Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。 在Lock出现之前,java程序是靠synchronized关键字实现锁功能的,而Java SE5之后,...

ZH-JSON ⋅ 36分钟前 ⋅ 0

线程组和 ThreadLocal

前言 在上面文章中,我们从源码的角度上解析了一下线程池,并且从其 execute 方法开始把线程池中的相关执行流程过了一遍。那么接下来,我们来看一个新的关于线程的知识点:线程组。 线程组 ...

猴亮屏 ⋅ 37分钟前 ⋅ 0

相对路径和绝对路径

基本概念   文件路径就是文件在电脑中的位置,表示文件路径的方式有两种,相对路径和绝对路径。在网页设计中通过路径可以表示链接,插入图像、Flash、CSS文件的位置。   物理路径:物理路...

临江仙卜算子 ⋅ 41分钟前 ⋅ 0

消息队列属性及常见消息队列介绍

什么是消息队列? 消息队列是在消息的传输过程中保存消息的容器,用于接收消息并以文件的方式存储,一个队列的消息可以同时被多个消息消费者消费。分布式消息服务DMS则是分布式的队列系统,消...

中间件小哥 ⋅ 44分钟前 ⋅ 0

java程序员使用web3j进行以太坊开发详解

如何使用web3j为Java应用或Android App增加以太坊区块链支持,教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤器...

笔阁 ⋅ 44分钟前 ⋅ 0

vim编辑模式、vim命令模式

vim编辑模式 使用vim filename 进入的界面是一般模式,在这个模式下虽然我们能够查看,复制,剪切,粘贴,但是不能编辑新的内容,如何能直接写入东西呢?这就需要进入编辑模式了,从一般模式...

李超小牛子 ⋅ 47分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部