Spring是什麼
首先Spring是一個輕量級控制反轉(IoC)和麪向切面(AOP)的容器框架
Spring框架是由於軟件開發的複雜性而創建的。Spring使用的是基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限於服務器端的開發。從簡單性、可測試性和鬆耦合性角度而言,絕大部分Java應用都可以從Spring中受益。
也就是說,我們學習Spring是學習其理念思想也就是IoC和AOP。其適用範圍很廣,以至於任何應用都能從中受益。
Spring的優勢
- 低侵入 / 低耦合 (降低組件之間的耦合度,實現軟件各層之間的解耦)
- 聲明式事務管理(基於切面和慣例)
- 方便集成其他框架(如MyBatis、Hibernate)
- 降低 Java 開發難度
- Spring 框架中包括了 J2EE 三層的每一層的解決方案(一站式)
Spring框架結構
一些概念
- pojo即 Plain Old Java Objects,簡單老式 Java 對象
- JavaBean即符合 JavaBean 規範的 Java 類。JavaBean 是一種JAVA語言寫成的可重用組件。爲寫成JavaBean,類必須是具體的和公共的,並且具有無參數的構造器。JavaBean 通過提供符合一致性設計模式的公共方法將內部域暴露成員屬性,set和get方法獲取。衆所周知,屬性名稱符合這種模式,其他Java 類可以通過自省機制(反射機制)發現和操作這些JavaBean 的屬性。
IoC
IoC:Inverse of Control(控制反轉)
- 讀作“反轉控制”,更好理解,不是什麼技術,而是一種設計思想,就是將原本在程序中手動創建對象的控制權,交由Spring框架來管理。
- 正控:若要使用某個對象,需要自己去負責對象的創建
- 反控:若要使用某個對象,只需要從 Spring 容器中獲取需要使用的對象,不關心對象的創建過程,也就是把創建對象的控制權反轉給了Spring框架
- 好萊塢法則:Don’t call me ,I’ll call you
一個常見的例子
傳統方法:
對於傳統模式,我們有這樣代碼實現
Juice juice = new Juice("橙子","多糖","超大杯");
工廠模式:
同樣的,我們有如下的代碼實現:
Shop shop = new Shop("橙子","多糖","超大杯");
Juice juice = Shop.makeJuice();
事實上對於這樣簡單的類,我們不需要引入工廠模式來增加複雜度,但是對於更加複雜的類(假如橙子,糖都是一個比較複雜的類),我們使用工廠的話會減少代碼的管理成本。
先記住這個例子,我們編寫一個簡單的Spring程序。
首先編寫一個pojo(傳統對象)
package pojo;
public class Source {
private String fruit; // 類型
private String sugar; // 糖分描述
private String size; // 大小杯
/* setter and getter */
}
編寫配置文件裝配bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="source" class="pojo.Source">
<property name="fruit" value="橙子"/>
<property name="sugar" value="多糖"/>
<property name="size" value="超大杯"/>
</bean>
</beans>
接着來試一下Spring
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
Source source = (Source) context.getBean("source");
System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());
輸出如下:
(我們的工程文件結構如下)
(如何創建Spring工程?)
我們使用IDEA
這樣就獲得了一個包含Spring環境的工程。
AOP
如果說 IoC 是 Spring 的核心,那麼面向切面編程就是 Spring 最爲重要的功能之一了,在數據庫事務中切面編程被廣泛使用
AOP 即 Aspect Oriented Program 面向切面編程
首先,在面向切面編程的思想裏面,把功能分爲核心業務功能,和周邊功能。
- 所謂的核心業務,比如登陸,增加數據,刪除數據都叫核心業務
- 所謂的周邊功能,比如性能統計,日誌,事務管理等等
周邊功能在 Spring 的面向切面編程AOP思想裏,即被定義爲切面
在面向切面編程AOP的思想裏面,核心業務功能和切面功能分別獨立進行開發,然後把切面功能和核心業務功能 “編織” 在一起,這就叫AOP
AOP 當中的概念:
- 切入點(Pointcut) 在哪些類,哪些方法上切入(where)
- 通知(Advice) 在方法執行的什麼實際(when:方法前/方法後/方法前後)做什麼(what:增強的功能)
- 切面(Aspect) 切面 = 切入點 + 通知,通俗點就是:在什麼時機,什麼地方,做什麼增強!
- 織入(Weaving) 把切面加入到對象,並創建出代理對象的過程。(由 Spring 來完成)
AOP 編程
我們還是採用配置文件的方式來進行一個演示
首先我們編寫核心功能:
package service;
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
還有周邊功能
package aspect;
//這裏的依賴包需要額外安裝,建議看這個教程:https://blog.csdn.net/shuduti/article/details/53069241
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
按照IoC的方式進行配置文檔編輯
<?xml version="1.0" encoding="UTF-8"?>
<!--注意,這裏修改了一些依賴包 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="productService" class="service.ProductService" />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
</beans>
編寫測試方法
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
ProductService productService = (ProductService) context.getBean("productService");
productService.doSomeService();
}
運行
接下來,就是本節重點
我們使用AOP來切入
我們希望以doSomeService()爲切入點,在其執行前後增強日誌功能
這樣配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="productService" class="service.ProductService" />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
<!-- 配置AOP -->
<aop:config>
<!-- where:在哪些地方(包.類.方法)做增加 -->
<aop:pointcut id="loggerCutpoint"
expression="execution(* service.ProductService.*(..)) "/>
<!-- what:做什麼增強 -->
<aop:aspect id="logAspect" ref="loggerAspect">
<!-- when:在什麼時機(方法前/後/前後) -->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
</beans>
運行:
我們獲得了日誌功能,但是沒有修改業務代碼