主頁 > 後端開發 > 我把面試問爛了的?Spring面試題?總結了一下(帶答案,萬字總結,精心打磨,建議收藏)

我把面試問爛了的?Spring面試題?總結了一下(帶答案,萬字總結,精心打磨,建議收藏)

2021-09-01 14:26:46 後端開發

💂 個人主頁: Java程式魚
🤟 整個Java 體系的面試題我都會分享,大家可以持續關注
💬 如果文章對你有幫助、歡迎關注、點贊、收藏(一鍵三連)和訂閱專欄哦
💅 有任何問題歡迎私信,看到會及時回復!

序號內容鏈接地址
1Java基礎知識面試題https://blog.csdn.net/qq_35620342/article/details/119636436
2Java集合容器面試題待分享
3Java并發編程面試題待分享
4Java例外面試題待分享
5JVM面試題待分享
6Java Web面試題https://blog.csdn.net/qq_35620342/article/details/119642114
7Spring面試題https://blog.csdn.net/qq_35620342/article/details/119956512
8Spring MVC面試題https://blog.csdn.net/qq_35620342/article/details/119965560
9Spring Boot面試題待分享
10MyBatis面試題https://blog.csdn.net/qq_35620342/article/details/119956541
11Spring Cloud面試題待分享
12Redis面試題https://blog.csdn.net/qq_35620342/article/details/119575020
13MySQL資料庫面試題https://blog.csdn.net/qq_35620342/article/details/119930887
14RabbitMQ面試題待分享
15Dubbo面試題待分享
16Linux面試題待分享
17Tomcat面試題待分享
18ZooKeeper面試題待分享
19Netty面試題待分享
20資料結構與演算法面試題待分享

文章目錄

  • 一、Spring概述
    • 1.什么是spring?
    • 2.Spring框架的設計目標,設計理念,和核心是什么?
    • 3.使用Spring的好處是什么?
    • 4.Spring由哪些模塊組成?
    • 5.Spring 框架中都用到了哪些設計模式?
    • 6.詳細講解一下核心容器(spring context應用背景關系) 模塊
    • 7.Spring框架中有哪些不同型別的事件
    • 8.Spring 應用程式有哪些不同組件?
    • 9.使用 Spring 有哪些方式?
  • 二、Spring控制反轉
    • 1.什么是Spring IOC 容器?
    • 2.控制反轉(IoC)有什么作用?
    • 3.SpringIOC如何降低物件之間的耦合度?
    • 4.IOC的優點是什么?
    • 5.Spring 的 IOC支持哪些功能
    • 6.BeanFactory 和 ApplicationContext有什么區別?
    • 7.ApplicationContext通常的實作是什么?
    • 8.有哪些不同型別的依賴注入實作方式?
    • 9.構造器依賴注入和 Setter方法注入的區別
  • 三、Spring Beans
    • 1.什么是 Spring beans?
    • 2.一個 Spring Bean 定義包含什么?
    • 3.如何給Spring 容器提供配置元資料?Spring有幾種配置方式
    • 4.Spring基于xml注入bean的幾種方式
    • 5.你怎樣定義類的作用域?
    • 6.解釋Spring支持的幾種bean的作用域
    • 7.Spring框架中的單例bean是執行緒安全的嗎?
    • 8.Spring如何處理執行緒并發問題?
    • 9.解釋Spring框架中bean的生命周期
    • 10.在 Spring中如何注入一個java集合?
    • 12.什么是bean裝配?
    • 13.什么是bean的自動裝配?
    • 14.Sring 自動裝配 bean 有哪些方式?
    • 15.使用@Autowired注解自動裝配的程序是怎樣的?
    • 16.自動裝配有哪些局限性?
  • 四、Spring注解
    • 1.什么是基于Java的Spring注解配置? 給一些注解的例子
    • 2.怎樣開啟注解裝配?
    • 3.@Component, @Controller, @Repository, @Service 有何區別?
    • 4.@Required 注解有什么作用
    • 5.@Autowired 注解有什么作用
    • 6.@Autowired和@Resource之間的區別
    • 7.@Qualifier 注解有什么作用
    • 8.@RequestMapping 注解有什么用?
  • 五、Spring資料訪問
    • 1.解釋物件/關系映射集成模塊
    • 2.在Spring框架中如何更有效地使用JDBC?
    • 3.解釋JDBC抽象和DAO模塊
    • 4.Spring DAO 有什么用?
    • 5.Spring JDBC API 中存在哪些類?
    • 6.JdbcTemplate是什么
    • 7.使用Spring通過什么方式訪問Hibernate?使用 Spring 訪問 Hibernate 的方法有哪些?
    • 8.如何通過HibernateDaoSupport將Spring和Hibernate結合起來?
    • 9.Spring支持的事務管理型別, spring 事務實作方式有哪些?
    • 10.Spring事務的實作方式和實作原理
    • 11.Spring的事務傳播行為
    • 12.Spring 的事務隔離?
    • 13.Spring框架的事務管理有哪些優點?
    • 14.你更傾向用那種事務管理型別?
  • 六、Spring面向切面編程(AOP)
    • 1.什么是AOP?
    • 2.Spring AOP and AspectJ AOP 有什么區別?AOP 有哪些實作方式?
    • 3.JDK動態代理和CGLIB動態代理的區別
    • 4.如何理解 Spring 中的代理?
    • 5.解釋一下Spring AOP里面的幾個名詞
    • 6.Spring在運行時通知物件
    • 7.Spring只支持方法級別的連接點
    • 8.在Spring AOP 中,關注點和橫切關注的區別是什么?在 spring aop 中 concern 和 cross-cutting concern 的不同之處
    • 9.Spring通知有哪些型別?
    • 10.什么是切面 Aspect?
    • 11.解釋基于XML Schema方式的切面實作
    • 12.解釋基于注解的切面實作
    • 13.有幾種不同型別的自動代理?


一、Spring概述

1.什么是spring?

Spring是一個輕量級Java開發框架,最早由Rod Johnson創建,目的是為了解決企業級應用開發的業務邏輯層和其他各層的耦合問題,它是一個分層的JavaSE/JavaEE full-stack(一站式)輕量級開源框架,為開發Java應用程式提供全面的基礎架構支持,Spring負責基礎架構,因此Java開發者可以專注于應用程式的開發,

Spring最根本的使命是解決企業級應用開發的復雜性,即簡化Java開發

Spring可以做很多事情,它為企業級開發提供給了豐富的功能,但是這些功能的底層都依賴于它的兩個核心特性,也就是依賴注入(dependency injection,DI)面向切面編程(aspect-oriented programming,AOP)

為了降低Java開發的復雜性,Spring采取了以下4種關鍵策略

  • 基于POJO的輕量級和最小侵入性編程;
  • 通過依賴注入和面向介面實作松耦合;
  • 基于切面和慣例進行宣告式編程;
  • 通過切面和模板減少樣板式代碼,

2.Spring框架的設計目標,設計理念,和核心是什么?

Spring設計目標:Spring為開發者提供一個一站式輕量級應用開發平臺;

Spring設計理念:在JavaEE開發中,支持POJO和JavaBean開發方式,使應用面向介面開發,充分支持OO(面向物件)設計方法;Spring通過IoC容器實作物件耦合關系的管理,并實作依賴反轉,將物件之間的依賴關系交給IoC容器,實作解耦;

Spring框架的核心:IoC容器和AOP模塊,通過IoC容器管理POJO物件以及他們之間的耦合關系;通過AOP以動態非侵入的方式增強服務,

IoC讓相互協作的組件保持松散的耦合,而AOP編程允許你把遍布于應用各層的功能分離出來形成可重用的功能組件,

3.使用Spring的好處是什么?

  • 輕量:Spring 是輕量的,基本的版本大約 2MB

  • 控制反轉:Spring 通過控制反轉實作了松散耦合,物件們給出它們的依賴,而不是創建或查找依賴的物件們

  • 面向切面的編程(AOP):Spring 支持面向切面的編程,并且把應用業務邏輯和系統服務分開

  • 容器:Spring 包含并管理應用中物件的生命周期和配置

  • MVC 框架:Spring 的 WEB 框架是個精心設計的框架,是 Web 框架的一個很好的替代品

  • 事務管理:Spring 提供一個持續的事務管理介面,可以擴展到上至本地事務下至全域事務(JTA)

  • 例外處理:Spring 提供方便的 API 把具體技術相關的例外(比如由 JDBC,Hibernate or JDO拋出的)轉化為一致的 unchecked 例外

4.Spring由哪些模塊組成?

在這里插入圖片描述

  • 核心容器( Core Container)

    核心容器提供Spring框架的基本功能,核心容器中的主要組件是BeanFactory類,它是工廠模式的實作,JavaBean的管理就由它來負責,BeanFactory類通過IOC將應用程式的配置以及依賴性規范與實際的應用程式代碼相分離,

  • 資料訪問/集成模塊(Data Access/Integration)
    ORM模塊為主流的物件關系映射(object-relation mapping)API提供了集成層,這些主流的物件關系映射API包括了JPA、JDO、hibernate和IBatis,該模塊可以將O/R映射框架與Spring提供的特性進行組合來使用

  • Web模塊
    Web模塊包括Web、Servlet、Protlet這幾個模塊,
    Web模塊提供了基本的面向Web的集成功能,如多檔案上傳、使用servlet監聽器初始化IOC容器和面向Web的應用背景關系,還包含Spring的遠程支持中與Web相關的部分,
    Servlet模塊提供了Spring的Web應用的模型-視圖-控制器(MVC)實作,
    Protlet模塊提供了一個在protlet環境中使用的MVC實作,

  • AOP模塊
    AOP模塊提供了一個在AOP聯盟標準的面向切面編程的實作

  • Messaging
    模塊包含發布和訂閱訊息的特性,

  • Test模塊
    Test模塊支持使用JUnit和TestNG對Spring組件進行測驗,它提供一致的ApplicationContexts并快取這下背景關系,他還提供了一些mock物件,使得開發者可以獨立的測驗代碼,

5.Spring 框架中都用到了哪些設計模式?

(1)工廠設計模式
Spring使用工廠模式可以通過 BeanFactory 或 ApplicationContext 創建 bean 物件,

兩者對比:

  • BeanFactory :延遲注入(使用到某個 bean 的時候才會注入),相比于BeanFactory來說會占用更少的記憶體,程式啟動速度更快,
  • ApplicationContext :容器啟動的時候,不管你用沒用到,一次性創建所有 bean ,BeanFactory 僅提供了最基本的依賴注入支持,ApplicationContext 擴展了 BeanFactory ,除了有BeanFactory的功能之外還有額外更多功能,所以一般開發人員使用ApplicationContext會更多,

