Spring兩大特性:IOC 和AOP。IOC 控制反轉,AOP 面向切面編程
spring 核心容器的主要組件時Bean工廠(BeanFactory) ,Bean 工廠使用控制反轉模式來降低程序代碼之間的耦合度,並提供了面向切面編程的實現。
Spring 常用的注解
- @Controller :用於標注控制器成組件。
- @Service:用於標注業務成組件。
- @Component : 用於標注這是一個受spring 容器管理的組件,組件引用名稱是類名, 第一個字母小寫。也可以使用@Component("beanName")指定組件的名稱
- @Reposirty:用於標注數據庫訪問組件,即DAO組件。
- @Bean:方法級別的注解,主要用於@Configuration 或@Component 注解的類里, @Bean 注解的方法會產生一個Bean對象,該對象由Spring管理並放在IOC容器中。引用名稱是方法名, 也可以使用@Bean(name = "beanName")指定組件名稱
- @Scope("prototype"):將組件的范圍設置為原型的(即多例)。保證每一個請求有一個單獨的action來處理,避免action的線程問題。由於Spring默認是單例模式,只會創建一個action對象,每次訪問都是同一個對象,容易產生並發問題,數據不安全。
- @Autowired:默認按照類型進行自動裝配,在容器查找匹配的bean, 當僅有一個匹配的Bean時,Spring將其注入@Autowired標注的變量中。
- @Resource:默認按名稱進行自動裝配,當找不到與名稱匹配的Bean時會按照類型裝配。
能夠明確該類時一個控制器類組件時,就用@Controller;
能夠明確該類是一個服務類組件時,就用@Service;
能夠明確該類是一個數據訪問組件時,就用@Repository;
不明確它的作用時,使用@Component;
控制反轉IOC
創建對象的控制權反轉到Spring框架上。
通常,實例化一個對象時,都是使用類的構造方法來new一個對象,這個過程是由我們自己來控制的,而控制反轉就是把new對象的操作交給Spring容器
IOC的主要實現方式:依賴查找、依賴注入。依賴注入是一種更可取的方式。
依賴查找,依賴注入的區別?
依賴查找:主要是容器為組件提供一個回調接口和上下文環境。組件必須自己使用容器提供的API來查找資源和協作對象,控制反轉僅體現在那些回調方法上,容器調用這些回調方法,應用代碼獲取到資源。
依賴注入:組件不做定位查詢,只提供標磚的JAVA方法讓容器去決定依賴關系,容器全權負責組件的裝配,把符合依賴關系的對象通過JAVA Bean 屬性或構造方法傳遞給需要的對象。
IOC容器:具有依賴注入功能的容器,可以創建對象的容器。IOC容器負責實例化、定位、配置應用程序中的對象並建立這些對象之間的依賴。
依賴注入:由IOC容器動態地將某個對象所需要的外部資源(包括對象、資源、常量數據)注入到組件之中
Spring依賴注入的方式主要有四個:基於注解注入、Set注入方式、構造器注入方式、靜態工廠注入方式。推薦使用基於注解注入的方式,配置少,比較方便。
@Autowired和@Resource的區別
共同點: @Autowired和@Resource都是用來裝配Bean的, 都可以寫在字段、setter方法上。
不同點:
@Autowired默認按照類型進行自動裝配,該注解屬於Spring,.默認情況下要求依賴對象必須存在,如果允許為null,需要設置required屬性為false。如果要使用名稱進行裝配,可以和@QualiFier 注解一起使用
@Autowired @QualiFier("beanName") private BeanNameService beanNameService;
@Resource默認按照名稱進行裝配,該注解屬於J2EE,名稱可以通過name屬性來指定。如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行裝配;如果注解寫在setter方法上,默認取屬性名進行裝配。當找不到與名稱匹配的Bean時,會按照類型進行裝配,但是,name屬性一旦指定,就只會按照名稱進行裝配。
@Resource(name="beanNameService") private BeanNameService beanNameService;
面向切面編程AOP
AOP一般應用於:簽名驗簽、參數校驗、日志記錄、事務控制、權限控制、性能統計、異常處理等。
切面(Aspect):共有功能的實現。如日志切面、權限切面、驗簽切面等。在實際開發中通常是一個存放共有功能實現的標准Java類。當Java類使用了@Aspect注解修飾時,就能被AOP容器識別為切面。
通知(Advice):切面的具體實現。就是要給目標對象織入的事情。以目標方法為參照點,根據放置的地方不同,可分為前置通知、后置通知、異常通知、最終通知、環繞通知。通常切面類中的一個方法,具體屬於哪類通知,通過方法上的注解區分。
連接點(JoinPoint):程序在運行過程中能夠插入切面的地點。例如,方法調用、異常拋出等。Spring只支持方法級的連接點。一個類的所有方法前、后、拋出異常時等都是連接點。
切入點(Pointcut):用於定義通知應該切入到哪些連接點上。不同的通知通常需要切入到不同的連接點上,這種精准的匹配是由切入點的正則表達式來定義的。
目標對象(Target):那些即將切入切面的對象,也就是那些被通知的對象。這些對象專注業務本身的邏輯,所有的共有功能等待AOP容器的切入。
代理對象(Proxy):將通知應用到目標對象之后被動態創建的對象。可以簡單地理解為,代理對象的功能等於目標對象本身業務邏輯加上共有功能。代理對象對於使用者而言是透明的,是程序運行過程中的產物。目標對象被織入共有功能后產生的對象。
織入(Weaving):將切面應用到目標對象從而創建一個新的代理對象的過程。這個過程可以發生在編譯時、類加載時、運行時。Spring是在運行時完成織入,運行時織入通過Java語言的反射機制與動態代理機制來動態實現。
Pointcut用法
execution(modifier-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
修飾符匹配 modifier-pattern? 例:public private
返回值匹配 ret-type-pattern 可以用 * 表示任意返回值
類路徑匹配 declaring-type-pattern? 全路徑的類名
方法名匹配 name-pattern 可以指定方法名或者用 * 表示所有方法;set* 表示所有以set開頭的方法
參數匹配 (param-pattern) 可以指定具體的參數類型,多個參數用“,”分隔;可以用 * 表示匹配任意類型的參數;可以用 (..) 表示零個或多個任意參數
異常類型匹配throws-pattern? 例:throws Exception
其中后面跟着 ? 表示可選項
@Pointcut("execution(public * com.project.springbootdemo.controller.*.*(..))") private void sign() { }
示例:
/** SignAop類使用了@Aspect注解,則該類可以被AOP容器識別為切面。 */ @Aspect @Component public class SignAop { /**@Pointcut聲明一個切入點,范圍為controller包下所有的類的所有方法*/ @Pointcut("execution(public * cn.wbnull.springbootdemo.controller.*.*(..))") private void signAop() { } //doBefore()方法使用@Before("signAop()")注解,表示前置通知(在某連接點之前執行的通知),但這個通知不能阻止連接點之前的執行流程,除非它拋出一個異常。 @Before("signAop()") public void doBefore(JoinPoint joinPoint) throws Exception { //code } //doAfterReturning()方法使用@AfterReturning(value = "signAop()", returning = "params")注解,表示后置通知(在某連接點正常完成后執行的通知),通常在一個匹配的方法返回的時候執行。 @AfterReturning(value = "signAop()", returning = "params") public JSONObject doAfterReturning(JoinPoint joinPoint, JSONObject params) { //code } } //實際運行時,在進入controller包下所有方法前,都會進入doBefore()方法,在controller包下方法執行完成后,都會進入doAfterReturning()方法。