周六了,又是摸魚的一天,今天還有點不在狀態,腦瓜子迷迷糊糊的,昨晚出去喝可樂桶喝的腦子到現在都不是很正常(奉勸各位可以自己小酌:450ml威士忌+1L多一點可樂剛剛好,可能是我酒量不好),正好沒啥事就想整理一下自己的檔案夾,發現了很久之前整理的一個spring基礎的思維導圖,如下:

今天,就以這份思維導圖為基礎,講解一下spring基礎的內容,好了,我們來看一下**文字和代碼**的詳細決議吧
**需要這份思維導圖的,可以關注公眾號:Java架構師聯盟,后臺回復Java即可**
# 什么是Spring
spring是一個輕量級的控制反轉(ioc)和面向切面編程(AOP)的容器框架,
- 輕量:從大小與開銷兩方面而言Spring都是輕量的,完整的Spring框架可以在一個大小只有1MB多的jar檔案里發布;并且Spring所需的處理開銷也是微不足道的,
- 非入侵:在應用中,一般不需要參考springjar包里的類
- 控制反轉:Spring的核心機制,通過控制反轉技術促進了松耦合,簡單來講,就是把物件創建的權力交給了容器,讓容器來管理物件,
- 面向切面:允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發,AOP是基于代理的,通俗一點就是把 核心業務 和 系統服務(日志系統、權限管理等) 分開,
# Spring的核心配置和類
1. **applicationContext.xml**:核心組態檔,作用:用于配置所有的類,這些類可以稱為springbean
2. **BeanFactory**:容器的工廠類(介面),作用:用于創建或獲取springbean,即spring管理的物件,
3. **ApplicationContext**:應用背景關系(介面)他是BeanFactory的子類 作用:用于創建或獲取springbean,功能比BeanFactory強大,**BeanFactory**和**ApplicationContext**的區別: *BeanFactory*:懶加載 需要某個物件再去加載 *ApplicationContext*:非懶加載 一次性加載所有的物件
# Spring IOC
**控制反轉**:把物件的創建、銷毀的權利交給容器框架,由容器來管理物件的生命周期,ioc不是新的技術,只是一種思想或理念,實作松耦合,
IOC包括**依賴注入**(**DI**,核心) 和 依賴查找,
**DI**:依賴注入,簡單來講就是在spring實體化物件的時候,由容器來設定這些物件的屬性,
# spring的注入方式
# 屬性的注入(set方法注入)
**前提要有對應的setter方法**
以下為spring組態檔代碼 java bean忽略,
```
<bean id="person" >
<property name = "name" value = "https://www.cnblogs.com/biwz/archive/2020/12/05/xzy"/>
<property name = "coll">
<list>
<value>list</value>
<value>list2</value>
</list>
</property>
<property name = "map">
<map>
<entry key = "age" value = "https://www.cnblogs.com/biwz/archive/2020/12/05/21" />
</map>
</property>
<property name = "arr">
<array>
<value>java</value>
<value>C++</value>
</array>
</property>
</bean>
```
# 通過構造器注入
需要構造方法,javaBean代碼:
```
public class User {
private String name;
private Integer age;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
```
組態檔代碼:
```
<!--通過構造器的方式-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value = "https://www.cnblogs.com/biwz/archive/2020/12/05/Jay" ></constructor-arg>
<constructor-arg value = "https://www.cnblogs.com/biwz/archive/2020/12/05/21" />
</bean>
<!--指定下標的方式-->
<bean id="user" >
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/44" index="1"/>
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/Jack" index="0"/></bean>
<!--指定在構造中的引數名稱-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/44" name="age" />
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/xxx" name = "name" />
</bean>
```
# 注入其他類
```
<!--通過構造器注入-->
<bean id = "user" class = "com.xx.User">
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/Jack"></constructor-arg>
<constructor-arg value="https://www.cnblogs.com/biwz/archive/2020/12/05/44"></constructor-arg>
<!--參考的方式 設定參考id-->
<property name = "car" ref = "car"></property>
</bean>
<bean id = "car" class = "com.xx.Car">
<property name = "type" value = "https://www.cnblogs.com/biwz/archive/2020/12/05/BWM"></property>
</bean>
<!--內部宣告,用這種方式宣告,別的bean不能參考了-->
<property name = "car">
<bean class = "cn.xx.Car">
<property name = "type" value = "https://www.cnblogs.com/biwz/archive/2020/12/05/紅旗"/>
</bean>
</property>
```
# bean元素中的屬性
- id:Bean的唯一識別符號
- name:通過name對Bean進行管理和配置 name可以多個每個以逗號隔開,
- class:指定了Bean的具體實作類,必須是完整的類名 實用類的全限定名
- scope:設定Bean實體的作用域,其屬性有singleton(單例)、prototype(原型)、request、session、和global
Session,默認值為singleton.
- constructor-arg:的子元素,可以傳入構造引數實體化 該元素index屬性指定構造引數的序號(從0開始).
- property:的子元素,通過呼叫Bean實體中的setter方法完成屬性賦值.
- ref:property、constructor-arg等元素的子元素,該元素中的bean屬性用于指定對Bean工廠中某個Bean實體的參考;
- value:property、constructor-arg等元素的子元素,用來直接指定一個常量值;
- list:用于封裝List或陣列型別的依賴注入,
- set:用于封裝Set或陣列型別的依賴注入,
- map:用于封裝Map或陣列型別的依賴注入,
- entry:map元素的子元素 用于設定一個鍵值對,
# Bean的實體化
# 構造器實體化
Spring容器通過Bean對應的默認建構式來實體化Bean,
# 靜態工廠方式實體化
通過創建靜態工廠的方式來創建Bean的實體
```
public class BeanFactory {
public static Bean createBean(){
return new Bean();
}
}
<!--factory-method-->
<bean id = "bean" class = "com.xx.BeanFactory" factory-method = "createBean"></bean>
```
# 實體工廠化方式實體化
不再使用靜態方法創建Bean實體,而是采用直接創建Bean實體的方式.
```
public class BeanFactory {
public BeanFactory(){
System.err.println("BeanFactory 工廠實體化")
}
public static Bean createBean(){
return new Bean();
}
}
<!--配置工廠-->
<bean id = "beanFactory" class = "com.xx.BeanFactory" /><!--factory-bean 屬性指向配置的實體工廠;factory-method屬性確定使用工廠的哪個方法-->
<bean id = "bean" factory-bean="beanFactory" factory-method="createBean"/>
```
# Bean的作用域
# singleton:單例模式
Spring IOC容器中只會存在**一個共享的Bean實體**,無論有多少個Bean參考它,始終指向同一物件,
```
組態檔:
<bean id ="dog" class = "com.bean.Dog" scope="singleton"></bean>
java代碼:
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Dog dog1 = (Dog) ctx.getBean("dog");
System.err.println(dog1);
Dog dog2 = (Dog) ctx.getBean("dog");
System.err.println(dog2);
}
// 輸出的結果一致,表明為單例模式,
```
# prototype:原型模式
每次通過Spring容器獲取prototype定義的bean時,容器都將創建一個**新的Bean實體**,每個Bean實體都有自己的**屬性和狀態**,
# request
在一次Http請求中,容器會回傳該Bean的**同一實體**,而對不同的Http請求則會產生新的Bean,而且該bean**僅在當前HttpRequest內有效**,
針對每一次Http請求,Spring容器根據該bean的定義創建一個全新的實體,且該實體僅在當前Http請求內有效,而其它請求**無法看到當前請求中狀態的變化**,當當前Http請求結束,該bean實體也將會被銷毀,
# session
在一次Http Session中,容器會回傳該Bean的同一實體,而對不同的Session請求則會創建新的實體,該bean實體**僅在當前Session內有效**,
同Http請求相同,每一次session請求創建新的實體,而不同的實體之間不共享屬性,且實體**僅在自己的session請求內有效**,請求結束,則實體將被銷毀,
# Bean的裝配方式
# 基于XML的裝配
兩種裝配方式:setter注入和構造器注入,
設定注入的兩個要求:
- Bean類必須提供一個默認的午餐構造方法
- Bean類必須為需要注入的屬性提供對應的setter方法
# 基于注解(annotation)
常見注解:
- @Component 是所有受Spring管理組件的通用形式,
- @Repository 持久層使用,dao層使用,
- @Service 業務類,表示業務層組件,Service層使用,
- @Controller 控制器,表示web層組件
- @Autowired 按照型別來裝配
- @Resource 根據名字去裝配
# 自動裝配
屬性值說明語法default 默認值由的default-autowire屬性值確定default-autowire=“default”byName根據屬性名稱自動裝配byType根據屬性的資料型別自動裝配constructor根據建構式引數的資料型別自動裝配no不適用自動裝配,必須通過ref元素定義
# Spring AOP
實作**核心業務**和**系統服務**代碼之間的分開 通過一種特殊的技術手段來實作核心業務運行時能夠實作系統服務的功能;
***aop的本質是代理*** 通過對方法進行攔截、增強來實作的,
# AOP的基本概念
采用**橫向抽取機制**,把分散在各個方法中的**相同的代碼抽取出來**,然后在編譯器或者是運行時再把這些代碼應用到所需要執行的地方,
**通知(Advice)**:aop在切點上執行的增強處理,
通知的型別:
- 前通知(methodBeforeAdvice):方法執行前做增強
- 后通知(methodAfterAdvice):方法執行后做增強
- 環繞通知(MethodInterceptor):方法執行前和后做增強
- 回傳通知(AfterReturningAdvice): 成功回傳后 進行增強
- 例外通知(ThrowsAdvice): 拋出例外后 進行通知
**切點(Pointcut)**:就是帶有通知的連接點,就是對那些類 哪些方法做增強,
切點的型別:
基于正則運算式 JdkRegexpMethodPointcut
基于AspectJ的運算式 AspectJExpressionPointcut
**切面(Aspect)**:通常上就是一個類,里面定義了 **通知** 和 **切點**
***AOP = 通知 + 切點***
# AOP案例 java實作
```
// com.bean.User
public class User {
public String login(String name){
return "name is "+ name ;
}
public String login(String name , int pwd){
return "name is "+name+", pwd is "+pwd ;
}
}
// 新建一個環繞通知
public class MyAroundAdivice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable{
System.err.println("方法執行前:"+arg0.getMethod().getName()+","+arg0.getArguments()[0]);
Object obj = arg0.proceed();
System.err.println("執行完成后...");
return obj;
}
}
// 新建基于AspectJ切點的測驗類
@Test
public void test(){
// 1. 宣告通知
Advice advice = new MyAroundAdivice();
//2、基于AspectJ宣告切點物件
AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
//3、設定運算式
/*回傳型別為String型別 com.bean.User的login方法 引數為String型別*/
//cut.setExpression("execution (String com.bean.User.login(String))");
//任意放回型別 com包下包括com子包下 任意方法 任意的引數0~n
cut.setExpression("execution(* com..*(..))");
//4、切點+通知 =AOP
Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
//5、宣告代理bean
ProxyFactory proxy = new ProxyFactory(new User());
//6、設定aop
proxy.addAdvisor(advisor);
//7、從代理中獲取代理的物件
User user = (User) proxy.getProxy();
user.login("rose");
System.err.println("---------------");
user.login("jack",123);
}
```
AspectJ語法
*這個目錄下的,或是類上的所有…任意的多個,0~N個execution (* com.bean.User.login(String,int))對login方法,必須要接收兩個引數,且引數的型別必須是Strig,int的,且回傳值無限制,且這個方法必須是User這個類的Execution (* com.*.*(…))回傳所有的型別 所有在com包下的類,不包含子包 類的所有方法 接收任意多個引數 0~NExecution (* com…*.*(…))在com包下的,包含子包下的所有類的所有方法,所有引數都切入execution(* com…*.*(String)) || execution(* com…*.*(*,int))|| 邏輯或
# AOP案例 基于XML宣告式AspectJ
```
<bean id = "user" class = "com.bean.User">
<!-- 定義一個切面 -->
<bean id="myBeforeAdvice" ></bean>
<aop:config>
<!-- 配置切入點 -->
<!-- 運算式(用來表示方法) -->
<!-- execution(<訪問修飾符>?<回傳型別><方法名>(<引數>)<例外>),回傳值,方法名,引數不能少 -->
<!-- *代表:任意值 方法名:全類名.方法名 引數中的..:任意個數,任意型別 -->
<aop:pointcut expression="execution(* com..*(..))" id="myPointcut" />
<!-- 切面配置 -->
<aop:aspect ref="myBeforeAdvice">
<!-- 配置前通知 -->
<aop:before method="doBefore" pointcut-ref="myPointcut" />
<!-- 配置后通知 -->
<aop:after method="doAfter" pointcut-ref="myPointcut" />
<!-- 配置回傳通知 -->
<aop:after-returning method="doReturnAfter"
pointcut-ref="myPointcut" />
<!-- 配置環繞通知 -->
<aop:around method="doAround" pointcut-ref="myPointcut" />
<!-- 例外通知 -->
<aop:after-throwing method="doThrowing"
pointcut-ref="myPointcut" throwing="e" />
</aop:aspect>
</aop:config>
</bean>
```
# 基于注解的宣告式AspectJ (常用)
注解功能@Aspect注解在類上,宣告這是一個切面@Pointcut注解在方法上宣告是一個切點,且要宣告aspectj的切點運算式@Before前通知@After @AfterRetuning –正確執行成功回傳值 @AfterThrow – 錯誤的執行,拋出例外后通知@Around環繞通知
第一步:新建注解的切面類
```
@Aspect
@Component
public class MyAspect {
//定義一個切入點運算式 使用一個回傳值為void,方法體為空的方法來命名切點
@Pointcut("execution(* com..*(..))")
public void myPointCut(){}
//前置通知
@Before("myPointCut()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("前置通知,方法開始..."+":目標類是"+joinPoint.getTarget()+",被植入的增強方法是:"+joinPoint.getSignature().getName());
}
//后置通知
@After("myPointCut()")
public void doAfter()
{
System.out.println("后置通知,方法結束...");
}
//回傳通知
@AfterReturning("myPointCut()")
public void doReturnAfter()
{
System.out.println("回傳通知,方法最終結束...");
}
/**
* 環繞通知 ProceedingJoinPoint用來呼叫被增強的方法
* 必須是Object的回傳型別
* 必須接受一個引數,型別為ProceedingJoinPoint
* 必須throws Throwable
*/
@Around("myPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("環繞通知:begin...");
//執行被增強的方法
Object obj = joinPoint.proceed();
System.out.println("環繞通知:end.....");
return obj;
}
@AfterThrowing(value="https://www.cnblogs.com/biwz/archive/2020/12/05/myPointCut()",throwing="e")
public void doThrowing(Throwable e){
System.out.println("例外通知......."+e.getMessage());
}
}
```
第二步:xml組態檔
```
<bean id="user" ></bean>
<context:component-scan base-package="com.xxx"></context:component-scan>
<!-- 啟動基于注解的宣告式AspectJ支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
```
第三步:測驗方法
```
@Test
public void test1() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/xxx/applicationContext.xml");
User user = ctx.getBean("user",User.class);
user.say();
//user.run();
}
```
> 如有問題 請指出
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230513.html
標籤:其他