ApplicationContext的三個實作類:

  • ClassPathXmlApplication:把背景關系檔案當成類路徑資源,
  • FileSystemXmlApplication:從檔案系統中的 XML 檔案載入背景關系定義資訊,
  • XmlWebApplicationContext:從Web系統中的XML檔案載入背景關系定義資訊,

(2)單例模式
Spring中bean的默認作用域就是singleton(單例)的

(3)代理模式
AOP(Aspect-Oriented Programming:面向切面編程)能夠將那些與業務無關,卻為業務模塊所共同呼叫的邏輯或責任(例如事務處理、日志管理、權限控制等)封裝起來,便于減少系統的重復代碼,降低模塊間的耦合度,并有利于未來的可拓展性和可維護性,

Spring AOP就是基于動態代理的,如果要代理的物件實作了某個介面,那么Spring AOP會使用JDK Proxy,去創建代理物件,而對于沒有實作介面的物件,Spring AOP會使用Cglib,這時候Spring AOP會使用Cglib生成一個被代理物件的子類來作為代理,如下圖所示:
在這里插入圖片描述
當然你也可以使用AspectJ,Spring AOP以及集成了AspectJ,AspectJ應該算得上是Java生態系統中最完整的AOP框架了,

使用AOP之后我們可以把一些通用的功能抽象出來,在在需要用到的地方直接使用即可,這樣大大簡化了代碼量,我們需要增加新功能時也方便,這樣也提高了系統擴展性,日志功能、事務管理等等場景都用到了 AOP ,

(4)模板方法
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結尾的對資料庫操作的類,它們就使用到了模板模式,一般情況下,我們都是使用繼承的方式來實作模板模式,但是 Spring 并沒有使用這種方式,而是使用Callback 模式與模板方法模式配合,既達到了代碼復用的效果,同時增加了靈活性,

(5)觀察者模式
定義物件鍵一種一對多的依賴關系,當一個物件的狀態發生改變時,所有依賴于它的物件都會得到通知被制動更新,如Spring中listener的實作ApplicationListener,

(6)配接器模式
配接器模式(Adapter Pattern) 將一個介面轉換成客戶希望的另一個介面,配接器模式使介面不兼容的那些類可以一起作業,其別名為包裝器(Wrapper),

Spring AOP中的配接器模式:
我們知道 Spring AOP 的實作是基于代理模式,但是 Spring AOP 的增強或通知(Advice)使用到了配接器模式,與之相關的介面是AdvisorAdapter ,Advice 常用的型別有:BeforeAdvice(目標方法呼叫前,前置通知)、AfterAdvice(目標方法呼叫后,后置通知)、AfterReturningAdvice(目標方法執行結束后,return之前)等等,每個型別Advice(通知)都有對應的攔截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor,Spring預定義的通知要通過對應的配接器,適配成 MethodInterceptor介面(方法攔截器)型別的物件(如:MethodBeforeAdviceInterceptor 負責適配 MethodBeforeAdvice),

Spring MVC中的配接器模式:
在Spring MVC中,DispatcherServlet 根據請求資訊呼叫 HandlerMapping,決議請求對應的 Handler,決議到對應的 Handler(也就是我們平常說的 Controller 控制器)后,開始由HandlerAdapter 配接器處理,HandlerAdapter 作為期望介面,具體的配接器實作類用于對目標類進行適配,Controller 作為需要適配的類,

6.詳細講解一下核心容器(spring context應用背景關系) 模塊

這是基本的Spring模塊,提供spring 框架的基礎功能,BeanFactory 是任何以spring為基礎的應用的核心,Spring 框架建立在此模塊之上,它使Spring成為一個容器,

Bean 工廠是工廠模式的一個實作,提供了控制反轉功能,用來把應用的配置和依賴從真正的應用代碼中分離,最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根據XML檔案中的定義加載beans,該容器從XML 檔案讀取配置元資料并用它去創建一個完全配置的系統或應用,

7.Spring框架中有哪些不同型別的事件

Spring 提供了以下5種標準的事件:

  • 背景關系更新事件(ContextRefreshedEvent):在呼叫ConfigurableApplicationContext 介面中的refresh()方法時被觸發,

  • 背景關系開始事件(ContextStartedEvent):當容器呼叫ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發該事件,

  • 背景關系停止事件(ContextStoppedEvent):當容器呼叫ConfigurableApplicationContext的Stop()方法停止容器時觸發該事件,

  • 背景關系關閉事件(ContextClosedEvent):當ApplicationContext被關閉時觸發該事件,容器被關閉時,其管理的所有單例Bean都被銷毀,

  • 請求處理事件(RequestHandledEvent):在Web應用中,當一個http請求(request)結束觸發該事件,如果一個bean實作了ApplicationListener介面,當一個ApplicationEvent 被發布以后,bean會自動被通知,

8.Spring 應用程式有哪些不同組件?

Spring 應用一般有以下組件:

  • 介面 - 定義功能,
  • Bean 類 - 它包含屬性,setter 和 getter 方法,函式等,
  • Bean 組態檔 - 包含類的資訊以及如何配置它們,
  • Spring 面向切面編程(AOP) - 提供面向切面編程的功能,
  • 用戶程式 - 它使用介面,

9.使用 Spring 有哪些方式?

使用 Spring 有以下方式:

  • 作為一個成熟的 Spring Web 應用程式,
  • 作為第三方 Web 框架,使用 Spring Frameworks 中間層,
  • 作為企業級 Java Bean,它可以包裝現有的 POJO(Plain Old Java Objects),
  • 用于遠程使用,

二、Spring控制反轉

1.什么是Spring IOC 容器?

IOC 全稱為 Inversion of Control,翻譯為 “控制反轉”,

如何理解“控制反轉”好呢?理解好它的關鍵在于我們需要回答如下四個問題:

  • 誰控制誰:在傳統的開發模式下,我們都是采用直接 new 一個物件的方式來創建物件,也就是說你依賴的物件直接由你自己控制,但是有了 IOC 容器后,則直接由 IoC 容器來控制,所以“誰控制誰”,當然是 IoC 容器控制物件,
  • 控制什么:控制物件,
  • 為何是反轉:沒有 IoC 的時候我們都是在自己物件中主動去創建被依賴的物件,這是正轉,但是有了 IoC 后,所依賴的物件直接由 IoC 容器創建后注入到被注入的物件中,依賴的物件由原來的主動獲取變成被動接受,所以是反轉,
  • 哪些方面反轉了:所依賴物件的獲取被反轉了,

2.控制反轉(IoC)有什么作用?

  • 管理物件的創建和依賴關系的維護,物件的創建并不是一件簡單的事,在物件關系比較復雜時,如果依賴關系需要程式猿來維護的話,那是相當頭疼的

  • 解耦,由容器去維護具體的物件

  • 托管了類的產生程序,比如我們需要在類的產生程序中做一些處理,最直接的例子就是代理,如果有容器程式可以把這部分處理交給容器,應用程式則無需去關心類是如何完成代理的

3.SpringIOC如何降低物件之間的耦合度?

在我們的日常開發中,創建物件的操作隨處可見以至于對其十分熟悉的同時又感覺十分繁瑣,每次需要物件都需要親手將其new出來,甚至某些情況下由于壞編程習慣還會造成物件無法被回收,這是相當糟糕的,但更為嚴重的是,我們一直倡導的松耦合,少入侵原則,這種情況下變得一無是處,于是前輩們開始謀求改變這種編程陋習,考慮如何使用編碼更加解耦合,由此而來的解決方案是面向介面的編程,于是便有了如下寫法:

public class BookServiceImpl {

	 private  BookDaoImpl bookDaoImpl;
	 
	 public void oldCode(){
	     //原來的做法
	     bookDaoImpl = new bookDaoImpl();
	     bookDaoImpl.getAllCategories();
	 }
}

public class BookServiceImpl {

	 private BookDao bookDao;
	
	 public void newCode(){
	     //變為面向介面編程
	     bookDao = new bookDaoImpl();
	     bookDao.getAllCategories();
	 }
}

BookServiceImpl類中由原來直接與BookDaoImp打互動變為BookDao,即使BookDao最終實作依然是BookDaoImp,這樣的做的好處是顯而易見的,所有呼叫都通過介面bookDao來完成,而介面的真正的實作者和最終的執行者就是bookDaoImpl,當替換bookDaoImpl類,也只需修改bookDao指向新的實作類,

在這里插入圖片描述
雖然上述的代碼在很大程度上降低了代碼的耦合度,但是代碼依舊存在入侵性和一定程度的耦合性,比如在修改bookDao的實作類時,仍然需求修改BookServiceImpl的內部代碼,當依賴的類多起來時,查找和修改的程序也會顯得相當糟糕,因此我們仍需要尋找一種方式,它可以令開發者在無需觸及BookServiceImpl內容代碼的情況下實作修改bookDao的實作類,以便達到最低的耦合度和最少入侵的目的,實際上存在一種稱為反射的編程技術可以協助解決上述問題,反射是一種根據給出的完整類名(字串方式)來動態地生成物件,這種編程方式可以讓物件在生成時才決定到底是哪一種物件,因此可以這樣假設,在某個組態檔,該檔案已寫好bookDaoImpl類的完全限定名稱,通過讀取該檔案而獲取到bookDao的真正實作類完全限定名稱,然后通過反射技術在運行時動態生成該類,最終賦值給bookDao介面,也就解決了剛才的存在問題,這里為簡單演示,使用properties檔案作為組態檔,className.properties如下:

bookDao.name=com.spring.dao.BookDaoImpl

獲取該組態檔資訊動態為bookDao生成實作類:

public class BookServiceImpl implements BookService
{
    //讀取組態檔的工具類
    PropertiesUtil propertiesUtil=new PropertiesUtil("conf/className.properties");

    private BookDao bookDao;

    public void DaymicObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //獲取完全限定名稱
        String className=propertiesUtil.get("bookDao.name");
        //通過反射
        Class c=Class.forName(className);
        //動態生成實體物件
        bookDao= (BookDao) c.newInstance();
    }
}

的確如我們所愿生成了bookDao的實體,這樣做的好處是在替換bookDao實作類的情況只需修改組態檔的內容而無需觸及BookServiceImpl的內部代碼,從而把代碼修改的程序轉到組態檔中,相當于BookServiceImpl及其內部的bookDao通過組態檔與bookDao的實作類進行關聯,這樣BookServiceImpl與bookDao的實作類間也就實作了解耦合,當然BookServiceImpl類中存在著BookDao物件是無法避免的,畢竟這是協同作業的基礎,我們只能最大程度去解耦合,

