主頁 > 後端開發 > 這可能是最全面的Spring面試題總結了

這可能是最全面的Spring面試題總結了

2023-04-02 07:48:04 後端開發

Spring是什么?

Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器框架,

Spring的優點

  • 通過控制反轉和依賴注入實作松耦合
  • 支持面向切面的編程,并且把應用業務邏輯和系統服務分開,
  • 通過切面和模板減少樣板式代碼,
  • 宣告式事務的支持,可以從單調繁冗的事務管理代碼中解脫出來,通過宣告式方式靈活地進行事務的管理,提高開發效率和質量,
  • 方便集成各種優秀框架,內部提供了對各種優秀框架的直接支持(如:Hessian、Quartz、MyBatis等),
  • 方便程式的測驗,Spring支持Junit4,添加注解便可以測驗Spring程式,

Spring 用到了哪些設計模式?

1、簡單工廠模式BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一標識來獲得 Bean 物件,

@Override
public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

2、工廠方法模式FactoryBean就是典型的工廠方法模式,spring在使用getBean()呼叫獲得該bean時,會自動呼叫該bean的getObject()方法,每個 Bean 都會對應一個 FactoryBean,如 SqlSessionFactory 對應 SqlSessionFactoryBean

3、單例模式:一個類僅有一個實體,提供一個訪問它的全域訪問點,Spring 創建 Bean 實體默認是單例的,

4、配接器模式:SpringMVC中的配接器HandlerAdatper,由于應用會有多個Controller實作,如果需要直接呼叫Controller方法,那么需要先判斷是由哪一個Controller處理請求,然后呼叫相應的方法,當增加新的 Controller,需要修改原來的邏輯,違反了開閉原則(對修改關閉,對擴展開放),

為此,Spring提供了一個配接器介面,每一種 Controller 對應一種 HandlerAdapter 實作類,當請求過來,SpringMVC會呼叫getHandler()獲取相應的Controller,然后獲取該Controller對應的 HandlerAdapter,最后呼叫HandlerAdapterhandle()方法處理請求,實際上呼叫的是Controller的handleRequest(),每次添加新的 Controller 時,只需要增加一個配接器類就可以,無需修改原有的邏輯,

常用的處理器配接器:SimpleControllerHandlerAdapterHttpRequestHandlerAdapterAnnotationMethodHandlerAdapter

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

public class HttpRequestHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {//handler是被適配的物件,這里使用的是物件的配接器模式
        return (handler instanceof HttpRequestHandler);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }
}

5、代理模式:spring 的 aop 使用了動態代理,有兩種方式JdkDynamicAopProxyCglib2AopProxy

6、觀察者模式:spring 中 observer 模式常用的地方是 listener 的實作,如ApplicationListener

7、模板模式: Spring 中 jdbcTemplatehibernateTemplate 等,就使用到了模板模式,

本文已經收錄到Github倉庫,該倉庫包含計算機基礎、Java基礎、多執行緒、JVM、資料庫、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服務、設計模式、架構、校招社招分享等核心知識點,歡迎star~

Github地址

如果訪問不了Github,可以訪問gitee地址,

gitee地址

什么是AOP?

面向切面編程,作為面向物件的一種補充,將公共邏輯(事務管理、日志、快取等)封裝成切面,跟業務代碼進行分離,可以減少系統的重復代碼和降低模塊之間的耦合度,切面就是那些與業務無關,但所有業務模塊都會呼叫的公共邏輯,

AOP有哪些實作方式?

AOP有兩種實作方式:靜態代理和動態代理,

靜態代理

靜態代理:代理類在編譯階段生成,在編譯階段將通知織入Java位元組碼中,也稱編譯時增強,AspectJ使用的是靜態代理,

缺點:代理物件需要與目標物件實作一樣的介面,并且實作介面的方法,會有冗余代碼,同時,一旦介面增加方法,目標物件與代理物件都要維護,

動態代理

動態代理:代理類在程式運行時創建,AOP框架不會去修改位元組碼,而是在記憶體中臨時生成一個代理物件,在運行期間對業務方法進行增強,不會生成新類,

Spring AOP的實作原理

SpringAOP實作原理其實很簡單,就是通過動態代理實作的,如果我們為Spring的某個bean配置了切面,那么Spring在創建這個bean的時候,實際上創建的是這個bean的一個代理物件,我們后續對bean中方法的呼叫,實際上呼叫的是代理類重寫的代理方法,而SpringAOP使用了兩種動態代理,分別是JDK的動態代理,以及CGLib的動態代理

JDK動態代理和CGLIB動態代理的區別?

Spring AOP中的動態代理主要有兩種方式:JDK動態代理和CGLIB動態代理,

JDK動態代理

