主頁 > 後端開發 > 就想搞明白,component-scan 是怎么把Bean都注冊到Spring容器的!

就想搞明白,component-scan 是怎么把Bean都注冊到Spring容器的!

2021-07-31 08:03:15 後端開發


作者:小傅哥
博客:https://bugstack.cn

沉淀、分享、成長,讓自己和他人都能有所識訓!😄

一、前言

忒復雜,沒等搞明白大促都過去了!

你經歷過618雙11嗎?你加入過大促時候那么多復雜的營銷活動賺幾毛錢嗎?你開發過連讀明白玩法都需要一周但只使用3天的大促需求嗎?有時候對于有些產品的需求真的是太復雜了,復雜到開發、測驗都需要在整個程序中不斷的學習最后才可能讀懂產品為啥這樣的玩,要是一個長期的活動可能也就算了,培養用戶心智嗎!但這一整套拉新、助力、激活、下單、投保、領券、消費、開紅包等等一連串的騷操作下來,如果在線上只用3天呢,或者是只用1天,那TM連參與的用戶都沒弄明白呢,活動就結束了,最后能打來什么樣好的資料呢?對于這樣流程復雜,估計連羊毛當都看不上!!!

以上只是舉個例子,大部分時候并不會搞的這么惡心,評審也是過不去的!而同樣的道理用在程式設計開發和使用中也是一樣的,如果你把你的代碼邏輯實作的過于分散,讓外部呼叫方在使用的時候,需要呼叫你的介面多個和多次,還沒有訊息觸達,只能定時自己輪訓你的介面查看訂單狀態,每次還只能查10條,查多了你說不行,等等反人類的設計,都會給呼叫方帶來要干你的體會,

所以,如果我們能在完成目的的情況下,都是希望盡可能流程簡單、模式清晰、自動服務,那這在Spring的框架中也是有所體現的,這個框架的普及使用程度和它所能帶來的方便性是分不開的,而我們如果能做到如此的方便,那肯定是一種好的設計和實作,

二、目標

其實到本章節我們已經把關于 IOC 和 AOP 全部核心內容都已經實作完成了,只不過在使用上還有點像早期的 Spring 版本,需要一個一個在 spring.xml 中進行配置,這與實際的目前使用的 Spring 框架還是有蠻大的差別,而這種差別其實都是在核心功能邏輯之上建設的在更少的配置下,做到更簡化的使用,

這其中就包括:包的掃描注冊、注解配置的使用、占位符屬性的填充等等,而我們的目標就是在目前的核心邏輯上填充一些自動化的功能,讓大家可以學習到這部分的設計和實作,從中體會到一些關于代碼邏輯的實作程序,總結一些編碼經驗,

三、方案

首先我們要考慮🤔,為了可以簡化 Bean 物件的配置,讓整個 Bean 物件的注冊都是自動掃描的,那么基本需要的元素包括:掃描路徑入口、XML決議掃描資訊、給需要掃描的Bean物件做注解標記、掃描Class物件摘取Bean注冊的基本資訊,組裝注冊資訊、注冊成Bean物件,那么在這些條件元素的支撐下,就可以實作出通過自定義注解和配置掃描路徑的情況下,完成 Bean 物件的注冊,除此之外再順帶解決一個配置中占位符屬性的知識點,比如可以通過 ${token} 給 Bean 物件注入進去屬性資訊,那么這個操作需要用到 BeanFactoryPostProcessor,因為它可以處理 在所有的 BeanDefinition 加載完成后,實體化 Bean 物件之前,提供修改 BeanDefinition 屬性的機制 而實作這部分內容是為了后續把此類內容結合到自動化配置處理中,整體設計結構如下圖:

結合bean的生命周期,包掃描只不過是掃描特定注解的類,提取類的相關資訊組裝成BeanDefinition注冊到容器中,

在XmlBeanDefinitionReader中決議<context:component-scan />標簽,掃描類組裝BeanDefinition然后注冊到容器中的操作在ClassPathBeanDefinitionScanner#doScan中實作,

  • 自動掃描注冊主要是掃描添加了自定義注解的類,在xml加載程序中提取類的資訊,組裝 BeanDefinition 注冊到 Spring 容器中,
  • 所以我們會用到 <context:component-scan /> 配置包路徑并在 XmlBeanDefinitionReader 決議并做相應的處理,這里的處理會包括對類的掃描、獲取注解資訊等
  • 最后還包括了一部分關于 BeanFactoryPostProcessor 的使用,因為我們需要完成對占位符配置資訊的加載,所以需要使用到 BeanFactoryPostProcessor 在所有的 BeanDefinition 加載完成后,實體化 Bean 物件之前,修改 BeanDefinition 的屬性資訊,這一部分的實作也為后續處理關于占位符配置到注解上做準備

