環境搭建
構建 Spring 開發環境需要用到 5 個 jar 包,包括 4 個 Spring 基礎包以及一個第三方日誌包。
Spring FrameWork 下載教程:Spring Framework 5.0.0下載
日誌 jar 包下載地址: commons-logging
將下載的 Spring FrameWork 壓縮包解壓後,進入 libs 目錄,可以看到如下文件:
Spring 框架每一個模塊由 3 個 jar 包組成,例如 aop 模塊由以下三個 jar 包組成:
- spring-aop-5.2.0.RELEASE.jar:二進制文件(.class 文件)
- spring-aop-5.2.0.RELEASE-sources.jar:源碼
- spring-aop-5.2.0.RELEASE-javadoc.jar:在線幫助文檔
Spring 框架基礎包有以下 4 個:
- spring-beans-5.2.0.RELEASE.jar:IOC 的基礎實現,包含訪問配置文件、創建和管理 bean 等
- spring-context-5.2.0.RELEASE.jar:Spring 提供的在基礎 IoC 功能上的擴展服務
- spring-core-5.2.0.RELEASE.jar:Spring 框架的核心,其他模塊都要依賴它
- spring-expression-5.2.0.RELEASE.jar:Spring 表達式語言
新建 Java 工程,創建 lib 目錄,將上面提到的 5 個 jar 包拷貝到 lib 目錄下,然後將它們添加到類庫。
在 src 目錄下添加配置文件 applicationContext.xml,文件內容如下:
<?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">
</beans>
如果在 Eclipse 中安裝 Spring 插件 spring tool suite,創建配置文件時,插件就能爲我們自動生成上面的內容。當然,你也可以直接使用 STS(Spring 官網提供的集成 Spring 插件的 Eclipse)。
以上步驟完成後,項目結構如下所示:
IOC 快速上手
現在我們有一個 Student 類,類信息如下:
public class Student {
private String no;
private String name;
private int age;
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [no=" + no + ", name=" + name + ", age=" + age + "]";
}
}
按照以往的方式,如果我們要使用 Student 對象,需要經過創建對象、爲對象的屬性賦值兩個步驟:
private static void createStudentByCommon() {
Student student = new Student();
student.setNo("s-001");
student.setName("zhangsan");
student.setAge(21);
System.out.println(student);
}
使用 IOC 的方式獲取 Student 對象也需要兩個步驟。
一、在 applicationContext.xml 中配置 bean
配置 bean 主要需要三個參數:
- id:bean 唯一標識符,保證 bean 在 IOC 容器唯一
- class:bean 類型,和 Java 類對應(需要使用全類名)
- property:bean 屬性,和 Java 類的屬性對應
<bean id="stu_2" class="org.hu.entity.Student">
<property name="no" value="s-002"></property>
<property name="name" value="lisi"></property>
<property name="age" value="24"></property>
</bean>
二、從 IOC 容器中獲取 bean
想從 IOC 容器中拿取東西,首先要有 IOC 容器,我們需要用配置文件初始化 IOC 容器。然後根據配置文件中 bean 的 id 獲取 bean,由於 bean 都是 Object 類型,將 bean 強制轉換爲相應的類型即可:
private static void createStudentByIOC() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student)context.getBean("stu_2");
System.out.println(student);
}
兩種方式運行結果:
以上兩種獲取對象的方式都經歷了創建對象、爲對象賦值兩個步驟。不同之處在於,傳統方式的獲取對象需要我們手動完成這兩個步驟;而通過 IOC 方式的獲取對象,IOC 容器會負責完成這兩個步驟,我們只需要從 IOC 容器中獲取就可以了。
AOP 快速上手
想要使用 AOP 功能,需要在項目中再引入 3 個 jar 包:Spring Framework 提供的 aop 包以及兩個第三方包:
將上面 3 個包添加到類庫後,在 applicationContext.xml 配置文件中勾選 aop 支持:
現在我們有一個學生服務的接口,裏面聲明有添加學生的方法:
public interface IStudentService {
void addStudent(Student stu);
}
實現該接口:
public class StudentServiceImpl implements IStudentService {
@Override
public void addStudent(Student stu) {
System.out.println("add student:" + stu.getName() + "," + stu.getAge());
}
}
我想各位在項目開發過程中,應該都寫過類似下面的代碼:
private static void LogBeforeByCommon() {
IStudentService iStudentService = new StudentServiceImpl();
Student stu = new Student();
stu.setName("wangwu");
stu.setAge(25);
System.out.println("start to add student...");
iStudentService.addStudent(stu);
}
打印 log 可以幫助我們瞭解相關代碼是否執行,但是等到項目通過測試後,我們還需要把自己親手寫下的打印 log 代碼註釋掉或刪除(說到這裏,我就想起了曾經被分配到的一個任務:刪除項目中 log 相關的代碼,刪的我想死的心都有了)。像打印 log 這樣的代碼,通常會散亂的分佈在項目各個角落,它們和業務邏輯沒有什麼關聯,但是又和業務代碼牢牢的粘合在一起。爲了讓這類代碼和業務代碼解耦,AOP 出現了。
下面我們就實際體驗一下 AOP。
首先,既然 AOP 的目的是解耦業務代碼和非業務代碼(打印 log),那麼我們就要把非業務代碼抽離出來。其次,爲了讓 Spring 區分哪些是非業務代碼,就要遵循一定的規範。
結合以上兩點,創建 LogBefore 類並實現 aop 包中提供的 MethodBeforeAdvice 接口,重寫 before() 方法:
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("start to add student...");
}
}
現在業務代碼和非業務代碼都已編寫完成,如何將二者聯繫起來呢?答案是配置文件。
在 applicationContext.xml 中添加 aop 相關配置:
<bean id="stuSerImp" class="org.hu.service.StudentServiceImpl"/>
<bean id="logBef" class="org.hu.aop.LogBefore" />
<aop:config>
<aop:pointcut id="addstu"
expression="execution(public void org.hu.service.StudentServiceImpl.addStudent(org.hu.entity.Student))" />
<aop:advisor advice-ref="logBef" pointcut-ref="addstu"/>
</aop:config>
這裏我主要解釋一下兩個標籤的作用:
- aop:pointcut:切點,指定業務代碼(某個類裏的某個方法)。
- aop:advisor:通知,指定非業務代碼(以 bean 形式配置)以及需要關聯的業務代碼。
編寫測試代碼:
private static void LogBeforeByAOP() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IStudentService iStudentService = (IStudentService)context.getBean("stuSerImp");
iStudentService.addStudent((Student)context.getBean("stu_2"));
}
兩種方式運行結果:
項目代碼:https://github.com/BWHN/spring
參考: