Spring框架面試總結

Spring章節中本篇中,IOC/DI 的概念是基礎,主要面試點在於,spring的工作原理、AOP和事物方面知識點。一般還會問spring中的設計模式等。

Spring

​ Spring是一個輕量級的IoC和AOP容器框架。是爲Java應用程序提供基礎性服務的一套框架,目的是用於簡化企業應用程序的開發,它使得開發者只需要關心業務需求。


1、Spring 的優勢

​ 輕量級的 DI / IoC 和 AOP 容器的開源框架

  • 低侵入 / 低耦合 (降低組件之間的耦合度,實現軟件各層之間的解耦)
  • 支持聲明式事務管理(基於切面和慣例)
  • 方便集成其他框架(如MyBatis、Hibernate)
  • 降低 Java 開發難度

2、Spring Bean

1> SpringBean 3種創建方式

BeanFactory 創建bean

BeanFactory factory = new XmlBeanFactory( new ClassPathResource ( "com/hsp/beans.xml" ) ) ;
factory.getBean("beanId");

優點:節約內存。缺點:速度慢。

ApplicationContext 創建bean

優點:預先加載,使用的時候速度快;缺點:耗內存,所有的bean都被實例化了

1:調用構造器創建Bean

package com.itheima.spring.helloword;
public class Helloword {
	public Helloword() {
		System.out.println("new instance");
	}
	public void hello() {
		System.out.println("hello word");
	}
}
<bean id="helloWorld" class="com.itheima.spring.helloword.Helloword"></bean>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld");

2:調用靜態工廠方法創建Bean

<bean id="helloWorld2" class="com.itheima.spring.helloword.HelloWordFactory"  factory-method="getInstance"></bean>
public class HelloWordFactory {
	public static Helloword getInstance() {
		return new Helloword();
	}
}
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld2");
helloword.hello();

3:調用實例工廠方法創建Bean

<bean id="helloWorldFactory" class="com.itheima.spring.helloword.HelloWordFactory2"></bean>	
<bean id="helloWorld3" factory-bean="helloWorldFactory" factory-method="getInstance" ></bean>

public class HelloWordFactory2 {
	public Helloword getInstance() {
		return new Helloword();
	}
}

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld3");
helloword.hello();

Spring的beanFactory和factoryBean的區別

  • BeanFactory是接口類,表示它是一個工廠類,用於管理Bean的一個工廠

  • FactoryBean也是接口;

    • 不是簡單的Bean,是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似

    • 3個方法:getObject()、isSingleton()、getObjectType()

    • 如果希望獲取CarFactoryBean的實例,則需要在使用getBean(beanName)方法時在beanName前顯示的加上"&“前綴:如getBean(”&car");


2> Spring Bean 3種配置方式

  • 基於XML配置

    • 當Bean的實現類來源於第三方類庫,比如DataSource、HibernateTemplate等,無法在類中標註註解信息,只能通過XML進行配置;而且命名空間的配置,比如aop、context等,也只能採用基於XML的配置。
  • 基於註解配置

    • @Component:標註一個普通的Spring Bean類(可以指定Bean名稱,未指定時默認爲小寫字母開頭的類名)
    • @Controller:標註一個控制器類
    • @Service:標註一個業務邏輯類
    • @Repository:標註一個DAO類
  • 基於java反射機制

    • 註解:@Configuration、@Bean、@DependsOn、@Primary、@Lazy、@Import、@ImportResource、@Value

    • 
      @Configuration
      public class Config {
        @Bean
        public MyService myService() {
            return new MyServiceImpl();
        }
      }
      //測試類
      ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
      MyService myService = ctx.getBean(MyService.class);
      myService.sayHello();
      

3> Spring Bean 5種作用域

singleton、prototype、request、session和globalSession。

  • singleton:定義bean的範圍爲每個spring容器一個實例(默認值);
  • prototype:定義bean可以被多次實例化(使用一次就創建一次);
  • request:定義bean的範圍是http請求(SpringMVC中有效);
  • session:定義 bean的範圍是http會話(SpringMVC)中有效;
  • global-session:定義bean的範圍是全局http會話(portlet)中有效。