四、實作

1. 工程結構

small-spring-step-13
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework
    │           ├── aop
    │           │   ├── aspectj
    │           │   │   └── AspectJExpressionPointcut.java
    │           │   │   └── AspectJExpressionPointcutAdvisor.java
    │           │   ├── framework 
    │           │   │   ├── adapter
    │           │   │   │   └── MethodBeforeAdviceInterceptor.java
    │           │   │   ├── autoproxy
    │           │   │   │   └── MethodBeforeAdviceInterceptor.java
    │           │   │   ├── AopProxy.java
    │           │   │   ├── Cglib2AopProxy.java
    │           │   │   ├── JdkDynamicAopProxy.java
    │           │   │   ├── ProxyFactory.java
    │           │   │   └── ReflectiveMethodInvocation.java
    │           │   ├── AdvisedSupport.java
    │           │   ├── Advisor.java
    │           │   ├── BeforeAdvice.java
    │           │   ├── ClassFilter.java
    │           │   ├── MethodBeforeAdvice.java
    │           │   ├── MethodMatcher.java
    │           │   ├── Pointcut.java
    │           │   ├── PointcutAdvisor.java
    │           │   └── TargetSource.java
    │           ├── beans
    │           │   ├── factory
    │           │   │   ├── config
    │           │   │   │   ├── AutowireCapableBeanFactory.java
    │           │   │   │   ├── BeanDefinition.java
    │           │   │   │   ├── BeanFactoryPostProcessor.java
    │           │   │   │   ├── BeanPostProcessor.java
    │           │   │   │   ├── BeanReference.java
    │           │   │   │   ├── ConfigurableBeanFactory.java
    │           │   │   │   ├── InstantiationAwareBeanPostProcessor.java
    │           │   │   │   └── SingletonBeanRegistry.java
    │           │   │   ├── support
    │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   │   ├── AbstractBeanDefinitionReader.java
    │           │   │   │   ├── AbstractBeanFactory.java
    │           │   │   │   ├── BeanDefinitionReader.java
    │           │   │   │   ├── BeanDefinitionRegistry.java
    │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   │   ├── DefaultListableBeanFactory.java
    │           │   │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── FactoryBeanRegistrySupport.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── FactoryBean.java
    │           │   │   ├── HierarchicalBeanFactory.java
    │           │   │   ├── InitializingBean.java
    │           │   │   ├── ListableBeanFactory.java
    │           │   │   └── PropertyPlaceholderConfigurer.java
    │           │   ├── BeansException.java
    │           │   ├── PropertyValue.java
    │           │   └── PropertyValues.java 
    │           ├── context
    │           │   ├── annotation
    │           │   │   ├── ClassPathBeanDefinitionScanner.java 
    │           │   │   ├── ClassPathScanningCandidateComponentProvider.java 
    │           │   │   └── Scope.java 
    │           │   ├── event
    │           │   │   ├── AbstractApplicationEventMulticaster.java 
    │           │   │   ├── ApplicationContextEvent.java 
    │           │   │   ├── ApplicationEventMulticaster.java 
    │           │   │   ├── ContextClosedEvent.java 
    │           │   │   ├── ContextRefreshedEvent.java 
    │           │   │   └── SimpleApplicationEventMulticaster.java 
    │           │   ├── support
    │           │   │   ├── AbstractApplicationContext.java 
    │           │   │   ├── AbstractRefreshableApplicationContext.java 
    │           │   │   ├── AbstractXmlApplicationContext.java 
    │           │   │   ├── ApplicationContextAwareProcessor.java 
    │           │   │   └── ClassPathXmlApplicationContext.java 
    │           │   ├── ApplicationContext.java 
    │           │   ├── ApplicationContextAware.java 
    │           │   ├── ApplicationEvent.java 
    │           │   ├── ApplicationEventPublisher.java 
    │           │   ├── ApplicationListener.java 
    │           │   └── ConfigurableApplicationContext.java
    │           ├── core.io
    │           │   ├── ClassPathResource.java 
    │           │   ├── DefaultResourceLoader.java 
    │           │   ├── FileSystemResource.java 
    │           │   ├── Resource.java 
    │           │   ├── ResourceLoader.java
    │           │   └── UrlResource.java
    │           ├── stereotype
    │           │   └── Component.java
    │           └── utils
    │               └── ClassUtils.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   ├── IUserService.java
                │   └── UserService.java
                └── ApiTest.java