如果目標類實作了介面,Spring AOP會選擇使用JDK動態代理目標類,代理類根據目標類實作的介面動態生成,不需要自己撰寫,生成的動態代理類和目標類都實作相同的介面,JDK動態代理的核心是InvocationHandler介面和Proxy類,

缺點:目標類必須有實作的介面,如果某個類沒有實作介面,那么這個類就不能用JDK動態代理,

最全面的Java面試網站

CGLIB動態代理

通過繼承實作,如果目標類沒有實作介面,那么Spring AOP會選擇使用CGLIB來動態代理目標類,CGLIB(Code Generation Library)可以在運行時動態生成類的位元組碼,動態創建目標類的子類物件,在子類物件中增強目標類,

CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態代理的,

優點:目標類不需要實作特定的介面,更加靈活,

什么時候采用哪種動態代理?

  1. 如果目標物件實作了介面,默認情況下會采用JDK的動態代理實作AOP
  2. 如果目標物件實作了介面,可以強制使用CGLIB實作AOP
  3. 如果目標物件沒有實作了介面,必須采用CGLIB庫

兩者的區別

  1. jdk動態代理使用jdk中的類Proxy來創建代理物件,它使用反射技術來實作,不需要匯入其他依賴,cglib需要引入相關依賴:asm.jar,它使用位元組碼增強技術來實作,
  2. 當目標類實作了介面的時候Spring Aop默認使用jdk動態代理方式來增強方法,沒有實作介面的時候使用cglib動態代理方式增強方法,

Spring AOP相關術語

(1)切面(Aspect):切面是通知和切點的結合,通知和切點共同定義了切面的全部內容,

(2)連接點(Join point):指方法,在Spring AOP中,一個連接點總是代表一個方法的執行,連接點是在應用執行程序中能夠插入切面的一個點,這個點可以是呼叫方法時、拋出例外時、甚至修改一個欄位時,切面代碼可以利用這些點插入到應用的正常流程之中,并添加新的行為,

(3)通知(Advice):在AOP術語中,切面的作業被稱為通知,

(4)切入點(Pointcut):切點的定義會匹配通知所要織入的一個或多個連接點,我們通常使用明確的類和方法名稱,或是利用正則運算式定義所匹配的類和方法名稱來指定這些切點,

(5)引入(Introduction):引入允許我們向現有類添加新方法或屬性,

(6)目標物件(Target Object): 被一個或者多個切面(aspect)所通知(advise)的物件,它通常是一個代理物件,

(7)織入(Weaving):織入是把切面應用到目標物件并創建新的代理物件的程序,在目標物件的生命周期里有以下時間點可以進行織入:

  • 編譯期:切面在目標類編譯時被織入,AspectJ的織入編譯器是以這種方式織入切面的,
  • 類加載期:切面在目標類加載到JVM時被織入,需要特殊的類加載器,它可以在目標類被引入應用之前增強該目標類的位元組碼,AspectJ5的加載時織入就支持以這種方式織入切面,
  • 運行期:切面在應用運行的某個時刻被織入,一般情況下,在織入切面時,AOP容器會為目標物件動態地創建一個代理物件,SpringAOP就是以這種方式織入切面,

Spring通知有哪些型別?

在AOP術語中,切面的作業被稱為通知,通知實際上是程式運行時要通過Spring AOP框架來觸發的代碼段,

Spring切面可以應用5種型別的通知:

  1. 前置通知(Before):在目標方法被呼叫之前呼叫通知功能;
  2. 后置通知(After):在目標方法完成之后呼叫通知,此時不會關心方法的輸出是什么;
  3. 回傳通知(After-returning ):在目標方法成功執行之后呼叫通知;
  4. 例外通知(After-throwing):在目標方法拋出例外后呼叫通知;
  5. 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之后執行自定義的邏輯,

什么是依賴注入?

在Spring創建物件的程序中,把物件依賴的屬性注入到物件中,依賴注入主要有兩種方式:構造器注入和屬性注入,

什么是IOC?

IOC:控制反轉,由Spring容器管理bean的整個生命周期,通過反射實作對其他物件的控制,包括初始化、創建、銷毀等,解放手動創建物件的程序,同時降低類之間的耦合度,

IOC的好處?

ioc的思想最核心的地方在于,資源不由使用資源者管理,而由不使用資源的第三方管理,這可以帶來很多好處,第一,資源集中管理,實作資源的可配置和易管理,第二,降低了使用資源雙方的依賴程度,也就是我們說的耦合度,

