<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                上一課時我們講了 Bean 相關的內容,它其實也是屬于 IoC 的具體實現之一,本課時我們就來講講 Spring 中其他幾個高頻的面試點,希望能起到拋磚引玉的作用,能為你理解 Spring 打開一扇門。因為 Spring 涉及的內容和知識點太多了,用它來寫一本書也綽綽有余,因此這里我們只講核心的內容,希望下來你能查漏補缺,完善自己的 Spring 技術棧。 我們本課時的面試題是,談一談你對 IoC 和 DI 的理解。 #### 典型回答 IoC(Inversion of Control,翻譯為“控制反轉”)不是一個具體的技術,而是一種設計思想。與傳統控制流相比,IoC 會顛倒控制流,在傳統的編程中需要開發者自行創建并銷毀對象,而在 IoC 中會把這些操作交給框架來處理,這樣開發者就不用關注具體的實現細節了,拿來直接用就可以了,這就是控制反轉。 IoC 很好的體現出了面向對象的設計法則之一——好萊塢法則:“別找我們,我們找你”。即由 IoC 容器幫對象找到相應的依賴對象并注入,而不是由對象主動去找。 舉個例子,比如說傳統找對象,先要設定好你的要求,如身高、體重、長相等,然后再一個一個的主動去找符合要求的對象,而 IoC 相當于,你把這些要求直接告訴婚介中心,由他們直接給你匹配到符合要求的對象,理想情況下是直接會幫你找到合適的對象,這就是傳統編程模式和 IoC 的區別。 DI(Dependency Injection,翻譯為“依賴注入”)表示組件間的依賴關系交由容器在運行期自動生成,也就是說,由容器動態的將某個依賴關系注入到組件之中,這樣就能提升組件的重用頻率。通過依賴注入機制,我們只需要通過簡單的配置,就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心資源來自哪里、由誰實現等問題。 IoC 和 DI 其實是同一個概念從不同角度的描述的,由于控制反轉這個概念比較含糊(可能只理解成了容器控制對象這一個層面,很難讓人想到誰來維護對象關系),所以 2004 年被開發者尊稱為“教父”的 Martin Fowler(世界頂級專家,敏捷開發方法的創始人之一)又給出了一個新的名字“依賴注入”,相對 IoC 而言,“依賴注入”明確描述了“被注入對象依賴 IoC 容器配置依賴對象”。 #### 考點分析 IoC 和 DI 為 Spring 框架設計的精髓所在,也是面試中必問的考點之一,這個優秀的設計思想對于初學者來說可能理解起來比較困難,但對于 Spring 的使用者來說可以很快的看懂。因此如果對于此概念還有疑問的話,建議先上手使用 Spring 實現幾個小功能再回頭來看這些概念,相信你會豁然開朗。 Spring 相關的高頻面試題,還有以下這些: * Spring IoC 有哪些優勢? * IoC 的注入方式有哪些? * 談一談你對 AOP 的理解。 #### 知識擴展 * [ ] 1.Spring IoC 的優點 IoC 的優點有以下幾個: * 使用更方便,拿來即用,無需顯式的創建和銷毀的過程; * 可以很容易提供眾多服務,比如事務管理、消息服務等; * 提供了單例模式的支持; * 提供了 AOP 抽象,利用它很容易實現權限攔截、運行期監控等功能; * 更符合面向對象的設計法則; * 低侵入式設計,代碼的污染極低,降低了業務對象替換的復雜性。 * [ ] 2.Spring IoC 注入方式匯總 IoC 的注入方式有三種:構造方法注入、Setter 注入和接口注入。 * ① 構造方法注入 構造方法注入主要是依賴于構造方法去實現,構造方法可以是有參的也可以是無參的,我們平時 new 對象時就是通過類的構造方法來創建類對象的,每個類對象默認會有一個無參的構造方法,Spring 通過構造方法注入的代碼示例如下: ``` public class Person { public Person() { } public Person(int id, String name) { this.id = id; this.name = name; } private int id; private String name; // 忽略 Setter、Getter 的方法 } ``` applicationContext.xml 配置如下: ``` <bean id="person" class="org.springframework.beans.Person"> <constructor-arg value="1" type="int"></constructor-arg> <constructor-arg value="Java" type="java.lang.String"></constructor-arg> </bean> ``` * ② Setter 注入 Setter 方法注入的方式是目前 Spring 主流的注入方式,它可以利用 Java Bean 規范所定義的 Setter/Getter 方法來完成注入,可讀性和靈活性都很高,它不需要使用聲明式構造方法,而是使用 Setter 注入直接設置相關的值,實現示例如下: ``` <bean id="person" class="org.springframework.beans.Person"> <property name="id" value="1"/> <property name="name" value="Java"/> </bean> ``` * ③ 接口注入 接口注入方式是比較古老的注入方式,因為它需要被依賴的對象實現不必要的接口,帶有侵入性,因此現在已經被完全舍棄了,所以本文也不打算做過多的描述,大家只要知道有這回事就行了。 * [ ] 3.Spring AOP AOP(Aspect-OrientedProgramming,面向切面編程)可以說是 OOP(Object-Oriented Programing,面向對象編程)的補充和完善,OOP 引入封裝、繼承和多態性等概念來建立一種公共對象處理的能力,當我們需要處理公共行為的時候,OOP 就會顯得無能為力,而 AOP 的出現正好解決了這個問題。比如統一的日志處理模塊、授權驗證模塊等都可以使用 AOP 很輕松的處理。 Spring AOP 目前提供了三種配置方式: * 基于 Java API 的方式; * 基于 @AspectJ(Java)注解的方式; * 基于 `XML <aop />` 標簽的方式。 * ① 基于 Java API 的方式 此配置方式需要實現相關的接口,例如 MethodBeforeAdvice 和 AfterReturningAdvice,并且在 XML 配置中定義相應的規則即可實現。 我們先來定義一個實體類,代碼如下: ``` package org.springframework.beans; public class Person { public Person findPerson() { Person person = new Person(1, "JDK"); System.out.println("findPerson 被執行"); return person; } public Person() { } public Person(Integer id, String name) { this.id = id; this.name = name; } private Integer id; private String name; // 忽略 Getter、Setter 方法 } ``` 再定義一個 advice 類,用于對攔截方法的調用之前和調用之后進行相關的業務處理,實現代碼如下: ``` import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyAdvice implements MethodBeforeAdvice, AfterReturningAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("準備執行方法: " + method.getName()); } @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName() + " 方法執行結束"); } ``` 然后需要在 application.xml 文件中配置相應的攔截規則,配置如下: ``` <!-- 定義 advisor --> <bean id="myAdvice" class="org.springframework.advice.MyAdvice"></bean> <!-- 配置規則,攔截方法名稱為 find* --> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="myAdvice"></property> <property name="pattern" value="org.springframework.beans.*.find.*"></property> </bean> <!-- 定義 DefaultAdvisorAutoProxyCreator 使所有的 advisor 配置自動生效 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> ``` 從以上配置中可以看出,我們需要配置一個攔截方法的規則,然后定義一個 DefaultAdvisorAutoProxyCreator 讓所有的 advisor 配置自動生效。 最后,我們使用測試代碼來完成調用: ``` public class MyApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application.xml"); Person person = context.getBean("person", Person.class); person.findPerson(); } } ``` 以上程序的執行結果為: ``` 準備執行方法: findPerson findPerson 被執行 findPerson 方法執行結束 ``` 可以看出 AOP 的攔截已經成功了。 * ② 基于 @AspectJ 注解的方式 首先需要在項目中添加 aspectjweaver 的 jar 包,配置如下: ``` <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> ``` 此 jar 包來自于 AspectJ,因為 Spring 使用了 AspectJ 提供的一些注解,因此需要添加此 jar 包。之后,我們需要開啟 @AspectJ 的注解,開啟方式有兩種。 可以在 application.xml 配置如下代碼中開啟 @AspectJ 的注解: ``` <aop:aspectj-autoproxy/> ``` 也可以使用 @EnableAspectJAutoProxy 注解開啟,代碼如下: ``` @Configuration @EnableAspectJAutoProxy public class AppConfig { } ``` 之后我們需要聲明攔截器的類和攔截方法,以及配置相應的攔截規則,代碼如下: ``` import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyAspectJ { // 配置攔截類 Person @Pointcut("execution(* org.springframework.beans.Person.*(..))") public void pointCut() { } @Before("pointCut()") public void doBefore() { System.out.println("執行 doBefore 方法"); } @After("pointCut()") public void doAfter() { System.out.println("執行 doAfter 方法"); ``` 然后我們只需要在 application.xml 配置中添加注解類,配置如下: ``` <bean class="org.springframework.advice.MyAspectJ"/> ``` 緊接著,我們添加一個需要攔截的方法: ``` package org.springframework.beans; // 需要攔截的 Bean public class Person { public Person findPerson() { Person person = new Person(1, "JDK"); System.out.println("執行 findPerson 方法"); return person; } // 獲取其他方法 } ``` 最后,我們開啟測試代碼: ``` import org.springframework.beans.Person; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application.xml"); Person person = context.getBean("person", Person.class); person.findPerson(); } } ``` 以上程序的執行結果為: ``` 執行 doBefore 方法 執行 findPerson 方法 執行 doAfter 方法 ``` 可以看出 AOP 攔截成功了。 * ③ 基于 `XML <aop />` 標簽的方式 基于 XML 的方式與基于注解的方式類似,只是無需使用注解,把相關信息配置到 application.xml 中即可,配置如下: ``` <!-- 攔截處理類 --> <bean id="myPointcut" class="org.springframework.advice.MyPointcut"></bean> <aop:config> <!-- 攔截規則配置 --> <aop:pointcut id="pointcutConfig" expression="execution(* org.springframework.beans.Person.*(..))"/> <!-- 攔截方法配置 --> <aop:aspect ref="myPointcut"> <aop:before method="doBefore" pointcut-ref="pointcutConfig"/> <aop:after method="doAfter" pointcut-ref="pointcutConfig"/> </aop:aspect> </aop:config> ``` 之后,添加一個普通的類來進行攔截業務的處理,實現代碼如下: ``` public class MyPointcut { public void doBefore() { System.out.println("執行 doBefore 方法"); } public void doAfter() { System.out.println("執行 doAfter 方法"); } } ``` 攔截的方法和測試代碼與第二種注解的方式相同,這里就不在贅述。 最后執行程序,執行結果為: ``` 執行 doBefore 方法 執行 findPerson 方法 執行 doAfter 方法 ``` 可以看出 AOP 攔截成功了。 Spring AOP 的原理其實很簡單,它其實就是一個動態代理,我們在調用 getBean() 方法的時候返回的其實是代理類的實例,而這個代理類在 Spring 中使用的是 JDK Proxy 或 CgLib 實現的,它的核心代碼在 DefaultAopProxyFactory#createAopProxy(...) 中,源碼如下: ``` public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 判斷目標類是否為接口 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { // 是接口使用 jdk 的代理 return new JdkDynamicAopProxy(config); } // 其他情況使用 CgLib 代理 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } // 忽略其他代碼 } ``` #### 小結 本課時講了 IoC 和 DI 概念,以及 IoC 的優勢和 IoC 注入的三種方式:構造方法注入、Setter 注入和接口注入,最后講了 Spring AOP 的概念與它的三種配置方式:基于 Java API 的方式、基于 Java 注解的方式和基于 XML 標簽的方式。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看