工程原始碼公眾號「bugstack蟲洞堆疊」,回復:Spring 專欄,獲取完整原始碼

在Bean的生命周期中自動加載包掃描注冊Bean物件和設定占位符屬性的類關系,如圖 14-2

圖 14-2

  • 整個類的關系結構來看,其實涉及的內容并不多,主要包括的就是 xml 決議類 XmlBeanDefinitionReader 對 ClassPathBeanDefinitionScanner#doScan 的使用,
  • 在 doScan 方法中處理所有指定路徑下添加了注解的類,拆解出類的資訊:名稱、作用范圍等,進行創建 BeanDefinition 好用于 Bean 物件的注冊操作,
  • PropertyPlaceholderConfigurer 目前看上去像一塊單獨的內容,后續會把這塊的內容與自動加載 Bean 物件進行整合,也就是可以在注解上使用占位符配置一些在組態檔里的屬性資訊,

2. 處理占位符配置

cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer

public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {

    /**
     * Default placeholder prefix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    /**
     * Default placeholder suffix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    private String location;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 加載屬性檔案
        try {
            DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource(location);
            Properties properties = new Properties();
            properties.load(resource.getInputStream());

            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
            for (String beanName : beanDefinitionNames) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

                PropertyValues propertyValues = beanDefinition.getPropertyValues();
                for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                    Object value = propertyValue.getValue();
                    if (!(value instanceof String)) continue;
                    String strVal = (String) value;
                    StringBuilder buffer = new StringBuilder(strVal);
                    int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
                    int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
                    if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
                        String propKey = strVal.substring(startIdx + 2, stopIdx);
                        String propVal = properties.getProperty(propKey);
                        buffer.replace(startIdx, stopIdx + 1, propVal);
                        propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString()));
                    }
                }
            }
        } catch (IOException e) {
            throw new BeansException("Could not load properties", e);
        }
    }

    public void setLocation(String location) {
        this.location = location;
    }

}
  • 依賴于 BeanFactoryPostProcessor 在 Bean 生命周期的屬性,可以在 Bean 物件實體化之前,改變屬性資訊,所以這里通過實作 BeanFactoryPostProcessor 介面,完成對組態檔的加載以及摘取占位符中的在屬性檔案里的配置,
  • 這樣就可以把提取到的配置資訊放置到屬性配置中了,buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue

3. 定義攔截注解

cn.bugstack.springframework.context.annotation.Scope

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {

    String value() default "singleton";

}
  • 用于配置作用域的自定義注解,方便通過配置Bean物件注解的時候,拿到Bean物件的作用域,不過一般都使用默認的 singleton

cn.bugstack.springframework.stereotype.Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {

    String value() default "";

}
  • Component 自定義注解大家都非常熟悉了,用于配置到 Class 類上的,除此之外還有 Service、Controller,不過所有的處理方式基本一致,這里就只展示一個 Component 即可,

4. 處理物件掃描裝配

cn.bugstack.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

public class ClassPathScanningCandidateComponentProvider {

    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
        for (Class<?> clazz : classes) {
            candidates.add(new BeanDefinition(clazz));
        }
        return candidates;
    }

}
  • 這里先要提供一個可以通過配置路徑 basePackage=cn.bugstack.springframework.test.bean,決議出 classes 資訊的工具方法 findCandidateComponents,通過這個方法就可以掃描到所有 @Component 注解的 Bean 物件了,

cn.bugstack.springframework.context.annotation.ClassPathBeanDefinitionScanner

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    private BeanDefinitionRegistry registry;

    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public void doScan(String... basePackages) {
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition beanDefinition : candidates) {
                // 決議 Bean 的作用域 singleton、prototype
                String beanScope = resolveBeanScope(beanDefinition);
                if (StrUtil.isNotEmpty(beanScope)) {
                    beanDefinition.setScope(beanScope);
                }
                registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
            }
        }
    }

    private String resolveBeanScope(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Scope scope = beanClass.getAnnotation(Scope.class);
        if (null != scope) return scope.value();
        return StrUtil.EMPTY;
    }

    private String determineBeanName(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Component component = beanClass.getAnnotation(Component.class);
        String value = component.value();
        if (StrUtil.isEmpty(value)) {
            value = StrUtil.lowerFirst(beanClass.getSimpleName());
        }
        return value;
    }

}
  • ClassPathBeanDefinitionScanner 是繼承自 ClassPathScanningCandidateComponentProvider 的具體掃描包處理的類,在 doScan 中除了獲取到掃描的類資訊以后,還需要獲取 Bean 的作用域和類名,如果不配置類名基本都是把首字母縮寫,

5. 決議xml中呼叫掃描

cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();

        // 決議 context:component-scan 標簽,掃描包中的類并提取相關資訊,用于組裝 BeanDefinition
        Element componentScan = root.element("component-scan");
        if (null != componentScan) {
            String scanPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(scanPath);
        }
       
        // ... 省略其他
            
        // 注冊 BeanDefinition
        getRegistry().registerBeanDefinition(beanName, beanDefinition);
    }

    private void scanPackage(String scanPath) {
        String[] basePackages = StrUtil.splitToArray(scanPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.doScan(basePackages);
    }

}
  • 關于 XmlBeanDefinitionReader 中主要是在加載組態檔后,處理新增的自定義配置屬性 component-scan,決議后呼叫 scanPackage 方法,其實也就是我們在 ClassPathBeanDefinitionScanner#doScan 功能,
  • 另外這里需要注意,為了可以方便的加載和決議xml,XmlBeanDefinitionReader 已經全部替換為 dom4j 的方式進行決議處理,

五、測驗

1. 事先準備

@Component("userService")
public class UserService implements IUserService {

    private String token;

    public String queryUserInfo() {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "小傅哥,100001,深圳";
    }

    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "注冊用戶:" + userName + " success!";
    }

    @Override
    public String toString() {
        return "UserService#token = { " + token + " }";
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}
  • 給 UserService 類添加一個自定義注解 @Component("userService") 和一個屬性資訊 String token,這是為了分別測驗包掃描和占位符屬性,

2. 屬性組態檔

token=RejDlI78hu223Opo983Ds
  • 這里配置一個 token 的屬性資訊,用于通過占位符的方式進行獲取

3. spring.xml 配置物件

spring-property.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd
		 http://www.springframework.org/schema/context">

    <bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:token.properties"/>
    </bean>

    <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
        <property name="token" value="${token}"/>
    </bean>

</beans>
  • 加載 classpath:token.properties 設定占位符屬性值 ${token}

spring-scan.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd
		 http://www.springframework.org/schema/context">

    <context:component-scan base-package="cn.bugstack.springframework.test.bean"/>

</beans>
  • 添加 component-scan 屬性,設定包掃描根路徑

4. 單元測驗(占位符)

@Test
public void test_property() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml");
    IUserService userService = applicationContext.getBean("userService", IUserService.class);
    System.out.println("測驗結果:" + userService);
}

測驗結果

測驗結果:UserService#token = { RejDlI78hu223Opo983Ds }

Process finished with exit code 0
  • 通過測驗結果可以看到 UserService 中的 token 屬性已經通過占位符的方式設定進去組態檔里的 token.properties 的屬性值了,

5. 單元測驗(包掃描)

@Test
public void test_scan() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");
    IUserService userService = applicationContext.getBean("userService", IUserService.class);
    System.out.println("測驗結果:" + userService.queryUserInfo());
}

測驗結果

測驗結果:小傅哥,100001,深圳

Process finished with exit code 0
  • 通過這個測驗結果可以看出來,現在使用注解的方式就可以讓 Class 注冊完成 Bean 物件了,

六、總結

  • 通過整篇的內容實作可以看出來,目前的功能添加其實已經不復雜了,都是在 IOC 和 AOP 核心的基礎上來補全功能,這些補全的功能也是在完善 Bean 的生命周期,讓整個功能使用也越來越容易,
  • 在你不斷的實作著 Spring 的各項功能時,也可以把自己在平常使用 Spring 的一些功能想法融入進來,比如像 Spring 是如何動態切換資料源的,執行緒池是怎么提供配置的,這些內容雖然不是最基礎的核心范圍,但也非常重要,
  • 可能有些時候這些類實作的內容對新人來說比較多,可以一點點動手實作逐步理解,在把一些稍微較有難度的內容實作后,其實后面也就沒有那么難理解了,

七、系列推薦

  • 13年畢業,用兩年時間從外包走進互聯網大廠
  • 作業兩三年了,整不明白架構圖都畫啥?
  • 面試現場:小伙伴美團一面的分享和分析(含解答)
  • LinkedList插入速度比ArrayList快?你確定嗎?
  • Netty+JavaFx實戰:仿桌面版微信聊天

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/291029.html

標籤:java

上一篇:Windows命令列簡易入門

下一篇:容器型資料型別——元組

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more