也就是說,甲方要達成某種目的不需要直接依賴乙方,它只需要達到的目的告訴第三方機構就可以了,比如甲方需要一雙襪子,而乙方它賣一雙襪子,它要把襪子賣出去,并不需要自己去直接找到一個賣家來完成襪子的賣出,它也只需要找第三方,告訴別人我要賣一雙襪子,這下好了,甲乙雙方進行交易活動,都不需要自己直接去找賣家,相當于程式內部開放介面,賣家由第三方作為引數傳入,甲乙互相不依賴,而且只有在進行交易活動的時候,甲才和乙產生聯系,反之亦然,這樣做什么好處么呢,甲乙可以在對方不真實存在的情況下獨立存在,而且保證不交易時候無聯系,想交易的時候可以很容易的產生聯系,甲乙交易活動不需要雙方見面,避免了雙方的互不信任造成交易失敗的問題,因為交易由第三方來負責聯系,而且甲乙都認為第三方可靠,那么交易就能很可靠很靈活的產生和進行了,

這就是ioc的核心思想,生活中這種例子比比皆是,支付寶在整個淘寶體系里就是龐大的ioc容器,交易雙方之外的第三方,提供可靠性可依賴可靈活變更交易方的資源管理中心,另外人事代理也是,雇傭機構和個人之外的第三方,

參考鏈接:https://www.zhihu.com/question/23277575/answer/24259844

IOC容器初始化程序?

  1. 從XML中讀取組態檔,
  2. 將bean標簽決議成 BeanDefinition,如決議 property 元素, 并注入到 BeanDefinition 實體中,
  3. 將 BeanDefinition 注冊到容器 BeanDefinitionMap 中,
  4. BeanFactory 根據 BeanDefinition 的定義資訊創建實體化和初始化 bean,

單例bean的初始化以及依賴注入一般都在容器初始化階段進行,只有懶加載(lazy-init為true)的單例bean是在應用第一次呼叫getBean()時進行初始化和依賴注入,

// AbstractApplicationContext
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

多例bean 在容器啟動時不實體化,即使設定 lazy-init 為 false 也沒用,只有呼叫了getBean()才進行實體化,

loadBeanDefinitions采用了模板模式,具體加載 BeanDefinition 的邏輯由各個子類完成,

Bean的生命周期

1.呼叫bean的構造方法創建Bean

2.通過反射呼叫setter方法進行屬性的依賴注入

3.如果Bean實作了BeanNameAware介面,Spring將呼叫setBeanName(),設定 Bean的name(xml檔案中bean標簽的id)

4.如果Bean實作了BeanFactoryAware介面,Spring將呼叫setBeanFactory()把bean factory設定給Bean

5.如果存在BeanPostProcessor,Spring將呼叫它們的postProcessBeforeInitialization(預初始化)方法,在Bean初始化前對其進行處理

6.如果Bean實作了InitializingBean介面,Spring將呼叫它的afterPropertiesSet方法,然后呼叫xml定義的 init-method 方法,兩個方法作用類似,都是在初始化 bean 的時候執行

7.如果存在BeanPostProcessor,Spring將呼叫它們的postProcessAfterInitialization(后初始化)方法,在Bean初始化后對其進行處理

8.Bean初始化完成,供應用使用,這里分兩種情況:

8.1 如果Bean為單例的話,那么容器會回傳Bean給用戶,并存入快取池,如果Bean實作了DisposableBean介面,Spring將呼叫它的destory方法,然后呼叫在xml中定義的 destory-method方法,這兩個方法作用類似,都是在Bean實體銷毀前執行,

8.2 如果Bean是多例的話,容器將Bean回傳給用戶,剩下的生命周期由用戶控制,

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}
public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

BeanFactory和FactoryBean的區別?

BeanFactory:管理Bean的容器,Spring中生成的Bean都是由這個介面的實作來管理的,

FactoryBean:通常是用來創建比較復雜的bean,一般的bean 直接用xml配置即可,但如果一個bean的創建程序中涉及到很多其他的bean 和復雜的邏輯,直接用xml配置比較麻煩,這時可以考慮用FactoryBean,可以隱藏實體化復雜Bean的細節,

當組態檔中bean標簽的class屬性配置的實作類是FactoryBean時,通過 getBean()方法回傳的不是FactoryBean本身,而是呼叫FactoryBean#getObject()方法所回傳的物件,相當于FactoryBean#getObject()代理了getBean()方法,如果想得到FactoryBean必須使用 '&' + beanName 的方式獲取,

Mybatis 提供了 SqlSessionFactoryBean,可以簡化 SqlSessionFactory的配置:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
	//復雜邏輯
  }
    
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }
}

在 xml 配置 SqlSessionFactoryBean:

<bean id="tradeSqlSessionFactory" >
    <property name="dataSource" ref="trade" />
    <property name="mapperLocations" value="https://www.cnblogs.com/tyson03/archive/2023/04/01/classpath*:mapper/trade/*Mapper.xml" />
    <property name="configLocation" value="https://www.cnblogs.com/tyson03/archive/2023/04/01/classpath:mybatis-config.xml" />
    <property name="typeAliasesPackage" value="https://www.cnblogs.com/tyson03/archive/2023/04/01/com.bytebeats.mybatis3.domain.trade" />
</bean>

Spring 將會在應用啟動時創建 SqlSessionFactory,并使用 sqlSessionFactory 這個名字存盤起來,

BeanFactory和ApplicationContext有什么區別?

BeanFactory和ApplicationContext是Spring的兩大核心介面,都可以當做Spring的容器,其中ApplicationContext是BeanFactory的子介面,

兩者區別如下:

1、功能上的區別,BeanFactory是Spring里面最底層的介面,包含了各種Bean的定義,讀取bean配置檔案,管理bean的加載、實體化,控制bean的生命周期,維護bean之間的依賴關系,

ApplicationContext介面作為BeanFactory的派生,除了提供BeanFactory所具有的功能外,還提供了更完整的框架功能,如繼承MessageSource、支持國際化、統一的資源檔案訪問方式、同時加載多個組態檔等功能,

2、加載方式的區別,BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(呼叫getBean()),才對該Bean進行加載實體化,這樣,我們就不能發現一些存在的Spring的配置問題,如果Bean的某一個屬性沒有注入,BeanFacotry加載后,直至第一次使用呼叫getBean方法才會拋出例外,

而ApplicationContext是在容器啟動時,一次性創建了所有的Bean,這樣,在容器啟動時,我們就可以發現Spring中存在的配置錯誤,這樣有利于檢查所依賴屬性是否注入, ApplicationContext啟動后預載入所有的單例Bean,那么在需要的時候,不需要等待創建bean,因為它們已經創建好了,

相對于基本的BeanFactory,ApplicationContext 唯一的不足是占用記憶體空間,當應用程式配置Bean較多時,程式啟動較慢,

3、創建方式的區別,BeanFactory通常以編程的方式被創建,ApplicationContext還能以宣告的方式創建,如使用ContextLoader,

4、注冊方式的區別,BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動注冊,而ApplicationContext則是自動注冊,

Bean注入容器有哪些方式?

1、@Configuration + @Bean

@Configuration用來宣告一個配置類,然后使用 @Bean 注解,用于宣告一個bean,將其加入到Spring容器中,

@Configuration
public class MyConfiguration {
    @Bean
    public Person person() {
        Person person = new Person();
        person.setName("大彬");
        return person;
    }
}

2、通過包掃描特定注解的方式

@ComponentScan放置在我們的配置類上,然后可以指定一個路徑,進行掃描帶有特定注解的bean,然后加至容器中,

特定注解包括@Controller、@Service、@Repository、@Component

@Component
public class Person {
    //...
}
 
@ComponentScan(basePackages = "com.dabin.test.*")
public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

3、@Import注解匯入

@Import注解平時開發用的不多,但是也是非常重要的,在進行Spring擴展時經常會用到,它經常搭配自定義注解進行使用,然后往容器中匯入一個組態檔,

@ComponentScan
/*把用到的資源匯入到當前容器中*/
@Import({Person.class})
public class App {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBean(Person.class));
        context.close();
    }
}

4、實作BeanDefinitionRegistryPostProcessor進行后置處理,

在Spring容器啟動的時候會執行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,就是等beanDefinition加載完畢之后,對beanDefinition進行后置處理,可以在此進行調整IOC容器中的beanDefinition,從而干擾到后面進行初始化bean,

在下面的代碼中,我們手動向beanDefinitionRegistry中注冊了person的BeanDefinition,最終成功將person加入到applicationContext中,

public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
        applicationContext.refresh();
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        registry.registerBeanDefinition("person", beanDefinition);
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

5、使用FactoryBean介面

如下圖代碼,使用@Configuration + @Bean的方式將 PersonFactoryBean 加入到容器中,這里沒有向容器中直接注入 Person,而是注入 PersonFactoryBean,然后從容器中拿Person這個型別的bean,