4> Spring Bean的生命週期

  • 實例化Bean:

    對於BeanFactory容器,當客戶向容器請求一個尚未初始化的bean時,或初始化bean的時候需要注入另一個尚未初始化的依賴時,容器就會調用createBean進行實例化。對於ApplicationContext容器,當容器啓動結束後,通過獲取BeanDefinition對象中的信息,實例化所有的bean。

  • 設置對象屬性(依賴注入):

    實例化後的對象被封裝在BeanWrapper對象中,緊接着,Spring根據BeanDefinition中的信息 以及 通過BeanWrapper提供的設置屬性的接口完成依賴注入。

  • 處理Aware接口:

    接着,Spring會檢測該對象是否實現了xxxAware接口,並將相關的xxxAware實例注入給Bean:

    ​ ①如果這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的就是Spring配置文件中Bean的id值;

    ​ ②如果這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory()方法,傳遞的是Spring工廠自身。

    ​ ③如果這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文;

  • BeanPostProcessor:

    如果想對Bean進行一些自定義的處理,那麼可以讓Bean實現了BeanPostProcessor接口,那將會調用postProcessBeforeInitialization(Object obj, String s)方法。由於這個方法是在Bean初始化結束時調用的,所以可以被應用於內存或緩存技術;

  • InitializingBean 與 init-method:

    如果Bean在Spring配置文件中配置了 init-method 屬性,則會自動調用其配置的初始化方法。

    如果這個Bean實現了BeanPostProcessor接口,將會調用postProcessAfterInitialization(Object obj, String s)方法;

  • 以上幾個步驟完成後,Bean就已經被正確創建了,之後就可以使用這個Bean了。
  • DisposableBean:

    當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean這個接口,會調用其實現的destroy()方法;

  • **destroy-method:**最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。


3、IOC 和DI

1> IOC:控制反轉

  • 對象的創建交給外部容器完成,這個就做控制反轉

實現原理

  • IOC 就是通過反射技術,根據給出的類名(字符串方式)來動態地生成對象

  • IOC容器相當於一個工廠模式