在這里插入圖片描述
了解了上述的問題再來理解IOC就顯得簡單多了,Spring IOC 也是一個java物件,在某些特定的時間被創建后,可以進行對其他物件的控制,包括初始化、創建、銷毀等,簡單地理解,在上述程序中,我們通過組態檔配置了BookDaoImpl實作類的完全限定名稱,然后利用反射在運行時為BookDao創建實際實作類,包括BookServiceImpl的創建,Spring的IOC容器都會幫我們完成,而我們唯一要做的就是把需要創建的類和其他類依賴的類以組態檔的方式告訴IOC容器需要創建那些類和注入哪些類即可,Spring通過這種控制反轉(IoC)的設計模式促進了松耦合,這種方式使一個物件依賴其它物件時會通過被動的方式傳送進來(如BookServiceImpl被創建時,其依賴的BookDao的實作類也會同時被注入BookServiceImpl中),而不是通過手動創建這些類,我們可以把IoC模式看做是工廠模式的升華,可以把IoC看作是一個大工廠,只不過這個大工廠里要生成的物件都是在組態檔(XML)中給出定義的,然后利用Java的反射技術,根據XML中給出的類名生成相應的物件,從某種程度上來說,IoC相當于把在工廠方法里通過硬編碼創建物件的代碼,改變為由XML檔案來定義,也就是把工廠和物件生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性,更是達到最低的耦合度,因此我們要明白所謂為的IOC就將物件的創建權,交由Spring完成,從此解放手動創建物件的程序,同時讓類與類間的關系到達最低耦合度,

4.IOC的優點是什么?

  • IOC 或 依賴注入把應用的代碼量降到最低,
  • 它使應用容易測驗,單元測驗不再需要單例和JNDI查找機制,
  • 最小的代價和最小的侵入性使松散耦合得以實作,
  • IOC容器支持加載服務時的餓漢式初始化和懶加載,

5.Spring 的 IOC支持哪些功能

Spring 的 IoC 設計支持以下功能:

  • 依賴注入
  • 依賴檢查
  • 自動裝配
  • 支持集合
  • 指定初始化方法和銷毀方法
  • 支持回呼某些方法(但是需要實作 Spring 介面,略有侵入)

其中,最重要的就是依賴注入,從 XML 的配置上說,即 ref 標簽,對應 Spring RuntimeBeanReference 物件,

對于 IoC 來說,最重要的就是容器,容器管理著 Bean 的生命周期,控制著 Bean 的依賴注入,

6.BeanFactory 和 ApplicationContext有什么區別?

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

(1)依賴關系
BeanFactory:是Spring里面最底層的介面,包含了各種Bean的定義,讀取bean配置檔案,管理bean的加載、實體化,控制bean的生命周期,維護bean之間的依賴關系,
ApplicationContext介面作為BeanFactory的派生,除了提供BeanFactory所具有的功能外,還提供了更完整的框架功能:

  • 繼承 MessageSource,提供國際化的標準訪問策略,
  • 繼承 ApplicationEventPublisher ,提供強大的事件機制
  • 擴展 ResourceLoader,可以用來加載多個 Resource,可以靈活訪問不同的資源
  • 對 Web 應用的支持

(2)加載方式
ApplicationContext介面繼承BeanFactory介面,Spring核心工廠是BeanFactory,BranFactory采用延遲加載,第一次getBean時才會初始化Bean,ApplicationContext是會在加載組態檔時初始化Bean,

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

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

7.ApplicationContext通常的實作是什么?

  • FileSystemXmlApplicationContext:該容器從 XML 檔案中加載已被定義的 bean,在這里,你需要提供給構造器 XML 檔案的完整路徑

  • ClassPathXmlApplicationContext:該容器從 XML 檔案中加載已被定義的 bean,在這里,你不需要提供 XML 檔案的完整路徑,只需正確配置 CLASSPATH 環境變數即可,因為,容器會從 CLASSPATH 中搜索 bean 組態檔,

  • WebXmlApplicationContext:該容器會在一個 web 應用程式的范圍內加載在 XML 檔案中已被定義的 bean,

8.有哪些不同型別的依賴注入實作方式?

依賴注入是時下最流行的IoC實作方式,依賴注入分為介面注入(Interface Injection),Setter方法注入(Setter Injection)和構造器注入(Constructor Injection)三種方式,其中介面注入由于在靈活性和易用性比較差,現在從Spring4開始已被廢棄,

構造器依賴注入:構造器依賴注入通過容器觸發一個類的構造器來實作的,該類有一系列引數,每個引數代表一個對其他類的依賴,

Setter方法注入:Setter方法注入是容器通過呼叫無參構造器或無參static工廠 方法實體化bean之后,呼叫該bean的setter方法,即實作了基于setter的依賴注入,

9.構造器依賴注入和 Setter方法注入的區別

建構式注入setter 注入
沒有部分注入有部分注入
不會覆寫 setter 屬性會覆寫 setter 屬性
任意修改都會創建一個新實體任意修改不會創建一個新實體
適用于設定很多屬性適用于設定少量屬性

兩種依賴方式都可以使用,構造器注入和Setter方法注入,最好的解決方案是用構造器引數實作強制依賴,setter方法實作可選依賴,

三、Spring Beans

1.什么是 Spring beans?

Spring beans 是那些形成 Spring 應用的主干的 java 物件,它們被 Spring IOC 容器初始化,裝配,和管理,這些 beans 通過容器中配置的元資料創建,比如,以 XML 檔案中的形式定義,

Spring 框架定義的 beans 都是單件 beans,在 bean tag 中有個屬性”singleton”,如果它被賦為 TRUE,bean 就是單件,否則就是一個 prototype bean,默認是 TRUE,所以所有在Spring 框架中的 beans 預設都是單件,

2.一個 Spring Bean 定義包含什么?

一個 Spring Bean 的定義包含容器必知的所有配置元資料,包括如何創建一個 bean,它的生命周期詳情及它的依賴,

3.如何給Spring 容器提供配置元資料?Spring有幾種配置方式

這里有三種重要的方法給Spring 容器提供配置元資料,

  • XML組態檔,
  • 基于注解的配置,
  • 基于java的配置,

4.Spring基于xml注入bean的幾種方式

  • Set方法注入;

  • 構造器注入:①通過index設定引數的位置;②通過type設定引數型別;

  • 靜態工廠注入;

  • 實體工廠;

5.你怎樣定義類的作用域?

當定義一個 在Spring里,我們還能給這個bean宣告一個作用域,它可以通過bean 定義中的scope屬性來定義,如,當Spring要在需要的時候每次生產一個新的bean實體,bean的scope屬性被指定為prototype,另一方面,一個bean每次使用的時候必須回傳同一個實體,這個bean的scope 屬性 必須設為 singleton,

6.解釋Spring支持的幾種bean的作用域

(1)Singleton作用域
所謂Bean的作用域是指spring容器創建Bean后的生存周期即由創建到銷毀的整個程序,之前我們所創建的所有Bean其作用域都是Singleton,這是Spring默認的,在這樣的作用域下,每一個Bean的實體只會被創建一次,而且Spring容器在整個應用程式生存期中都可以使用該實體,因此之前的代碼中spring容器創建Bean后,通過代碼獲取的bean,無論多少次,都是同一個Bean的實體,我們可使用<bean>標簽的scope屬性來指定一個Bean的作用域,如下:

<!-- 默認情況下無需宣告Singleton -->
<bean name="accountDao" scope="singleton"    
class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>

(2)prototype作用域
除了Singleton外還有另外一種比較常用的作用域,prototype,它代表每次獲取Bean實體時都會新創建一個實體物件,類似new運算子,我們來簡單測驗一下:

<!-- 作用域:prototype -->
<bean name="accountDao" scope="prototype"     class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>

測驗代碼:

@Test
public void test2()  {
    ApplicationContext applicationContext=new
            ClassPathXmlApplicationContext("spring/spring-ioc.xml");
    AccountDao accountDao1 = (AccountDao) applicationContext.getBean("accountDao");
    AccountDao accountDao2 = (AccountDao) applicationContext.getBean("accountDao");
    System.out.println("accountDao1地址:"+accountDao1.toString());
    System.out.println("accountDao2地址:"+accountDao2.toString());
}
執行結果:
accountDao1地址:com.spring.springIoc.dao.impl.AccountDaoImpl@579a0931
accountDao2地址:com.spring.springIoc.dao.impl.AccountDaoImpl@49ads019

顯然是兩個不同的實體物件,當然我們也可通過注解來宣告作用域:

@Scope("prototype")
public class AccountDaoImpl {
    //......
}

這里還需要說明一種特殊的情景,當一個作用域為Singleton的Bean依賴于一個作用域為prototype的Bean時如下:

<!-- 作用域prototype-->
<bean name="accountDao" scope="prototype"
class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>
<!-- 作用域Singleton -->
<bean name="accountService" class="com.spring.springIoc.service.impl.AccountServiceImpl">
    <!-- 注入作用域為prototype的accountDao物件,需要set方法 -->
    <property name="accountDao" ref="accountDao"/>
</bean>

在這樣的情況下希望的是每次getBean(“accountService”)處理的都是一個新的accountDao實體物件,但是由于accountService的依賴是在Bean被創建時注入的,而且accountService是一個Singleton,整個生存周期中只會創建一次,因此它所依賴的accountDao實體物件也只會被注入一次,此后不會再注入任何新的accountDao實體物件,為了解決這種困境,只能放棄使用依賴注入的功能,使用代碼實作,如下:通過實作ApplicationContextAware介面,重寫setApplicationContext,這樣的話Spring容器在創建AccountServiceImpl實體時會自動注入ApplicationContext物件,此時便可以通過ApplicationContext獲取accountDao實體了,這樣可以保證每次獲取的accountDao實體都是新的(這里的代碼只是演示該程序,實際開發中一般不會要求accountDao每次都是新實體,因為accountDao無需記錄狀態資訊,即無狀態bean,一般默認singleton即可)

<bean name="accountDao" scope="prototype"  class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/>
<!-- accountDao通過代碼注入 -->
<bean name="accountService" class="com.zejian.spring.springIoc.service.impl.AccountServiceImpl" />

代碼注入示范:

public class AccountServiceImpl implements AccountService , ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void doSomething() {
        System.out.println("AccountServiceImpl#doSomething......");
        System.out.println("getAccountDao....."+ getAccountDao().toString());

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    private AccountDao getAccountDao(){
        return applicationContext.getBean(AccountDao.class);
    }
}

測驗代碼:

//加載組態檔
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml");
//測驗獲取不同實體的AccountDao
AccountService accountService= (AccountService) applicationContext.getBean("accountService");
accountService.doSomething();
AccountService accountService1= (AccountService) applicationContext.getBean("accountService");
accountService1.doSomething();
運行結果:
AccountServiceImpl#doSomething......
getAccountDao.....com.spring.springIoc.dao.impl.AccountDaoImpl@ab321564
AccountServiceImpl#doSomething......
getAccountDao.....com.spring.springIoc.dao.impl.AccountDaoImpl@2aga1580

顯然這樣的方式,每次獲取的daoAccount都不一樣,也就解決上述的問題,另外的一種情況是當一個作用域為propotype的Bean依賴于一個Singleton作用域的Bean時,解決方案跟上述是相同的,請注意,當一個Bean被設定為prototype 后Spring就不會對一個bean的整個生命周期負責,容器在初始化、配置、裝飾或者是裝配完一個prototype實體后,將它交給客戶端,隨后就對該prototype實體不聞不問了,因此我們需要慎用它,一般情況下,對有狀態的bean應該使用prototype作用域,而對無狀態的bean則應該使用singleton作用域,所謂有狀態就是該bean有保存資訊的能力,不能共享,否則會造成執行緒安全問題,而無狀態則不保存資訊,是執行緒安全的,可以共享,spring中大部分bean都是Singleton,整個生命周期程序只會存在一個,

(3)request與session作用域
在spring2.5中專門針對Web應該程式引進了request和session這兩種作用域,關于request作用域,對于每次HTTP請求到達應用程式,Spring容器會創建一個全新的Request作用域的bean實體,且該bean實體僅在當前HTTP request內有效,整個請求程序也只會使用相同的bean實體,因此我們可以根據需要放心的更改所建實體的內部狀態,而其他請求HTTP請求則創建新bean的實體互不干擾,當處理請求結束,request作用域的bean實體將被銷毀,如在接收引數時可能需要一個bean實體來裝載一些引數,顯然每次請求引數幾乎不會相同,因此希望bean實體每次都是足夠新的而且只在request作用域范圍內有效,關于Session可能你也已猜到,每當創建一個新的HTTP Session時就會創建一個Session作用域的Bean,并該實體bean伴隨著會話的存在而存在,下面看一個測驗程式:

@Component
@Scope(value = "singleton")
public class SingletonBean {
    //......
}

@Component
@Scope(value = "prototype" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
    //......
}

@Component
@Scope(value = "request" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
    //......
}

@Component
@Scope(value = "session" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionBean {
    //........
}

上述代碼,分別創建4個不同作用域的Bean,并使用注解的方式開發,@Component表名它們是組件類,需要Spring容器幫忙創建,@Scope注明作用域,value指明是哪種作用域,除了SingletonBean外,其它Bean還使用proxyMode指明哪種代理模式創建,這里沒有介面,因此使用CGLib代理生成(后面會分析為什么這么做),接著需要在xml注明掃描包告訴Spring容器它們在哪里(4個類都宣告在com.zejian.spring.dto包下),

<!-- 包掃描 -->
<context:component-scan  base-package="com.spring.dto" />

使用SpringMVC創建Web訪問層(如果不清楚springmvc,可以理解為web訪問層即將類似Servlet):

@Controller
public class BookController {
    @Autowired
    private RequestBean requestBean;
    @Autowired
    private SessionBean sessionBean;
    @Autowired
    private PrototypeBean prototypeBean;
    @Autowired
    private SingletonBean singletonBean;


    @RequestMapping(value = "/test")
    public void test()
    {
        print();
    }

    public void print() {
        System.out.println("first  time singleton is :" + singletonBean);
        System.out.println("second time singleton is :" + singletonBean);

        System.out.println("first  time prototype is :" + prototypeBean);
        System.out.println("second time prototype is :" + prototypeBean);

        System.out.println("first  time requestBean is :" + requestBean);
        System.out.println("second time requestBean is :" + requestBean);

        System.out.println("first  time sessionBean is :" + sessionBean);
        System.out.println("second time sessionBean is :" + sessionBean);

        System.out.println("===========================================");
    }
 }

現在啟動tomcat服務器使用Chrome瀏覽器進行連續兩次訪問結果如下:

first  time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
second time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
first  time prototypeBean is :com.zejian.spring.dto.PrototypeBean@1ed53cde
second time prototypeBean is :com.zejian.spring.dto.PrototypeBean@35c052be
first  time requestBean is :com.zejian.spring.dto.RequestBean@15b9dfe1
second time requestBean is :com.zejian.spring.dto.RequestBean@15b9dfe1
first  time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
second time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
===========================================
first  time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
second time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
first  time prototypeBean is :com.zejian.spring.dto.PrototypeBean@7775fd09
second time prototypeBean is :com.zejian.spring.dto.PrototypeBean@79b20d97
first  time requestBean is :com.zejian.spring.dto.RequestBean@7d8d9679
second time requestBean is :com.zejian.spring.dto.RequestBean@7d8d9679
first  time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
second time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
===========================================

顯然singletonBean永遠只有一個實體,而PrototypeBean則每次被獲取都會創建新的實體,對應RequestBean,在同一次Http請求程序中是同一個實體,當請求結束,RequestBean也隨著銷毀,在新的Http請求則會生成新的RequestBean實體,對于SessionBean,由于是在同一個瀏覽器中訪問屬于同一次會話,因此SessionBean實體都是同一個實體物件,現在使用另外一個瀏覽器啟動訪問,觀察SessionBean是否變化,

first  time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
second time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
first  time prototypeBean is :com.zejian.spring.dto.PrototypeBean@5a85c6a7
second time prototypeBean is :com.zejian.spring.dto.PrototypeBean@54423387
first  time requestBean is :com.zejian.spring.dto.RequestBean@507dadd7
second time requestBean is :com.zejian.spring.dto.RequestBean@507dadd7
first  time sessionBean is :com.zejian.spring.dto.SessionBean@157f39bc
second time sessionBean is :com.zejian.spring.dto.SessionBean@157f39bc
===========================================

顯然SessionBean已改變,也就說明不同的會話中SessionBean實體是不同的,但我們還是很詫異,為什么需要在其他3種作用域上設定代理模式?事實上這個問題的本質與前面Singleton作用域的bean依賴于Prototype作用域的bean原理是相同,Prototype前面已分析過了(使用注解時也必須宣告代理模式),這里我們主要分析request和session作用域,由于Spring容器只會在創建bean實體時幫助我們注入該實體bean所依賴的其他bean實體,而且只會注入一次,這并不是request、session作用域所希望看到的,畢竟它們都需要在不同的場景下注入新的實體物件而不是唯一不變的實體物件,為了解決這種困境,必須放棄直接在xml中注入bean實體,改用java代碼方式(實作ApplicationContextAware介面)或者注解的方式(@Autowired)注入,示例中選擇了后者,并在bean的宣告中宣告了動態代理模式(關于動態代理自行查閱相關資料),幸運地是,Spring容器足夠聰明,以至于spring容器通過代理的方式生成新的代理實體bean,以此來滿足創建新實體的需求,在程式運行程序中,當一個方法呼叫到達該代理物件時,Spring容器便嘗試在當前的請求(Request)或者會話(Session)獲取目標物件Bean(真正的實體Bean),如果已存在則使用該Bean,否則,代理方法將創建新實體bean處理請求或者會話,請注意,這里指的的是一次Http請求或者一次會話的程序,如果希望request和session作用域通過xml組態檔方式宣告時必須在<bean>標簽中放置<aop:scoped-proxy>作為子標簽,該作用于注解宣告代理模式效果相同(需要參考aop命名空間,關于aop不清楚的,這里可簡單理解為宣告代理即可),請注意,經過測驗這種宣告代理的方式不適合prototype作用域,該作用域生效的方式目前測驗中只有基于注解方式和前面基于實作ApplicationContextAware介面兩種方式,

<?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:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="requestBean" scope="request" class="com.spring.dto.RequestBean" >
    <!-- 宣告aop代理 -->
    <aop:scoped-proxy />
</bean>

<bean id="sessionBean" scope="request" class="com.spring.dto.SessionBean" >
    <!-- 宣告aop代理 -->
    <aop:scoped-proxy />
</bean>
</beans>

最后還有一點需要明確的是,如果web層適用的是SpringMVC處理web請求,則不需要做任何事情就可以使Request和Session作用域生效,但倘若適用其他web層框架的實作,務必需要在web.xml中宣告如下監聽器,以便使Request和Session作用域正常作業:

<web-app>
    <listener>
        <listener-class> org.springframework.web.context.request.RequestContextListener <listener-class>
    </listener>
</web-app>

(4)globalSession作用域
這種作用域類似于Session作用域,相當于全域變數,類似Servlet的Application,適用基于portlet的web應用程式,請注意,portlet在這指的是分布式開發,而不是portlet語言開發,

7.Spring框架中的單例bean是執行緒安全的嗎?

不是,Spring框架中的單例bean不是執行緒安全的,

spring 中的 bean 默認是單例模式,spring 框架并沒有對單例 bean 進行多執行緒的封裝處理,

實際上大部分時候 spring bean 無狀態的(比如 dao 類),所有某種程度上來說 bean 也是安全的,但如果 bean 有狀態的話(比如 view model 物件),那就要開發者自己去保證執行緒安全了,最簡單的就是改變 bean 的作用域,把“singleton”變更為“prototype”,這樣請求 bean 相當于 new Bean()了,所以就可以保證執行緒安全了,

  • 有狀態就是有資料存盤功能,
  • 無狀態就是不會保存資料,

8.Spring如何處理執行緒并發問題?

在一般情況下,只有無狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域,因為Spring對一些Bean中非執行緒安全狀態采用ThreadLocal進行處理,解決執行緒安全問題,

ThreadLocal和執行緒同步機制都是為了解決多執行緒中相同變數的訪問沖突問題,同步機制采用了“時間換空間”的方式,僅提供一份變數,不同的執行緒在訪問前需要獲取鎖,沒獲得鎖的執行緒則需要排隊,而ThreadLocal采用了“空間換時間”的方式,

ThreadLocal會為每一個執行緒提供一個獨立的變數副本,從而隔離了多個執行緒對資料的訪問沖突,因為每一個執行緒都擁有自己的變數副本,從而也就沒有必要對該變數進行同步了,ThreadLocal提供了執行緒安全的共享物件,在撰寫多執行緒代碼時,可以把不安全的變數封裝進ThreadLocal,

9.解釋Spring框架中bean的生命周期

在這里插入圖片描述

介面方法說明
BeanFactoryPostProcessorpostProcessBeanFactory在Bean物件實體化之前執行, 通過beanFactory可以獲取bean的定義資訊, 并可以修改bean的定義資訊,這點是和BeanPostProcessor最大區別
BeanPostProcessorpostProcessBeforeInitialization實體化、依賴注入完畢,在呼叫顯示的初始化之前完成一些定制的初始化任務
postProcessAfterInitialization實體化、依賴注入、初始化完畢時執行
InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation在方法實體化之前執行,回傳結果為null正常執行,回傳結果如果不為null則會跳過相關方法而進入初始化完成后的流程
postProcessAfterInstantiation在方法實體化之后執行,回傳結果true才會執行postProcessPropertyValues方法
postProcessPropertyValues可以用來修改Bean中屬性的內容
InitializingBeanafterPropertiesSet初始化的方法
DisposableBeandestroy容器銷毀前的回呼方法
AwaresetXXX感知對應Spring容器的內容
@PostConstruct標注在方法頭部,表示初始化的方法
@PreDestroy標注在方法頭部,表示銷毀前回呼的方法
init-method屬性指定初始化的方法
destory-method屬性指定銷毀前的回呼方法

(1)BeanFactoryPostProcessor介面
該介面中的方法是最先執行的,在Bean實體化之前執行

/**
 * 自定義BeanFactoryPostProcessor
 *
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	/**
	 * 本方法在Bean物件實體化之前執行,
	 * 通過beanFactory可以獲取bean的定義資訊,
	 * 并可以修改bean的定義資訊,這點是和BeanPostProcessor最大區別
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
		System.out.println("****** BeanFactoryPostProcessor 開始執行了");
		/*String[] names = beanFactory.getBeanDefinitionNames();
		for (String name : names) {
			if("user".equals(name)){
				
				BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
				MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
				// MutablePropertyValues如果設定了相關屬性,可以修改,如果沒有設定則可以添加相關屬性資訊
				if(propertyValues.contains("name")){
					propertyValues.addPropertyValue("name", "bobo");
					System.out.println("修改了屬性資訊");
				}
			}
		}*/
		System.out.println("******* BeanFactoryPostProcessor 執行結束了");
	}
}

