週六了,又是摸魚的一天,今天還有點不在狀態,腦瓜子迷迷糊糊的,昨晚出去喝可樂桶喝的腦子到現在都不是很正常(奉勸各位可以自己小酌:450ml威士忌+1L多一點可樂剛剛好,可能是我酒量不好),正好沒啥事就想整理一下自己的文件夾,發現了很久之前整理的一個spring基礎的思維導圖,如下:
今天,就以這份思維導圖爲基礎,講解一下spring基礎的內容,好了,我們來看一下文字和代碼的詳細解析吧
需要這份思維導圖的,可以關注公衆號:Java架構師聯盟,後臺回覆Java即可
什麼是Spring
spring是一個輕量級的控制反轉(ioc)和麪向切面編程(AOP)的容器框架。
輕量:從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小隻有1MB多的jar文件裏發佈;並且Spring所需的處理開銷也是微不足道的。
非入侵:在應用中,一般不需要引用springjar包裏的類
控制反轉:Spring的核心機制,通過控制反轉技術促進了松耦合。簡單來講,就是把對象創建的權力交給了容器,讓容器來管理對象。
面向切面:允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發。AOP是基於代理的,通俗一點就是把 核心業務 和 系統服務(日誌系統、權限管理等) 分開。
Spring的核心配置和類
applicationContext.xml:核心配置文件。作用:用於配置所有的類,這些類可以稱爲springbean
BeanFactory:容器的工廠類(接口)。作用:用於創建或獲取springbean,即spring管理的對象。
ApplicationContext:應用上下文(接口)他是BeanFactory的子類 作用:用於創建或獲取springbean。功能比BeanFactory強大。BeanFactory和ApplicationContext的區別: BeanFactory:懶加載 需要某個對象再去加載 ApplicationContext:非懶加載 一次性加載所有的對象
Spring IOC
控制反轉:把對象的創建、銷燬的權利交給容器框架,由容器來管理對象的生命週期。ioc不是新的技術,只是一種思想或理念,實現松耦合。
IOC包括依賴注入(DI,核心) 和 依賴查找。
DI:依賴注入,簡單來講就是在spring實例化對象的時候,由容器來設置這些對象的屬性。
spring的注入方式
屬性的注入(set方法注入)
前提要有對應的setter方法
以下爲spring配置文件代碼 java bean忽略。
<bean id="person" class="com.xx.Person">
<property name = "name" value = "xzy"/>
<property name = "coll">
<list>
<value>list</value>
<value>list2</value>
</list>
</property>
<property name = "map">
<map>
<entry key = "age" value = "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 = "21" />
</bean>
<!--指定下標的方式-->
<bean id="user" class="cn.pojo.User">
<!--指定在構造中的參數名稱-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value="xxx" name = "name" />
</bean>
注入其他類
<!--通過構造器注入-->
<bean id = "user" class = "com.xx.User">
<!--引用的方式 設置引用id-->
<property name = "car" ref = "car"></property>
</bean>
<bean id = "car" class = "com.xx.Car">
</bean>
<!--內部聲明,用這種方式聲明,別的bean不能引用了-->
<property name = "car">
</bean>
</property>
bean元素中的屬性
id:Bean的唯一標識符
name:通過name對Bean進行管理和配置 name可以多個每個以逗號隔開。
class:指定了Bean的具體實現類,必須是完整的類名 實用類的全限定名
scope:設定Bean實例的作用域,其屬性有singleton(單例)、prototype(原型)、request、session、和globalSession,默認值爲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">
<!-- 定義一個切面 -->
pointcut-ref="myPointcut" />
pointcut-ref="myPointcut" throwing="e" />
</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="myPointCut()",throwing="e")
public void doThrowing(Throwable e){
System.out.println("異常通知......."+e.getMessage());
}
}
第二步:xml配置文件
<bean id="user" class="com.bean.User"></bean>
<context:component-scan base-package="com.xxx"></context:component-scan>
<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();
}
如有問題 請指出