2> DI:依賴注入

  • 由容器動態的將某個依賴關係注入到組件之中,(3種注入方式:構造器注入、setter方法注入、註解注入。
//使用構造函數
public Teacher(Course course,int age){
    this.course = course;
    this.age = age;
}
<bean id="teacher" class="com.niit.DI2.Teacher">
    <!-- 構造函數方式注入的時候必須配置構造函數的參數 -->
    <constructor-arg name="course" ref="course"></constructor-arg>
    <constructor-arg name="age" value="20"></constructor-arg>
</bean>
//setter方法注入
public void setCourse(Course course) {
	this.course = course;
}
<bean id="student" class="com.niit.DI.Student">
    <!-- 以下代碼跟Student類中的set方法相關 -->
    <!-- name就寫Student中被注入變量的名稱 -->
    <property name="course" ref="course" ></property>
    <property name="age" value="20"></property>
</bean>

3> 循環注入

  • 循環依賴就是循環引用,就是兩個或多個Bean相互之間的持有對方
  • 構造器循環依賴 :只能拋出BeanCurrentlyInCreationException異常表示循環依賴。
  • setter方法循環注入:分單例(scope=“singleton”)和非單例模式(scope=“prototype”)的情況
    • 多例模式: 因爲prototype作用域的Bean,Spring容器不進行緩存,因此無法提前暴露一個創建中的Bean。會拋出異常
    • 單例模式:通過Spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的Bean來完成的,而且只能解決單例作用域的Bean循環依賴。

4、切面編程(AOP)

AOP

  • 通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術
  • 利用AOP可以對業務邏輯的各個部分進行隔離,降低耦合度,提高程序的可重用性,同時提高了開發的效率;
  • 主要功能:日誌記錄,性能統計,安全控制,事務處理,異常處理等等。
  • Struts2的攔截器 設計就是基於AOP的思想,是個比較經典的例子。

**AOP實現的3種方式:**XML、註解、動態代理

  • 基於XML

    • //目標對象
      public class BraveKnight {
          public void saying(){
              System.out.println("我是騎士");
          }
      }
      //切面類
      public class Minstrel {
          public void beforSay(){
              System.out.println("前置通知");
          }
          public void afterSay(){
              System.out.println("後置通知");
          }
      }
      
      <!-- 目標對象 -->
      <bean id="knight" class="com.cjh.aop2.BraveKnight"/>
      <!-- 切面bean ;定義通知內容,也就是切入點執行前後需要做的事情 -->
      <bean id="mistrel" class="com.cjh.aop2.Minstrel"/>
      <!-- 面向切面編程 -->
      <aop:config>
          <aop:aspect ref="mistrel">
              <!-- 定義切點 -->
              <aop:pointcut expression="execution(* *.saying(..))" id="embark"/>
              <!-- 聲明前置通知 (在切點方法被執行前調用)-->
              <aop:before method="beforSay" pointcut-ref="embark"/>
              <!-- 聲明後置通知 (在切點方法被執行後調用)-->
              <aop:after method="afterSay" pointcut-ref="embark"/>
          </aop:aspect>
      </aop:config>
      
      
  • 基於註解

    • <!-- 掃描 -->
      <context:component-scan base-package="com.linjie.aop"></context:component-scan>  
      <!-- 使AspectJ註解自動爲匹配的類生成代理對象 -->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
      //目標對象
      @Component("arithmetic")
      public class Arithmetic  {
          public void add(int d,int e) {
              System.out.println("add method ! a+b="+(d+e));
          }
      }
      //切面類
      @Component("logginAspectJ")
      @Aspect
      public class LogginAspectJ  {
          /*
           *定義一個方法,用於聲明切點表達式,該方法一般沒有方法體
           *@Pointcut用來聲明切點表達式
           *通知直接使用定義的方法名即可引入當前的切點表達式 
           */
          @Pointcut("execution(* com.hsy.aop.Arithmetic.*(..))")
          public void PointcutDeclaration() {}
          //前置通知,方法執行之前執行
          @Before("PointcutDeclaration()")
          public void beforAdd(){
              System.out.println("前置通知");
          }
          //後置通知,方法執行之後執行(不管是否發生異常)
          @After("PointcutDeclaration()")
          public void afterAdd(){
              System.out.println("後置通知");
          }
      }
      
      
  • 基於代理:(靜態代理、cglib 代理、JDK動態代理:代碼實現見設計模式—代理模式)

    • 靜態代理:被代理對象與代理對象一起實現相同的接口或者是繼承相同父類.
    • jdk動態代理:java.lang.reflect.Proxy.newProxyInstance() 和InvocationHandler對象
    • cglib動態代理:實現MethodInterceptor 接口,重寫intercept 方法

AOP編程中:如果加入容器的目標對象有實現接口,用JDK代理;如果目標對象沒有實現接口,用Cglib代理

5種通知類型: 前置、後置、環繞、異常、最後

  • 前置通知(before advice) 在某個連接點(jion point)之前執行的通知,但不能阻止連接點前的執行(除非拋出一個異常)
  • 後置通知(after returning advice)在某個連接點(jion point)正常執行完後執行通知
  • 異常通知(after throwing advice) 在方法異常退出時執行的通知
  • 最終通知(after(finally) advice)在方法退出時候的執行通知(不管正常返回還是異常退出)
  • 環繞通知(around advice) 包圍一個連接點(jion point)的通知

AOP核心概念

  • 切面(Aspect): 一個關注點的模塊化,這個關注點可能會橫切多個對象
  • 連接點(Joinpoint) :程序執行過程中某個特定的連接點
  • 通知(Advice) :在切面的某個特的連接點上執行的動作
  • 切入點(Pointcut) :匹配連接的斷言,在Aop中通知和一個切入點表達式關聯
  • 引入(Intruduction) : 在不修改類代碼的前提下,爲類添加新的方法和屬性
  • 目標對象(Target Object) : 被一個或者多個切面所通知的對象
  • Aop代理(AOP Proxy) : AOP框架創建的對象,用來實現切面契約(aspect contract)(包括方法執行等)
  • 織入(Weaving) :把切面連接到其他的應用程序類型或者對象上,並創建一個被通知的對象,氛圍:編譯時織入,類加載時織入。執行時織入

5、Spring 聲明式事物

事務的兩種聲明方式: xml、註解(@Transactional )

1> 事務隔離級別

Spring中支持五種事務隔離級別:

默認隔離級別、未提交讀、提交讀(Oracle、sqlserver 默認)可重複讀(Mysql 默認)串行化(級別最高)

  • ISOLATION_DEFAULT:數據庫默認隔離級別,(Mysql 默認:可重複讀,Oracle 默認:讀已提交)
  • ISOLATION_READ_UNCOMMITTED:未提交讀;讀取到沒有被提交的數據
  • ISOLATION_READ_COMMITTED:提交讀(Oracle\sqlserver 默認),讀到那些已經提交的數據
  • ISOLATION_REPEATABLE_READ(repeatable):可重複讀(Mysql 默認);這個事務不結束,別的事務就不可以改這條記錄,加鎖
  • SERIALIZABLE(serializable):串行化的 ,運行完一個事務的所有子事務之後纔可以執行另外一個事務

2> 事物的傳播途徑

Spring共有7種傳播途徑:propagation_

  • 支持當前事務

    • required:支持當前事務,如果不存在 就新建一個(默認)
    • supports:支持當前事務,如果不存在,就不使用事務
    • mandatory(強制):支持當前事務,如果不存在,拋出異常
  • 不支持當前事務

    • requires_new:如果有事務存在,掛起當前事務,創建一個新的事務
    • not_supported:如果有事務存在,掛起當前事務 ,如果不存在以非事務方式運行
    • never:如果有事務存在,拋出異常 ,如果不存在以非事務方式運行
    • nested:如果當前事務存在,則嵌套事務執行(他的提交是要等和他的父事務一塊提交的,父回滾他回滾)

3> 事務實現原理

  • 事務聲明默認使用 AOP 代理;
  • 如果使用 this.xxx 的方式,事物會失效。this 此時不是代理類的對象。
  • 解決方法:1.注入自身,使用代理對象調用;2.使用AopContext,獲取當前代理對象;3.使用BeanFactory獲取代理對象。

4> 事務屬性

Transactional 的事務屬性 (如級別、回滾、傳播途徑、時長等)

  • @isolation:用於指定事務的隔離級別。默認爲底層事務的隔離級別
  • @noRollbackFor:指定遇到特定異常時不回滾事務
  • @noRollbackForClassName:指定遇到特定的多個異常時不回滾事務
  • @propagation:指定事務傳播行爲
  • @readOnly:指定事務是否可讀
  • @rollbackFor:指定遇到特定異常時回滾事務
  • @rollbackForClassName:指定遇到特定的多個異常時回滾事務
  • @timeout:指定事務的超長時長。

6、Spring 註解

@Controller, @Service, @Repository,@Component

@RequestMapping("/test")

bean的註解

  • @Controller 在展現層使用,控制層的聲明
  • @Service 在業務邏輯層使用(service層)
  • @Repository 在數據訪問層使用(dao層)
  • @Component 組件:沒有明確的角色

屬性注入註解

  • @Autowired:按照類型(byType)裝配依賴對象;和Qualifier(name=" ")一起用;

  • @Resource:默認按 byName自動注入

異步註解

  • EnableAsync :開啓對異步任務的支持,作用在類級別

  • Async :申明其是一個異步任務,作用於方法

配置類相關注解

  • @Configuration :聲明當前類爲配置類,相當於xml形式的Spring配置(類上)
  • @Bean :註解在方法上,聲明當前方法的返回值爲一個bean,替代xml中的方式(方法上)
  • @ComponentScan : 用於對Component進行掃描,相當於xml中的(類上)
  • @WishlyConfiguration :爲@Configuration與@ComponentScan的組合註解,可以替代這兩個註解

AOP相關注解

  • @Aspect :聲明一個切面(類上) ;使用@After、@Before、@Around定義建言(advice),可直接將攔截規則(切點)作爲參數。
  • @After 在方法執行之後執行(方法上)
  • @Before 在方法執行之前執行(方法上)
  • @Around 在方法執行之前與之後執行(方法上)
  • @PointCut 聲明切點 在java配置類中使用@EnableAspectJAutoProxy註解開啓Spring對AspectJ代理的支持(類上)

7、Spring 的設計模式

(1)工廠模式:BeanFactory就是簡單工廠模式的體現,用來創建對象的實例;

(2)單例模式:Bean默認爲單例模式。

(3)代理模式:Spring的AOP功能用到了JDK的動態代理和CGLIB字節碼生成技術;

(4)模板方法:用來解決代碼重複的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。

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


8、spring常見jar包

  • spring-core.jar:包含spring框架基本的核心工具類,其他組件都要使用這個包裏面的類,是其他組件的核心;
  • spring-bean.jar:是所有的應用都要用到的,包含訪問配置文件、創建和管理bean以及進行IoC和DI操作所需的相關類;
  • spring-web.jar:包含web開發時,用到spring框架時所需的核心類;
  • spring-webmvc.jar:包含 Spring MVC框架相關的所有類;
  • spring-aop.jar:包含使用AOP特性時所需的類;
  • spring-dao.jar:包含spring DAO、spring Transaction進行數據訪問的所有類;
  • spring-hibernate.jar:包含spring對hibernate 2以及hibernate 3進行封裝的所有類;
  • spring-jdbc.jar:包含spring對JDBC數據庫訪問進行封裝的所有類;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章