(2)BeanPostProcessor介面
該介面中定義了兩個方法,分別在Bean物件實體化及裝配后在初始化的前后執行

/**
 * 自定義BeanPostProcessor實作類
 * BeanPostProcessor介面的作用是:
 * 	 我們可以通過該介面中的方法在bean實體化、配置以及其他初始化方法前后添加一些我們自己的邏輯
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor{

	/**
	 * 實體化、依賴注入完畢,在呼叫顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法回傳值不能為null
	 * 如果回傳null那么在后續初始化方法將報空指標例外或者通過getBean()方法獲取不到bena實體物件
	 * 因為后置處理器從Spring IoC容器中取出bean實體物件沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println(">>后置處理器 before方法:"+bean+"\t"+beanName);
		}
		
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實體化、依賴注入、初始化完畢時執行 
	 * 注意:方法回傳值不能為null
	 * 如果回傳null那么在后續初始化方法將報空指標例外或者通過getBean()方法獲取不到bena實體物件
	 * 因為后置處理器從Spring IoC容器中取出bean實體物件沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println("<<后置處理器after方法:"+bean+"\t"+beanName);
		}
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}
}

(3)InstantiationAwareBeanPostProcessor介面
該介面是BeanPostProcessor介面的子介面,所以該介面肯定具有BeanPostProcessor介面的功能,同時又定義了三個自己的介面,這三個介面是在Bean實體化前后執行的方法,

/**
 * 自定義處理器
 *
 */
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{

	/**
	 * BeanPostProcessor介面中的方法
	 * 在Bean的自定義初始化方法之前執行
	 * Bean物件已經存在了
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		// TODO Auto-generated method stub
		if("user".equals(beanName)){
			System.out.println("【---InstantiationAwareBeanPostProcessor---】 postProcessBeforeInitialization");
		}
		
		return bean;
	}

	/**
	 * BeanPostProcessor介面中的方法
	 * 在Bean的自定義初始化方法執行完成之后執行
	 * Bean物件已經存在了
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println("【--InstantiationAwareBeanPostProcessor----】 postProcessAfterInitialization");
		}
		return bean;
	}

	/**
	 * InstantiationAwareBeanPostProcessor中自定義的方法
	 * 在方法實體化之前執行  Bean物件還沒有
	 */
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println("【--InstantiationAwareBeanPostProcessor----】postProcessBeforeInstantiation");
		}
		return null;
	}

	/**
	 * InstantiationAwareBeanPostProcessor中自定義的方法
	 * 在方法實體化之后執行  Bean物件已經創建出來了
	 */
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println("【--InstantiationAwareBeanPostProcessor----】postProcessAfterInstantiation");
		}
		return true;
	}

	/**
	 * InstantiationAwareBeanPostProcessor中自定義的方法
	 * 可以用來修改Bean中屬性的內容
	 */
	@Override
	public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
			String beanName) throws BeansException {
		if("user".equals(beanName)){
			System.out.println("【--InstantiationAwareBeanPostProcessor----】postProcessPropertyValues--->");
		}
		return pvs;
	}
}

(4)BeanNameAware,BeanFactoryAware等Aware介面
Aware介面是用來讓物件感知當前的IOC環境

(5)InitializingBean,DisposableBean介面
這兩個介面是Bean初始化及銷毀回呼的方法,

(6)@PostConstruct和@PreDestroy注解

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
 * 實作InitializingBean和DisposableBean介面
 *
 */
public class User implements InitializingBean,DisposableBean,BeanNameAware,BeanFactoryAware{

	private int id;
	
	private String name;
	//感知本物件在Spring容器中的id屬性
	private String beanName;
	// 感知本物件所屬的BeanFactory物件
	private BeanFactory factory;
	
	public User(){
		System.out.println("構造方法被執行了...User 被實體化");
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		System.out.println("《注入屬性》注入name屬性"+name);
		this.name = name;
	}

	public String getBeanName() {
		return beanName;
	}

	
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
	}

	/**
	 * bean物件銷毀前的回呼方法
	 */
	@Override
	public void destroy() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("《DisposableBean介面》destory ....");
	}

	/**
	 * 初始化的方法
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("初始化:《InitializingBean介面》afterPropertiesSet....");
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		// TODO Auto-generated method stub
		System.out.println("【BeanFactoryAware介面】setBeanFactory");
		this.factory = beanFactory;
	}

	@Override
	public void setBeanName(String name) {
		System.out.println("【BeanNameWare介面】setBeanName");
		this.beanName = name;
	}

	public BeanFactory getFactory() {
		return factory;
	}

	/**
	 * 也是個初始化的方法
	 */
	@PostConstruct
	public void postConstruct(){
		System.out.println("初始化:【@PostConstruct】執行了...");
	}
	/**
	 * 銷毀前的回呼方法
	 */
	@PreDestroy
	public void preDestory(){
		System.out.println("【@preDestory】執行了...");
	} 
	/**
	 * 初始化的方法
	 * 通過bean標簽中的 init-method屬性指定
	 */
	public void start(){
		System.out.println("初始化:【init-method】方法執行了....");
	}
	
	/**
	 * 銷毀前的回呼方法
	 * 通過bean標簽中的 destory-method屬性指定
	 */
	public void stop(){
		System.out.println("【destory-method】方法執行了....");
	}
}

(7)init-method、destroy-method

<?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 http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<context:annotation-config/>

	<bean class="com.pojo.User" id="user" init-method="start" destroy-method="stop" >
		<property name="name" value="烤鴨"></property>
	</bean>
	
	<!-- 注冊后置處理器 -->
	<bean class="com.processor.MyBeanPostProcessor"/>
	
	
	<!-- 注冊 InstantiationAwareBeanPostProcessor -->
	<bean class="com.processor.MyInstantiationAwareBeanPostProcessor"></bean>
	<!-- 注冊 BeanFactoryPostProcessor物件-->
	<bean class="com.factoryprocessor.MyBeanFactoryPostProcessor"/>
</beans>

(8)測驗

@Test
public void test1() {
	System.out.println("Spring容器開始加載....");
	ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
	User user = ac.getBean(User.class);
	System.out.println("---------------"+user);
	ac.registerShutdownHook();
	System.out.println("Spring容器卸載完成....");
}

結果:

Spring容器開始加載....
三月 04, 2019 11:14:38 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@707f7052: startup date [Mon Mar 04 23:14:38 CST 2019]; root of context hierarchy
三月 04, 2019 11:14:39 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContext.xml]
****** BeanFactoryPostProcessor 開始執行了
******* BeanFactoryPostProcessor 執行結束了
【--InstantiationAwareBeanPostProcessor----】postProcessBeforeInstantiation
構造方法被執行了...User 被實體化
【--InstantiationAwareBeanPostProcessor----】postProcessAfterInstantiation
【--InstantiationAwareBeanPostProcessor----】postProcessPropertyValues--->
《注入屬性》注入name屬性波波烤鴨
【BeanNameWare介面】setBeanName
【BeanFactoryAware介面】setBeanFactory
>>后置處理器 before方法:User [id=0, name=波波烤鴨, beanName=user]	user
【---InstantiationAwareBeanPostProcessor---】 postProcessBeforeInitialization
初始化:【@PostConstruct】執行了...
初始化:《InitializingBean介面》afterPropertiesSet....
初始化:【init-method】方法執行了....
<<后置處理器after方法:User [id=0, name=烤鴨, beanName=user]	user
【--InstantiationAwareBeanPostProcessor----】 postProcessAfterInitialization
---------------User [id=0, name=波波烤鴨, beanName=user]
Spring容器卸載完成....
三月 04, 2019 11:14:39 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
資訊: Closing org.springframework.context.support.ClassPathXmlApplicationContext@707f7052: startup date [Mon Mar 04 23:14:38 CST 2019]; root of context hierarchy
【@preDestory】執行了...
《DisposableBean介面》destroy ....
【destory-method】方法執行了....

(9)Bean物件生命周期總結
1.如果實作了BeanFactoryPostProcessor介面,那么在容器啟動的時候,該介面中的postProcessBeanFactory方法可以修改Bean中元資料中的資訊,該方法是在實體化物件之前執行
2.如果實作了InstantiationAwareBeanPostProcessor介面,那么在實體化Bean物件之前會呼叫postProcessBeforeInstantiation方法,該方法如果回傳的不為null則會直接呼叫postProcessAfterInitialization方法,而跳過了Bean實體化后及初始化前的相關方法,如果回傳null則正常流程,postProcessAfterInstantiation在實體化成功后執行,這個時候物件已經被實體化,但是該實體的屬性還未被設定,都是null,因為它的回傳值是決定要不要呼叫postProcessPropertyValues方法的其中一個因素(因為還有一個因素是mbd.getDependencyCheck());如果該方法回傳false,并且不需要check,那么postProcessPropertyValues就會被忽略不執行;如果回傳true, postProcessPropertyValues就會被執行,postProcessPropertyValues用來修改屬性,在初始化方法之前執行,
3.如果實作了Aware相關的結果,那么相關的set方法會在初始化之前執行,
4.如果實作了BeanPostProcessor介面,那么該介面的方法會在實體化后的初始化方法前后執行,
5.如果實作了InitializingBean介面則在初始化的時候執行afterPropertiesSet
6.如果指定了init-method屬性則在初始化的時候會執行指定的方法,
7.如果指定了@PostConstruct則在初始化的時候會執行標注的方法,
8.到此物件創建完成
9.當物件需要銷毀的時候,
10.如果實作了DisposableBean介面會執行destroy方法
11.如果指定了destroy-method屬性則會執行指定的方法
12.如果指定了@PreDestroy注解則會執行標注的方法