@Configuration
public class Demo1 {
    @Bean
    public PersonFactoryBean personFactoryBean() {
        return new PersonFactoryBean();
    }
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

最全面的Java面試網站

Bean的作用域

1、singleton:單例,Spring中的bean默認都是單例的,

2、prototype:每次請求都會創建一個新的bean實體,

3、request:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效,

4、session:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP session內有效,

5、global-session:全域session作用域,

Spring自動裝配的方式有哪些?

Spring的自動裝配有三種模式:byType(根據型別),byName(根據名稱)、constructor(根據建構式),

byType

找到與依賴型別相同的bean注入到另外的bean中,這個程序需要借助setter注入來完成,因此必須存在set方法,否則注入失敗,

當xml檔案中存在多個相同型別名稱不同的實體Bean時,Spring容器依賴注入仍然會失敗,因為存在多種適合的選項,Spring容器無法知道該注入那種,此時我們需要為Spring容器提供幫助,指定注入那個Bean實體,可以通過<bean>標簽的autowire-candidate設定為false來過濾那些不需要注入的實體Bean

<bean id="userDao"   />

<!-- autowire-candidate="false" 過濾該型別 -->
<bean id="userDao2" autowire-candidate="false"  />

<!-- byType 根據型別自動裝配userDao-->
<bean id="userService" autowire="byType"  />

byName

將屬性名與bean名稱進行匹配,如果找到則注入依賴bean,

<bean id="userDao"   />
<bean id="userDao2"  />

<!-- byName 根據名稱自動裝配,找到UserServiceImpl名為 userDao屬性并注入-->
<bean id="userService" autowire="byName"  />

constructor

存在單個實體則優先按型別進行引數匹配(無論名稱是否匹配),當存在多個型別相同實體時,按名稱優先匹配,如果沒有找到對應名稱,則注入失敗,

@Autowired和@Resource的區別?

Autowire是spring的注解,默認情況下@Autowired是按型別匹配的(byType),如果需要按名稱(byName)匹配的話,可以使用@Qualifier注解與@Autowired結合,@Autowired 可以傳遞一個required=false的屬性,false指明當userDao實體存在就注入不存就忽略,如果為true,就必須注入,若userDao實體不存在,就拋出例外,

public class UserServiceImpl implements UserService {
    //標注成員變數
    @Autowired
    @Qualifier("userDao1")
    private UserDao userDao;   
 }

Resource是j2ee的注解,默認按 byName模式自動注入,@Resource有兩個中重要的屬性:name和type,name屬性指定bean的名字,type屬性則指定bean的型別,因此使用name屬性,則按byName模式的自動注入策略,如果使用type屬性,則按 byType模式自動注入策略,倘若既不指定name也不指定type屬性,Spring容器將通過反射技術默認按byName模式注入,

@Resource(name="userDao")
private UserDao  userDao;//用于成員變數

//也可以用于set方法標注
@Resource(name="userDao")
public void setUserDao(UserDao userDao) {
   this.userDao= userDao;
}

上述兩種自動裝配的依賴注入并不適合簡單值型別,如int、boolean、long、String以及Enum等,對于這些型別,Spring容器也提供了@Value注入的方式,

@Value和@Autowired、@Resource類似,也是用來對屬性進行注入的,只不過@Value是用來從Properties檔案中來獲取值的,并且@Value可以決議SpEL(Spring運算式),

比如,jdbc.properties檔案如下:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

利用注解@Value獲取jdbc.url和jdbc.username的值,實作如下:

public class UserServiceImpl implements UserService {
    //占位符方式
    @Value("${jdbc.url}")
    private String url;
    //SpEL表達方式,其中代表xml組態檔中的id值configProperties
    @Value("#{configProperties['jdbc.username']}")
    private String userName;

}

@Qualifier 注解有什么作用

當需要創建多個相同型別的 bean 并希望僅使用屬性裝配其中一個 bean 時,可以使用@Qualifier 注解和 @Autowired 通過指定應該裝配哪個 bean 來消除歧義,

@Bean和@Component有什么區別?

都是使用注解定義 Bean,@Bean 是使用 Java 代碼裝配 Bean,@Component 是自動裝配 Bean,

@Component 注解用在類上,表明一個類會作為組件類,并告知Spring要為這個類創建bean,每個類對應一個 Bean,

@Bean 注解用在方法上,表示這個方法會回傳一個 Bean,@Bean 需要在配置類中使用,即類上需要加上@Configuration注解,

@Component
public class Student {
    private String name = "lkm";
 
    public String getName() {
        return name;
    }
}

@Configuration
public class WebSocketConfig {
    @Bean
    public Student student(){
        return new Student();
    }
}

@Bean 注解更加靈活,當需要將第三方類裝配到 Spring 容器中,因為沒辦法源代碼上添加@Component注解,只能使用@Bean 注解的方式,當然也可以使用 xml 的方式,

@Component、@Controller、@Repositor和@Service 的區別?

@Component:最普通的組件,可以被注入到spring容器進行管理,

@Controller:將類標記為 Spring Web MVC 控制器,

@Service:將類標記為業務層組件,

@Repository:將類標記為資料訪問組件,即DAO組件,

Spring 事務實作方式有哪些?

事務就是一系列的操作原子執行,Spring事務機制主要包括宣告式事務和編程式事務,