10.在 Spring中如何注入一個java集合?

Spring提供以下幾種集合的配置元素:

型別用于注入一列值,允許有相同的值,

型別用于注入一組值,不允許有相同的值,

型別用于注入一組鍵值對,鍵和值都可以為任意型別,

型別用于注入一組鍵值對,鍵和值都只能為String型別,

12.什么是bean裝配?

裝配,或bean 裝配是指在Spring 容器中把bean組裝到一起,前提是容器需要知道bean的依賴關系,如何通過依賴注入來把它們裝配到一起,

13.什么是bean的自動裝配?

在Spring框架中,在組態檔中設定bean的依賴關系是一個很好的機制,Spring 容器能夠自動裝配相互合作的bean,這意味著容器不需要和配置,能通過Bean工廠自動處理bean之間的協作,這意味著 Spring可以通過向Bean Factory中注入的方式自動搞定bean之間的依賴關系,自動裝配可以設定在每個bean上,也可以設定在特定的bean上,

14.Sring 自動裝配 bean 有哪些方式?

Spring 容器能夠自動裝配 bean,也就是說,可以通過檢查 BeanFactory 的內容讓 Spring 自動決議 bean 的協作者,

自動裝配的不同模式:

  • no,這是默認設定,表示沒有自動裝配,應使用顯式 bean 參考進行裝配,
  • byName,它根據 bean 的名稱注入物件依賴項,它匹配并裝配其屬性與 XML 檔案中由相同名稱定義的 bean,
  • byType,它根據型別注入物件依賴項,如果屬性的型別與 XML 檔案中的一個 bean 名稱匹配,則匹配并裝配屬性,
  • constructor,它通過呼叫類的建構式來注入依賴項,它有大量的引數,
  • autodetect,首先容器嘗試通過建構式使用 autowire 裝配,如果不能,則嘗試通過 byType 自動裝配,

15.使用@Autowired注解自動裝配的程序是怎樣的?

使用@Autowired注解來自動裝配指定的bean,在使用@Autowired注解之前需要在Spring組態檔進行配置,<context:annotation-config />,

在啟動Spring IOC時,容器自動裝載了一個AutowiredAnnotationBeanPostProcessor后置處理器,當容器掃描到@Autowied、@Resource或@Inject時,就會在IoC容器自動查找需要的bean,并裝配給該物件的屬性,在使用@Autowired時,首先在容器中查詢對應型別的bean:

  • 如果查詢結果剛好為一個,就將該bean裝配給@Autowired指定的資料;

  • 如果查詢的結果不止一個,那么@Autowired會根據名稱來查找;

  • 如果上述查找的結果為空,那么會拋出例外,解決方法時,使用required=false,

16.自動裝配有哪些局限性?

自動裝配的局限性是:

重寫:你仍需用 和 配置來定義依賴,意味著總要重寫自動裝配,

基本資料型別:你不能自動裝配簡單的屬性,如基本資料型別,String字串,和類,

模糊特性:自動裝配不如顯式裝配精確,如果有可能,建議使用顯式裝配,

四、Spring注解

1.什么是基于Java的Spring注解配置? 給一些注解的例子

基于Java的配置,允許你在少量的Java注解的幫助下,進行你的大部分Spring配置而非通過XML檔案,

以@Configuration 注解為例,它用來標記類可以當做一個bean的定義,被Spring IOC容器使用,

另一個例子是@Bean注解,它表示此方法將要回傳一個物件,作為一個bean注冊進Spring應用背景關系,

@Configuration
public class StudentConfig {
    @Bean
    public StudentBean myStudent() {
        return new StudentBean();
    }
}

2.怎樣開啟注解裝配?

注解裝配在默認情況下是不開啟的,為了使用注解裝配,我們必須在Spring組態檔中配置 <context:annotation-config/>元素,

3.@Component, @Controller, @Repository, @Service 有何區別?

@Component:這將 java 類標記為 bean,它是任何 Spring 管理組件的通用構造型,spring 的組件掃描機制現在可以將其拾取并將其拉入應用程式環境中,

@Controller:這將一個類標記為 Spring Web MVC 控制器,標有它的 Bean 會自動匯入到 IoC 容器中,

@Service:此注解是組件注解的特化,它不會對 @Component 注解提供任何其他行為,您可以在服務層類中使用 @Service 而不是 @Component,因為它以更好的方式指定了意圖,

@Repository:這個注解是具有類似用途和功能的 @Component 注解的特化,它為 DAO 提供了額外的好處,它將 DAO 匯入 IoC 容器,并使未經檢查的例外有資格轉換為 Spring DataAccessException,

4.@Required 注解有什么作用

這個注解表明bean的屬性必須在配置的時候設定,通過一個bean定義的顯式的屬性值或通過自動裝配,若@Required注解的bean屬性未被設定,容器將拋出BeanInitializationException,示例:

public class Employee {
    private String name;
    @Required
    public void setName(String name){
        this.name=name;
    }
    public string getName(){
        return name;
    }
}

5.@Autowired 注解有什么作用

@Autowired默認是按照型別裝配注入的,默認情況下它要求依賴物件必須存在(可以設定它required屬性為false),@Autowired 注解提供了更細粒度的控制,包括在何處以及如何完成自動裝配,它的用法和@Required一樣,修飾setter方法、構造器、屬性或者具有任意名稱和/或多個引數的PN方法,

public class Employee {
    private String name;
    @Autowired
    public void setName(String name) {
        this.name=name;
    }
    public string getName(){
        return name;
    }
}

6.@Autowired和@Resource之間的區別

@Autowired可用于:建構式、成員變數、Setter方法

@Autowired和@Resource之間的區別

  • @Autowired默認是按照型別裝配注入的,默認情況下它要求依賴物件必須存在(可以設定它required屬性為false),

  • @Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照型別來裝配注入,

7.@Qualifier 注解有什么作用

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

8.@RequestMapping 注解有什么用?

@RequestMapping 注解用于將特定 HTTP 請求方法映射到將處理相應請求的控制器中的特定類/方法,此注釋可應用于兩個級別:

  • 類級別:映射請求的 URL
  • 方法級別:映射 URL 以及 HTTP 請求方法

五、Spring資料訪問

1.解釋物件/關系映射集成模塊

Spring 通過提供ORM模塊,支持我們在直接JDBC之上使用一個物件/關系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS,JPA,TopLink,JDO,OJB ,Spring的事務管理同樣支持以上所有ORM框架及JDBC,

2.在Spring框架中如何更有效地使用JDBC?

使用Spring JDBC 框架,資源管理和錯誤處理的代價都會被減輕,所以開發者只需寫statements 和 queries從資料存取資料,JDBC也可以在Spring框架提供的模板類的幫助下更有效地被使用,這個模板叫JdbcTemplate

3.解釋JDBC抽象和DAO模塊

通過使用JDBC抽象和DAO模塊,保證資料庫代碼的簡潔,并能避免資料庫資源錯誤關閉導致的問題,它在各種不同的資料庫的錯誤資訊之上,提供了一個統一的例外訪問層,它還利用Spring的AOP 模塊給Spring應用中的物件提供事務管理服務,

4.Spring DAO 有什么用?

Spring DAO(資料訪問物件) 使得 JDBC,Hibernate 或 JDO 這樣的資料訪問技術更容易以一種統一的方式作業,這使得用戶容易在持久性技術之間切換,它還允許您在撰寫代碼時,無需考慮捕獲每種技術不同的例外,

5.Spring JDBC API 中存在哪些類?

JdbcTemplate

SimpleJdbcTemplate

NamedParameterJdbcTemplate

SimpleJdbcInsert

SimpleJdbcCall

6.JdbcTemplate是什么

JdbcTemplate 類提供了很多便利的方法解決諸如把資料庫資料轉變成基本資料型別或物件,執行寫好的或可呼叫的資料庫操作陳述句,提供自定義的資料錯誤處理,

7.使用Spring通過什么方式訪問Hibernate?使用 Spring 訪問 Hibernate 的方法有哪些?

在Spring中有兩種方式訪問Hibernate:

  • 使用 Hibernate 模板和回呼進行控制反轉
  • 擴展 HibernateDAOSupport 并應用 AOP 攔截器節點

8.如何通過HibernateDaoSupport將Spring和Hibernate結合起來?

用Spring的 SessionFactory 呼叫 LocalSessionFactory,集成程序分三步:

  • 配置the Hibernate SessionFactory
  • 繼承HibernateDaoSupport實作一個DAO
  • 在AOP支持的事務中裝配

9.Spring支持的事務管理型別, spring 事務實作方式有哪些?

Spring支持兩種型別的事務管理:

編程式事務管理:這意味你通過編程的方式管理事務,給你帶來極大的靈活性,但是難維護,

宣告式事務管理:這意味著你可以將業務代碼和事務管理分離,你只需用注解和XML配置來管理事務,

10.Spring事務的實作方式和實作原理

Spring事務的本質其實就是資料庫對事務的支持,沒有資料庫的事務支持,spring是無法提供事務功能的,真正的資料庫層的事務提交和回滾是通過binlog或者redo log實作的,

11.Spring的事務傳播行為

一共有7種事務傳播行為:

(1)PROPAGATION_REQUIRED:這個是最常見的,就是說,如果ServiceA.method呼叫了ServiceB.method,如果ServiceA.method開啟了事務,然后ServiceB.method也宣告了事務,那么ServiceB.method不會開啟獨立事務,而是將自己的操作放在ServiceA.method的事務中來執行,ServiceA和ServiceB任何一個報錯都會導致整個事務回滾,這就是默認的行為,其實一般我們都是需要這樣子的,

(2)PROPAGATION_SUPPORTS:如果ServiceA.method開了事務,那么ServiceB就將自己加入ServiceA中來運行,如果ServiceA.method沒有開事務,那么ServiceB自己也不開事務

(3)PROPAGATION_MANDATORY:必須被一個開啟了事務的方法來呼叫自己,否則報錯

(4)PROPAGATION_REQUIRES_NEW:ServiceB.method強制性自己開啟一個新的事務,然后ServiceA.method的事務會卡住,等ServiceB事務完了自己再繼續,這就是影響的回滾了,如果ServiceA報錯了,ServiceB是不會受到影響的,ServiceB報錯了,ServiceA也可以選擇性的回滾或者是提交,

(5)PROPAGATION_NOT_SUPPORTED:就是ServiceB.method不支持事務,ServiceA的事務執行到ServiceB那兒,就掛起來了,ServiceB用非事務方式運行結束,ServiceA事務再繼續運行,這個好處就是ServiceB代碼報錯不會讓ServiceA回滾,