  • 編程式事務:通過編程的方式管理事務,這種方式帶來了很大的靈活性,但很難維護,
  • 宣告式事務:將事務管理代碼從業務方法中分離出來,通過aop進行封裝,Spring宣告式事務使得我們無需要去處理獲得連接、關閉連接、事務提交和回滾等這些操作,使用 @Transactional 注解開啟宣告式事務,

@Transactional相關屬性如下:

屬性 型別 描述
value String 可選的限定描述符,指定使用的事務管理器
propagation enum: Propagation 可選的事務傳播行為設定
isolation enum: Isolation 可選的事務隔離級別設定
readOnly boolean 讀寫或只讀事務,默認讀寫
timeout int (in seconds granularity) 事務超時時間設定
rollbackFor Class物件陣列,必須繼承自Throwable 導致事務回滾的例外類陣列
rollbackForClassName 類名陣列,必須繼承自Throwable 導致事務回滾的例外類名字陣列
noRollbackFor Class物件陣列,必須繼承自Throwable 不會導致事務回滾的例外類陣列
noRollbackForClassName 類名陣列,必須繼承自Throwable 不會導致事務回滾的例外類名字陣列

有哪些事務傳播行為?

在TransactionDefinition介面中定義了七個事務傳播行為:

  1. PROPAGATION_REQUIRED如果存在一個事務,則支持當前事務,如果沒有事務則開啟一個新的事務,如果嵌套呼叫的兩個方法都加了事務注解,并且運行在相同執行緒中,則這兩個方法使用相同的事務中,如果運行在不同執行緒中,則會開啟新的事務,
  2. PROPAGATION_SUPPORTS 如果存在一個事務,支持當前事務,如果沒有事務,則非事務的執行,
  3. PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務,如果不存在事務,則拋出例外IllegalTransactionStateException
  4. PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務,需要使用JtaTransactionManager作為事務管理器,
  5. PROPAGATION_NOT_SUPPORTED 總是非事務地執行,并掛起任何存在的事務,需要使用JtaTransactionManager作為事務管理器,
  6. PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則拋出例外,
  7. PROPAGATION_NESTED 如果一個活動的事務存在,則運行在一個嵌套的事務中,如果沒有活動事務, 則按PROPAGATION_REQUIRED 屬性執行,

PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:

使用PROPAGATION_REQUIRES_NEW時,內層事務與外層事務是兩個獨立的事務,一旦內層事務進行了提交后,外層事務不能對其進行回滾,兩個事務互不影響,

使用PROPAGATION_NESTED時,外層事務的回滾可以引起內層事務的回滾,而內層事務的例外并不會導致外層事務的回滾,它是一個真正的嵌套事務,

Spring事務在什么情況下會失效?

1.訪問權限問題

java的訪問權限主要有四種:private、default、protected、public,它們的權限從左到右,依次變大,

如果事務方法的訪問權限不是定義成public,這樣會導致事務失效,因為spring要求被代理方法必須是public的,

翻開原始碼,可以看到,在AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則回傳null,即不支持事務,

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }
	...
}

2. 方法用final修飾

如果事務方法用final修飾,將會導致事務失效,因為spring事務底層使用了aop,也就是通過jdk動態代理或者cglib,幫我們生成了代理類,在代理類中實作的事務功能,

但如果某個方法用final修飾了,那么在它的代理類中,就無法重寫該方法,而添加事務功能,

同理,如果某個方法是static的,同樣無法通過動態代理,變成事務方法,

3.物件沒有被spring管理

使用spring事務的前提是:物件要被spring管理,需要創建bean實體,如果類沒有加@Controller、@Service、@Component、@Repository等注解,即該類沒有交給spring去管理,那么它的方法也不會生成事務,

4.表不支持事務

如果MySQL使用的存盤引擎是myisam,這樣的話是不支持事務的,因為myisam存盤引擎不支持事務,

5.方法內部呼叫

如下代碼所示,update方法上面沒有加 @Transactional 注解,呼叫有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事務會失效,

因為發生了自身呼叫,呼叫該類自己的方法,而沒有經過 Spring 的代理類,只有在外部呼叫事務才會生效,

@Service
public class OrderServiceImpl implements OrderService {

    public void update(Order order) {
        this.updateOrder(order);
    }

    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}

解決方法:

1、再宣告一個service,將內部呼叫改為外部呼叫

2、使用編程式事務

3、使用AopContext.currentProxy()獲取代理物件

@Servcie
public class OrderServiceImpl implements OrderService {
    
   public void update(Order order) {
        ((OrderService)AopContext.currentProxy()).updateOrder(order);
   }

    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
 }

6.未開啟事務

如果是spring專案,則需要在組態檔中手動配置事務相關引數,如果忘了配置,事務肯定是不會生效的,

如果是springboot專案,那么不需要手動配置,因為springboot已經在DataSourceTransactionManagerAutoConfiguration類中幫我們開啟了事務,

7.吞了例外