(6)PROPAGATION_NEVER:不能被一個事務來呼叫,ServiceA.method開事務了,但是呼叫了ServiceB會報錯

(7)PROPAGATION_NESTED:開啟嵌套事務,ServiceB開啟一個子事務,如果回滾的話,那么ServiceB就回滾到開啟子事務的這個save point,

大家回頭想想那個面試題,其實就是ServiceA里回圈51呼叫ServiceB,第51次呼叫ServiceB失敗了,第一個選項,就是兩個事務都設定為PROPAGATION_REQUIRED就好了,ServiceB的所有操作都加入了ServiceA啟動的一個大事務里去,任何一次失敗都會導致整個事務的回滾;第二個選項,就是將ServiceB設定為PROPAGATION_REQUIRES_NEW,這樣ServiceB的每次呼叫都在一個獨立的事務里執行,這樣的話,即使第51次報錯,但是僅僅只是回滾第51次的操作,前面50次都在獨立的事務里成功了,是不會回滾的,

其實一般也就PROPAGATION_REQUIRES_NEW比較常用,要的效果就是嵌套的那個事務是獨立的事務,自己提交或者回滾,不影響外面的大事務,外面的大事務可以獲取拋出的例外,自己決定是繼續提交大事務還是回滾大事務,

12.Spring 的事務隔離?

什么是事務的隔離性?
隔離性是指,多個用戶的并發事務訪問同一個資料庫時,一個用戶的事務不應該被其他用戶的事務干擾,多個并發事務之間要相互隔離,

咱們舉例子來說明:

建表陳述句:

CREATE TABLE `T`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = INNODB;

資料串列:

idname
1xiaohong
2zhangsan
3lisi

案例一:

事務A,先執行,處于未提交的狀態:

insert into T values(4, wangwu);

事務B,后執行,也未提交:

select * from T;

如果事務B能夠讀取到(4, wangwu)這條記錄,事務A就對事務B產生了影響,這個影響叫做“讀臟”,讀到了未提交事務操作的記錄,

案例二:

事務A,先執行:

select * from T where id=1;

結果集為:1, xiaohong

事務B,后執行,并且提交:

update T set name=hzy where id=1;

commit;

事務A,再次執行相同的查詢:

select * from T where id=1;

結果集為:1, hzy

這次是已提交事務B對事務A產生的影響,這個影響叫做“不可重復讀”,一個事務內相同的查詢,得到了不同的結果,

案例三:

事務A,先執行:

select * from T where id>3;

結果集為: NULL

事務B,后執行,并且提交:

insert into T values(4, wangwu);
commit;

事務A,首次查詢了id>3的結果為NULL,于是想插入一條為4的記錄:

insert into T values(4, hzy);

結果集為: Error : duplicate key!

這次是已提交事務B對事務A產生的影響,這個影響叫做“幻讀”,

可以看到,并發的事務可能導致其他事務:

  • 讀臟

  • 不可重復讀

  • 幻讀

InnoDB實作了四種不同事務的隔離級別:

  • 讀未提交(Read Uncommitted)
  • 讀提交(Read Committed, RC)
  • 可重復讀(Repeated Read, RR)
  • 串行化(Serializable)

不同事務的隔離級別,實際上是一致性與并發性的一個權衡與折衷,

InnoDB的四種事務的隔離級別,分別是怎么實作的?

InnoDB使用不同的鎖策略(Locking Strategy)來實作不同的隔離級別,

(1)讀未提交(Read Uncommitted)

這種事務隔離級別下,select陳述句不加鎖,

此時,可能讀取到不一致的資料,即“讀臟”,這是并發最高,一致性最差的隔離級別,

(2)串行化(Serializable)

這種事務的隔離級別下,所有select陳述句都會被隱式的轉化為select … in share mode.

這可能導致,如果有未提交的事務正在修改某些行,所有讀取這些行的select都會被阻塞住,

這是一致性最好的,但并發性最差的隔離級別, 在互聯網大資料量,高并發量的場景下,幾乎不會使用上述兩種隔離級別,

(3)可重復讀(Repeated Read, RR) 這是InnoDB默認的隔離級別,在RR下:

①普通的select使用快照讀(snapshot read),這是一種不加鎖的一致性讀(Consistent Nonlocking Read),底層使用MVCC來實作;

②加鎖的select(select … in share mode / select … for update), update, delete等陳述句,它們的鎖,依賴于它們是否在唯一索引(unique index)上使用了唯一的查詢條件(unique search condition),或者范圍查詢條件(range-type search condition):

  • 在唯一索引上使用唯一的查詢條件,會使用記錄鎖(record lock),而不會封鎖記錄之間的間隔,即不會使用間隙鎖(gap lock)與臨鍵鎖(next-key lock)
  • 范圍查詢條件,會使用間隙鎖與臨鍵鎖,鎖住索引記錄之間的范圍,避免范圍間插入記錄,以避免產生幻影行記錄,以及避免不可重復的讀

(4)讀提交(Read Committed, RC) 這是互聯網最常用的隔離級別,在RC下:

①普通讀是快照讀;

②加鎖的select, update, delete等陳述句,除了在外鍵約束檢查(foreign-key constraint checking)以及重復鍵檢查(duplicate-key checking)時會封鎖區間,其他時刻都只使用記錄鎖;

此時,其他事務的插入依然可以執行,就可能導致,讀取到幻影記錄,

13.Spring框架的事務管理有哪些優點?

  • 為不同的事務API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一個不變的編程模式,
  • 為編程式事務管理提供了一套簡單的API而不是一些復雜的事務API
  • 支持宣告式事務管理,
  • 和Spring各種資料訪問抽象層很好得集成,

14.你更傾向用那種事務管理型別?

大多數Spring框架的用戶選擇宣告式事務管理,因為它對應用代碼的影響最小,因此更符合一個無侵入的輕量級容器的思想,宣告式事務管理要優于編程式事務管理,雖然比編程式事務管理(這種方式允許你通過代碼控制事務)少了一點靈活性,唯一不足地方是,最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別,

六、Spring面向切面編程(AOP)

1.什么是AOP?

OOP即面向物件的程式設計,談起了OOP,我們就不得不了解一下POP即面向程序程式設計,它是以功能為中心來進行思考和組織的一種編程方式,強調的是系統的資料被加工和處理的程序,說白了就是注重功能性的實作,效果達到就好了,而OOP則注重封裝,強調整體性的概念,以物件為中心,將物件的內部組織與外部環境區分開來,之前看到過一個很貼切的解釋,博主把它們畫成一幅圖如下:
在這里插入圖片描述
在這里我們暫且把程式設計比喻為房子的布置,一間房子的布局中,需要各種功能的家具和潔具(類似方法),如馬桶、浴缸、天然氣灶,床、桌子等,對于面向程序的程式設計更注重的是功能的實作(即功能方法的實作),效果符合預期就好,因此面向程序的程式設計會更傾向圖1設定結構,各種功能都已實作,房子也就可以正常居住了,但對于面向物件的程式設計則是無法忍受的,這樣的設定使房子內的各種家具和潔具間擺放散亂并且相互暴露的機率大大增加,各種氣味相互參雜,顯然是很糟糕的,于是為了更優雅地設定房屋的布局,面向物件的程式設計便采用了圖2的布局,對于面向物件程式設計來說這樣設定好處是顯而易見的,房子中的每個房間都有各自的名稱和相應功能(在java程式設計中一般把類似這樣的房間稱為類,每個類代表著一種房間的抽象體),如衛生間是大小解和洗澡梳妝用的,臥室是休息用的,廚房則是做飯用的,每個小房間都各司其職并且無需時刻向外界暴露內部的結構,整個房間結構清晰,外界只需要知道這個房間并使用房間內提供的各項功能即可(方法呼叫),同時也更有利于后期的拓展了,畢竟哪個房間需要添加那些功能,其范圍也有了限制,也就使職責更加明確了(單一責任原則),OOP的出現對POP確實存在很多顛覆性的,但并不能說POP已沒有價值了,畢竟只是不同時代的產物,從方法論來講,更喜歡將面向程序與面向物件看做是事物的兩個方面–區域與整體(你必須要注意到區域與整體是相對的),因此在實際應用中,兩者方法都同樣重要,了解完OOP和POP各自的特點,接著看java程式設計程序中OOP應用,在java程式設計程序中,我們幾乎享盡了OOP設計思想帶來的甜頭,以至于在這個一切皆物件,眾生平等的世界里,狂歡不已,而OOP確實也遵循自身的宗旨即將資料及對資料的操作行為放在一起,作為一個相互依存、不可分割的整體,這個整體美其名曰:物件,利用該定義對于相同型別的物件進行分類、抽象后,得出共同的特征,從而形成了類,在java程式設計中這些類就是class,由于類(物件)基本都是現實世界存在的事物概念(如前面的不同的小房間)因此更接近人們對客觀事物的認識,同時把資料和方法(演算法)封裝在一個類(物件)中,這樣更有利于資料的安全,一般情況下屬性和演算法只單獨屬于某個類,從而使程式設計更簡單,也更易于維護,基于這套理論思想,在實際的軟體開發中,整個軟體系統事實也是由系列相互依賴的物件所組成,而這些物件也是被抽象出來的類,相信大家在實際開發中是有所體驗的(本篇檔案假定讀者已具備面向物件的開發思想包括封裝、繼承、多型的知識點),但隨著軟體規模的增大,應用的逐漸升級,慢慢地,OOP也開始暴露出一些問題,現在不需要急于知道它們,通過案例,我們慢慢感受,

A類:

public class A {
    public void executeA(){
        //其他業務操作省略......
        recordLog();
    }

    public void recordLog(){
        //....記錄日志并上報日志系統
    }
}

B類:

public class B {
    public void executeB(){
        //其他業務操作省略......
        recordLog();
    }

    public void recordLog(){
        //....記錄日志并上報日志系統
    }
}

C類:

public class C {
    public void executeC(){
        //其他業務操作省略......
        recordLog();
    }

    public void recordLog(){
        //....記錄日志并上報日志系統
    }
}

假設存在A、B、C三個類,需要對它們的方法訪問進行日志記錄,在代碼中各種存在recordLog方法進行日志記錄并上報,或許對現在的工程師來說幾乎不可能寫出如此糟糕的代碼,但在OOP這樣的寫法是允許的,而且在OOP開始階段這樣的代碼確實并大量存在著,直到工程師實在忍受不了一次修改,到處挖墳時(修改recordLog內容),才下定決心解決該問題,為了解決程式間過多冗余代碼的問題,工程師便開始使用下面的編碼方式,