有時候事務不會回滾,有可能是在代碼中手動catch了例外,因為開發者自己捕獲了例外,又沒有手動拋出,把例外吞掉了,這種情況下spring事務不會回滾,

如果想要spring事務能夠正常回滾,必須拋出它能夠處理的例外,如果沒有拋例外,則spring認為程式是正常的,

Spring怎么解決回圈依賴的問題?

首先,有兩種Bean注入的方式,

構造器注入和屬性注入,

對于構造器注入的回圈依賴,Spring處理不了,會直接拋出BeanCurrentlylnCreationException例外,

對于屬性注入的回圈依賴(單例模式下),是通過三級快取處理來回圈依賴的,

而非單例物件的回圈依賴,則無法處理,

下面分析單例模式下屬性注入的回圈依賴是怎么處理的:

首先,Spring單例物件的初始化大略分為三步:

  1. createBeanInstance:實體化bean,使用構造方法創建物件,為物件分配記憶體,
  2. populateBean:進行依賴注入,
  3. initializeBean:初始化bean,

Spring為了解決單例的回圈依賴問題,使用了三級快取:

singletonObjects:完成了初始化的單例物件map,bean name --> bean instance

earlySingletonObjects :完成實體化未初始化的單例物件map,bean name --> bean instance

singletonFactories : 單例物件工廠map,bean name --> ObjectFactory,單例物件實體化完成之后會加入singletonFactories,

在呼叫createBeanInstance進行實體化之后,會呼叫addSingletonFactory,將單例物件放到singletonFactories中,

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);
        }
    }
}

假如A依賴了B的實體物件,同時B也依賴A的實體物件,

  1. A首先完成了實體化,并且將自己添加到singletonFactories中
  2. 接著進行依賴注入,發現自己依賴物件B,此時就嘗試去get(B)
  3. 發現B還沒有被實體化,對B進行實體化
  4. 然后B在初始化的時候發現自己依賴了物件A,于是嘗試get(A),嘗試一級快取singletonObjects和二級快取earlySingletonObjects沒找到,嘗試三級快取singletonFactories,由于A初始化時將自己添加到了singletonFactories,所以B可以拿到A物件,然后將A從三級快取中移到二級快取中
  5. B拿到A物件后順利完成了初始化,然后將自己放入到一級快取singletonObjects中
  6. 此時回傳A中,A此時能拿到B的物件順利完成自己的初始化

由此看出,屬性注入的回圈依賴主要是通過將實體化完成的bean添加到singletonFactories來實作的,而使用構造器依賴注入的bean在實體化的時候會進行依賴注入,不會被添加到singletonFactories中,比如A和B都是通過構造器依賴注入,A在呼叫構造器進行實體化的時候,發現自己依賴B,B沒有被實體化,就會對B進行實體化,此時A未實體化完成,不會被添加到singtonFactories,而B依賴于A,B會去三級快取尋找A物件,發現不存在,于是又會實體化A,A實體化了兩次,從而導致拋例外,

總結:1、利用快取識別已經遍歷過的節點; 2、利用Java參考,先提前設定物件地址,后完善物件,

Spring啟動程序

  1. 讀取web.xml檔案,

  2. 創建 ServletContext,為 ioc 容器提供宿主環境,

  3. 觸發容器初始化事件,呼叫 contextLoaderListener.contextInitialized()方法,在這個方法會初始化一個應用背景關系WebApplicationContext,即 Spring 的 ioc 容器,ioc 容器初始化完成之后,會被存盤到 ServletContext 中,

  4. 初始化web.xml中配置的Servlet,如DispatcherServlet,用于匹配、處理每個servlet請求,

Spring 的單例 Bean 是否有并發安全問題?

當多個用戶同時請求一個服務時,容器會給每一個請求分配一個執行緒,這時多個執行緒會并發執行該請求對應的業務邏輯,如果業務邏輯有對單例狀態的修改(體現為此單例的成員屬性),則必須考慮執行緒安全問題,

無狀態bean和有狀態bean

  • 有實體變數的bean,可以保存資料,是非執行緒安全的,
  • 沒有實體變數的bean,不能保存資料,是執行緒安全的,

在Spring中無狀態的Bean適合用單例模式,這樣可以共享實體提高性能,有狀態的Bean在多執行緒環境下不安全,一般用Prototype模式或者使用ThreadLocal解決執行緒安全問題,

Spring Bean如何保證并發安全?

Spring的Bean默認都是單例的,某些情況下,單例是并發不安全的,

Controller 舉例,假如我們在 Controller 中定義了成員變數,當多個請求來臨,進入的都是同一個單例的 Controller 物件,并對此成員變數的值進行修改操作,因此會互相影響,會有并發安全的問題,

應該怎么解決呢?

為了讓多個HTTP請求之間不互相影響,可以采取以下措施:

1、單例變原型