//通用父類
public class Dparent {
    public void commond(){
        //通用代碼
    }
}
//A 繼承 Dparent 
public class A extends Dparent {
    public void executeA(){
        //其他業務操作省略......
        commond();
    }
}
//B 繼承 Dparent 
public class B extends Dparent{
    public void executeB(){
        //其他業務操作省略......
        commond();
    }
}
//C 繼承 Dparent 
public class C extends Dparent{
    public void executeC(){
        //其他業務操作省略......
        commond();
    }
}

顯然代碼冗余也得到了解決,這種通過繼承抽取通用代碼的方式也稱為縱向拓展,與之對應的還有橫向拓展(現在不需急于明白,后面的分析中它將隨處可見),事實上有了上述兩種解決方案后,在大部分業務場景的代碼冗余問題也得到了實實在在的解決,原理如下圖
在這里插入圖片描述
但是隨著軟體開發的系統越來越復雜,工程師認識到,傳統的OOP程式經常表現出一些不自然的現象,核心業務中總摻雜著一些不相關聯的特殊業務,如日志記錄,權限驗證,事務控制,性能檢測,錯誤資訊檢測等等,這些特殊業務可以說和核心業務沒有根本上的關聯而且核心業務也不關心它們,比如在用戶管理模塊中,該模塊本身只關心與用戶相關的業務資訊處理,至于其他的業務完全可以不理會,我們看一個簡單例子協助理解這個問題

public interface IUserService {

    void saveUser();

    void deleteUser();

    void findAllUser();
}
//實作類
public class UserServiceImpl implements IUserService {

    //核心資料成員

    //日志操作物件

    //權限管理物件

    //事務控制物件

    @Override
    public void saveUser() {

        //權限驗證(假設權限驗證丟在這里)

        //事務控制

        //日志操作

        //進行Dao層操作
        userDao.saveUser();

    }

    @Override
    public void deleteUser() {

    }

    @Override
    public void findAllUser() {

    }
}

上述代碼中我們注意到一些問題,權限,日志,事務都不是用戶管理的核心業務,也就是說用戶管理模塊除了要處理自身的核心業務外,還需要處理權限,日志,事務等待這些雜七雜八的不相干業務的外圍操作,而且這些外圍操作同樣會在其他業務模塊中出現,這樣就會造成如下問題

  • 代碼混亂:核心業務模塊可能需要兼顧處理其他不相干的業務外圍操作,這些外圍操作可能會混亂核心操作的代碼,而且當外圍模塊有重大修改時也會影響到核心模塊,這顯然是不合理的,
  • 代碼分散和冗余:同樣的功能代碼,在其他的模塊幾乎隨處可見,導致代碼分散并且冗余度高,
  • 代碼質量低擴展難:由于不太相關的業務代碼混雜在一起,無法專注核心業務代碼,當進行類似無關業務擴展時又會直接涉及到核心業務的代碼,導致拓展性低,

顯然前面分析的兩種解決方案已束手無策了,那么該如何解決呢?事實上我們知道諸如日志,權限,事務,性能監測等業務幾乎涉及到了所有的核心模塊,如果把這些特殊的業務代碼直接到核心業務模塊的代碼中就會造成上述的問題,而工程師更希望的是這些模塊可以實作熱插拔特性而且無需把外圍的代碼入侵到核心模塊中,這樣在日后的維護和擴展也將會有更佳的表現,假設現在我們把日志、權限、事務、性能監測等外圍業務看作單獨的關注點(也可以理解為單獨的模塊),每個關注點都可以在需要它們的時刻及時被運用而且無需提前整合到核心模塊中,這種形式相當下圖:
在這里插入圖片描述
從圖可以看出,每個關注點與核心業務模塊分離,作為單獨的功能,橫切幾個核心業務模塊,這樣的做的好處是顯而易見的,每份功能代碼不再單獨入侵到核心業務類的代碼中,即核心模塊只需關注自己相關的業務,當需要外圍業務(日志,權限,性能監測、事務控制)時,這些外圍業務會通過一種特殊的技術自動應用到核心模塊中,這些關注點有個特殊的名稱,叫做“橫切關注點”,上圖也很好的表現出這個概念,另外這種抽象級別的技術也叫AOP(面向切面編程),正如上圖所展示的橫切核心模塊的整面,因此AOP的概念就出現了,而所謂的特殊技術也就面向切面編程的實作技術,AOP的實作技術有多種,其中與Java無縫對接的是一種稱為AspectJ的技術,那么這種切面技術(AspectJ)是如何在Java中的應用呢?不必擔心,也不必全面了解AspectJ,本篇博文也不會這樣進行,對于AspectJ,我們只會進行簡單的了解,從而為理解Spring中的AOP打下良好的基礎(Spring AOP 與AspectJ 實作原理上并不完全一致,但功能上是相似的,這點后面會分析),畢竟Spring中已實作AOP主要功能,開發中直接使用Spring中提供的AOP功能即可,除非我們想單獨使用AspectJ的其他功能,這里還需要注意的是,AOP的出現確實解決外圍業務代碼與核心業務代碼分離的問題,但它并不會替代OOP,如果說OOP的出現是把編碼問題進行模塊化,那么AOP就是把涉及到眾多模塊的某一類問題進行統一管理,因此在實際開發中AOP和OOP同時存在并不奇怪,后面將會慢慢體會帶這點,好的,已迫不及待了,讓我們開始了解神一樣的AspectJ吧,

2.Spring AOP and AspectJ AOP 有什么區別?AOP 有哪些實作方式?

AOP實作的關鍵在于 代理模式,AOP代理主要分為靜態代理和動態代理,靜態代理的代表為AspectJ;動態代理則以Spring AOP為代表,

(1)AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將AspectJ(切面)織入到Java位元組碼中,運行的時候就是增強之后的AOP物件,

(2)Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改位元組碼,而是每次運行時在記憶體中臨時為方法生成一個AOP物件,這個AOP物件包含了目標物件的全部方法,并且在特定的切點做了增強處理,并回呼原物件的方法,

3.JDK動態代理和CGLIB動態代理的區別

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

  • JDK動態代理只提供介面的代理,不支持類的代理,核心InvocationHandler介面和Proxy類,InvocationHandler 通過invoke()方法反射來呼叫目標類中的代碼,動態地將橫切邏輯和業務編織在一起;接著,Proxy利用 InvocationHandler動態創建一個符合某一介面的的實體, 生成目標類的代理物件,

  • 如果代理類沒有實作 InvocationHandler 介面,那么Spring AOP會選擇使用CGLIB來動態代理目標類,CGLIB(Code Generation Library),是一個代碼生成的類別庫,可以在運行時動態的生成指定類的一個子類物件,并覆寫其中特定方法并添加增強代碼,從而實作AOP,CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態代理的,

靜態代理與動態代理區別在于生成AOP代理物件的時機不同,相對來說AspectJ的靜態代理方式具有更好的性能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理,

InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最終生成的代理實體; method 是被代理目標實體的某個具體方法; args 是被代理目標實體某個方法的具體入參, 在方法反射呼叫時使用,

4.如何理解 Spring 中的代理?

將 Advice 應用于目標物件后創建的物件稱為代理,在客戶端物件的情況下,目標物件和代理物件是相同的,

Advice + Target Object = Proxy

5.解釋一下Spring AOP里面的幾個名詞

(1)切面(Aspect):切面是通知和切點的結合,通知和切點共同定義了切面的全部內容, 在Spring AOP中,切面可以使用通用類(基于模式的風格) 或者在普通類中以 @AspectJ 注解來實作,

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

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

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

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

(6)目標物件(Target Object): 被一個或者多個切面(aspect)所通知(advise)的物件,它通常是一個代理物件,也有人把它叫做 被通知(adviced) 物件, 既然Spring AOP是通過運行時代理實作的,這個物件永遠是一個 被代理(proxied) 物件,

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

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

6.Spring在運行時通知物件

通過在代理類中包裹切面,Spring在運行期把切面織入到Spring管理的bean中,代理封裝了目標類,并攔截被通知方法的呼叫,再把呼叫轉發給真正的目標bean,當代理攔截到方法呼叫時,在呼叫目標bean方法之前,會執行切面邏輯,

直到應用需要被代理的bean時,Spring才創建代理物件,如果使用的是ApplicationContext的話,在ApplicationContext從BeanFactory中加載所有bean的時候,Spring才會創建被代理的物件,因為Spring運行時才創建代理物件,所以我們不需要特殊的編譯器來織入SpringAOP的切面,

7.Spring只支持方法級別的連接點

因為Spring基于動態代理,所以Spring只支持方法連接點,Spring缺少對欄位連接點的支持,而且它不支持構造器連接點,方法之外的連接點攔截功能,我們可以利用Aspect來補充,

8.在Spring AOP 中,關注點和橫切關注的區別是什么?在 spring aop 中 concern 和 cross-cutting concern 的不同之處

關注點(concern)是應用中一個模塊的行為,一個關注點可能會被定義成一個我們想實作的一個功能,

橫切關注點(cross-cutting concern)是一個關注點,此關注點是整個應用都會使用的功能,并影響整個應用,比如日志,安全和資料傳輸,幾乎應用的每個模塊都需要的功能,因此這些都屬于橫切關注點,

9.Spring通知有哪些型別?

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

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

同一個aspect,不同advice的執行順序:

①沒有例外情況下的執行順序:

around before advice
before advice
target method 執行
around after advice
after advice
afterReturning

②有例外情況下的執行順序:

around before advice
before advice
target method 執行
around after advice
after advice
afterThrowing:例外發生
java.lang.RuntimeException: 例外發生

10.什么是切面 Aspect?

aspect 由 pointcount 和 advice 組成,切面是通知和切點的結合, 它既包含了橫切邏輯的定義, 也包括了連接點的定義. Spring AOP 就是負責實施切面的框架, 它將切面所定義的橫切邏輯編織到切面所指定的連接點中.
AOP 的作業重心在于如何將增強編織目標物件的連接點上, 這里包含兩個作業:

  • 如何通過 pointcut 和 advice 定位到特定的 joinpoint 上
  • 如何在 advice 中撰寫切面代碼.

可以簡單地認為, 使用 @Aspect 注解的類就是切面.

在這里插入圖片描述

11.解釋基于XML Schema方式的切面實作

在這種情況下,切面由常規類以及基于XML的配置實作,

12.解釋基于注解的切面實作

在這種情況下(基于@AspectJ的實作),涉及到的切面宣告的風格與帶有java5標注的普通java類一致,

13.有幾種不同型別的自動代理?

BeanNameAutoProxyCreator

DefaultAdvisorAutoProxyCreator

Metadata autoproxying

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

標籤:java

上一篇:??導圖整理大廠面試高頻陣列8: 移除元素的雙指標優化, 力扣27??

下一篇:CGBTN2108_DAY01總結復習

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