對 web 專案,可以 Controller 類上加注解 @Scope("prototype")@Scope("request"),對非 web 專案,在 Component 類上添加注解 @Scope("prototype")

這種方式實作起來非常簡單,但是很大程度上增大了 Bean 創建實體化銷毀的服務器資源開銷,

2、盡量避免使用成員變數

在業務允許的條件下,可以將成員變數替換為方法中的區域變數,這種方式個人認為是最恰當的,

3、使用并發安全的類

如果非要在單例Bean中使用成員變數,可以考慮使用并發安全的容器,如 ConcurrentHashMapConcurrentHashSet 等等,將我們的成員變數包裝到這些并發安全的容器中進行管理即可,

4、分布式或微服務的并發安全

如果還要進一步考慮到微服務或分布式服務的影響,方式3便不合適了,這種情況下可以借助于可以共享某些資訊的分布式快取中間件,如Redis等,這樣即可保證同一種服務的不同服務實體都擁有同一份共享資訊了,

@Async注解的原理

當我們呼叫第三方介面或者方法的時候,我們不需要等待方法回傳才去執行其它邏輯,這時如果回應時間過長,就會極大的影響程式的執行效率,所以這時就需要使用異步方法來并行執行我們的邏輯,在springboot中可以使用@Async注解實作異步操作,

使用@Async注解實作異步操作的步驟:

1.首先在啟動類上添加 @EnableAsync 注解,

@Configuration
@EnableAsync
public class App {
    public static void main(String[] args) {
         ApplicationContext ctx = new  
             AnnotationConfigApplicationContext(App.class);
        MyAsync service = ctx.getBean(MyAsync.class);
        System.out.println(service.getClass());
        service.async1();
        System.out.println("main thread finish...");
    }
}

2.在對應的方法上添加@Async注解,

@Component
public class MyAsync {
    @Async
    public void asyncTest() {
        try {
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("asyncTest...");
    }
}

運行代碼,控制臺輸出:

main thread finish...
asyncTest...

證明asyncTest方法異步執行了,

原理:

我們在主啟動類上貼了一個@EnableAsync注解,才能使用@Async生效,@EnableAsync的作用是通過@import匯入了AsyncConfigurationSelector,在AsyncConfigurationSelector的selectImports方法將ProxyAsyncConfiguration定義為Bean注入容器,在ProxyAsyncConfiguration中通過@Bean的方式注入AsyncAnnotationBeanPostProcessor類,

代碼如下:

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
}

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] { ProxyAsyncConfiguration.class.getName() };
			//...
		}
	}
}

public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        //創建postProcessor
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        //...
    }
}

AsyncAnnotationBeanPostProcessor往往期創建了一個增強器AsyncAnnotationAdvisor,在AsyncAnnotationAdvisor的buildAdvice方法中,創建了AnnotationAsyncExecutionInterceptor,

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        //創建一個增強器
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        //...
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }
}


public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    public AsyncAnnotationAdvisor(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
        //增強方法
        this.advice = buildAdvice(executor, exceptionHandler);
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }

    // 委托給AnnotationAsyncExecutionInterceptor攔截器
    protected Advice buildAdvice(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
        //攔截器
        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
        interceptor.configure(executor, exceptionHandler);
        return interceptor;
    }
}

AnnotationAsyncExecutionInterceptor繼承自AsyncExecutionInterceptor,間接實作了MethodInterceptor,該攔截器的實作的invoke方法把原來方法的呼叫提交到新的執行緒池執行,從而實作了方法的異步,

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        //...
        //構建放到AsyncTaskExecutor執行Callable Task
        Callable<Object> task = () -> {
            //...
        };
        //提交到新的執行緒池執行
        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }
}

由上面分析可以看到,@Async注解其實是通過代理的方式來實作異步呼叫的,

那使用@Async有什么要注意的呢?

1.使用@Aysnc的時候最好配置一個執行緒池Executor以讓執行緒復用節省資源,或者為SimpleAsyncTaskExecutor設定基于執行緒池實作的ThreadFactory,在否則會默認使用SimpleAsyncTaskExecutor,該executor會在每次呼叫時新建一個執行緒,

2.呼叫本類的異步方法是不會起作用的,這種方式繞過了代理而直接呼叫了方法,@Async注解會失效,


最后給大家分享一個Github倉庫,上面有大彬整理的300多本經典的計算機書籍PDF,包括C語言、C++、Java、Python、前端、資料庫、作業系統、計算機網路、資料結構和演算法、機器學習、編程人生等,可以star一下,下次找書直接在上面搜索,倉庫持續更新中~

Github地址

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

標籤:其他

上一篇:Django筆記十三之select_for_update等選擇和更新等相關操作

下一篇:Rust編程語言入門之泛型、Trait、生命周期

標籤雲
其他(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