1、Spring定義
1.1 百度摘要
Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的鬆耦合問題,因此它將面向接口的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。(由Rod Johnson創建的一個開源框架)
1.2 Spring的一些優點
- 方便解耦,簡化開發
- 核心IOC與DI的思想
- AOP編程的支持
- 聲明式的事物支持
- 包容性,能夠集成各種優秀的框架
- 降低JavaEE API的使用難度
- 方便程序的測試
- Spring屬於低侵入,代碼污染極低
1.3 Spring的各個包
附帶:【Spring的各個包的作用】
Spring AOP:Spring的面向切面編程,提供AOP(面向切面編程)的實現
Spring Aspects:Spring提供的對AspectJ框架的整合
Spring Beans:Spring IOC的基礎實現,包含訪問配置文件、創建和管理bean等。
Spring Context:在基礎IOC功能上提供擴展服務,此外還提供許多企業級服務的支持,有郵件服務、任務調度、JNDI定位,EJB集成、遠程訪問、緩存以及多種視圖層框架的支持。
spring-context-indexer:
Spring Context Support:Spring context的擴展支持,用於MVC方面。
Spring Core:Spring的核心工具包
Spring expression:Spring表達式語言
spring-framework-bom:統一管理jar包版本
Spring Instrument:Spring對服務器的代理接口
Spring Instrument Tomcat:Spring對tomcat連接池的集成
Spring jcl : JCL採用了設計模式中的“適配器模式”,它對外提供統一的接口,然後在適配類中將對日誌的操作委託給具體的日誌框架,比如Log4J,Java Logging API等
Spring JDBC:對JDBC 的簡單封裝
Spring JMS:爲簡化jms api的使用而做的簡單封裝
Spring Messaging:集成messaging api和消息協議提供支持。
Spring orm:整合第三方的orm實現,如hibernate,ibatis,jdo以及spring 的jpa實現
Spring oxm:Spring對於object/xml映射的支持,可以讓JAVA與XML之間來回切換
Spring test:對JUNIT等測試框架的簡單封裝
Spring tx:爲JDBC、Hibernate、JDO、JPA等提供的一致的聲明式和編程式事務管理。
Spring web:包含Web應用開發時,用到Spring框架時所需的核心類,包括自動載入WebApplicationContext特性的類、Struts與JSF集成類、文件上傳的支持類、Filter類和大量工具輔助類。
Spring webmvc:包含SpringMVC框架相關的所有類。包含國際化、標籤、Theme、視圖展現的FreeMarker、JasperReports、 Tiles、Velocity、XSLT相關類。當然,如果你的應用使用了獨立的MVC框架,則無需這個JAR文件裏的任何類。
Spring webmvc portlet:Spring MVC的增強
Spring websocket:提供 Socket通信, web端的推送功能
2、Spring 入門案例
2.1 幾個小案例
2.1.1 包管理
使用maven管理
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-version>5.1.3.RELEASE</spring-version>
</properties>
<dependencies>
<!-- 是apache最早提供的日誌的門面接口。提供簡單的日誌實現以及日誌解耦功能 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2 IOC的入門小例子
IoC(Inversion of Control,控制反轉)。
所謂IoC,對於spring框架來說,就是由spring來負責控制對象的生命週期和對象間的關係。
簡單來說:初始化一個對象,不再是使用傳統的new方式,而是需要的時候,從Spring的容器中取。
- 接口類
public interface UserService {
void addUser();
void updateUser(String code);
void delUser(String code);
User findUserAll();
}
- 實現類
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("進入了addUser的方法.............");
}
@Override
public void updateUser(String code) {
System.out.println("進入了updateUser的方法.............");
}
@Override
public void delUser(String code) {
System.out.println("進入了delUser的方法.............");
}
@Override
public User findUserAll() {
System.out.println("進入了findUserAll的方法.............");
User user = null;
return user;
}
}
- 配置文件
<?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">
<!-- 配置service
<bean> 配置需要創建的對象
id :用於之後從spring容器獲得實例時使用的 【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
class :需要創建實例的全限定類名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl"></bean>
</beans>
測試類
public class UserTest {
/**
* 測試通過Spring的容器獲取bean
*/
@Test
public void test() {
//獲取Spring的容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
//獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
2.1.3 DI 的入門小例子
- Dao 接口類
public interface UserDao {
int saveUser(User user);
int updateUser(String code);
int delUser(String code);
User findUser();
}
- Dao 實現類
public class UserDaoImpl implements UserDao {
@Override
public int saveUser(User user) {
System.out.println("進入了Dao層的saveUser的方法.............");
return 0;
}
@Override
public int updateUser(String code) {
System.out.println("進入了Dao層的updateUser的方法.............");
return 0;
}
@Override
public int delUser(String code) {
System.out.println("進入了Dao層的delUser的方法.............");
return 0;
}
@Override
public User findUser() {
System.out.println("進入了Dao層的findUser的方法.............");
return null;
}
}
- Service接口類
使用IOC例子中的接口
- Service接口類實現類
public class UserServiceImpl implements UserService {
/**
* 注入的對象
*/
private UserDao userDao;
/**
* 注入對象,需要使用到setXXX方法
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() {
userDao.saveUser(new User());
System.out.println("進入了addUser的方法.............");
}
@Override
public void updateUser(String code) {
System.out.println("進入了updateUser的方法.............");
}
@Override
public void delUser(String code) {
System.out.println("進入了delUser的方法.............");
}
@Override
public User findUserAll() {
System.out.println("進入了findUserAll的方法.............");
User user = null;
return user;
}
}
- 配置文件
<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 id="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!-- 配置service
<bean> 配置需要創建的對象
id :用於之後從spring容器獲得實例時使用的 【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
class :需要創建實例的全限定類名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
<!--使用property進行對象注入
name :bean的屬性名
ref : 指向具體的實現對象引用
-->
<property name="userDao" ref="userDaoId"></property>
</bean>
</beans>
- 測試例子
public class UserTest {
/**
* 測試通過Spring的容器獲取bean
*/
@Test
public void test() {
//獲取Spring的容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
//獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
- 結果
2.1.4 依賴注入裝配Bean 屬性
- 依賴注入方式:構造器注入和setter注入
- 裝配方式(創建應用對象之間協作關係的行爲稱爲裝配):手動裝配與自動裝配
2.1.4.1 基於bean的注入
- bean對象 :User
public class User {
public int age;
String code;
public String name;
public User() {
}
public User(int age, String code, String name) {
this.age = age;
this.code = code;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Person {
private User user;
private String unicode ;
public Person() {
}
public Person(User user, String unicode) {
this.user = user;
this.unicode = unicode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getUnicode() {
return unicode;
}
public void setUnicode(String unicode) {
this.unicode = unicode;
}
@Override
public String toString() {
return "Person{" +
"user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
"】, unicode='" + unicode + '\'' +
'}';
}
}
spring的xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!-- 配置service
<bean> 配置需要創建的對象
id :用於之後從spring容器獲得實例時使用的 【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
class :需要創建實例的全限定類名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
<!--使用property進行對象注入
name :bean的屬性名
ref : 指向具體的實現對象引用
-->
<property name="userDao" ref="userDaoId"></property>
</bean>
<!--基於構造器方法注入
<constructor-arg> 用於配置構造方法一個參數argument
name :參數的名稱
value:設置普通數據
ref:引用數據,一般是另一個bean id值
index :參數的索引號,從0開始 。如果只有索引,匹配到了多個構造方法時,默認使用第一個。
type :確定參數類型
-->
<bean id="user" class="com.mall.spring.bean.User">
<!--方式1-->
<!-- <constructor-arg name="age" value="11" />
<constructor-arg name="code" value="00001" />
<constructor-arg name="name" value="張三" />-->
<!--方式2-->
<constructor-arg index="0" type="int" value="12"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="00001"></constructor-arg>
<constructor-arg index="2" type="java.lang.String" value="張三"></constructor-arg>
</bean>
<!--基於setter方法
* 普通數據
<property name="" value="值">
等效
<property name="">
<value>值
等效
<property p:屬性=值 />
* 引用數據
<property name="" ref="另一個bean">
等效
<property name="">
<ref bean="另一個bean"/>
-->
<bean id="userOther" class="com.mall.spring.bean.User" p:name="李四">
<property name="age" value="13"></property>
<property name="code">
<value>00002</value>
</property>
</bean>
<bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
<property name="user" ref="userOther"></property>
</bean>
</beans>
- 測試類
@Test
public void testBean() {
User user = (User) applicationContext.getBean("user");
System.out.println("user = " + user);
User userOther = (User) applicationContext.getBean("userOther");
System.out.println("userOther = " + userOther);
Person person = (Person) applicationContext.getBean("person");
System.out.println("person = " + person);
}
結果:
2.1.4.2 基於集合的注入
- 定義bean
public class Person {
private User user;
private String unicode ;
private String[] arrData ;
private List list;
private Set set;
private Map map;
public Set getSet() {
return set;
}
public void setSet(Set set) {
this.set = set;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public String[] getArrData() {
return arrData;
}
public void setArrData(String[] arrData) {
this.arrData = arrData;
}
public Person() {
}
public Person(User user, String unicode) {
this.user = user;
this.unicode = unicode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getUnicode() {
return unicode;
}
public void setUnicode(String unicode) {
this.unicode = unicode;
}
@Override
public String toString() {
return "Person{" +
"user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
"】, unicode='" + unicode + '\'' +
'}';
}
}
- 配置文件
<bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
<property name="user" ref="userOther"></property>
<!--數組的注入-->
<property name="arrData">
<array>
<value>Dene</value>
<value>Denr</value>
<value>Dent</value>
<value>Deny</value>
<value>Denu</value>
<value>Deno</value>
</array>
</property>
<!--List集合的注入-->
<property name="list">
<list>
<value>12</value>
<value>你好</value>
<value>是的</value>
<value>我不是</value>
<value>你就是</value>
</list>
</property>
<!--Set 集合的注入-->
<property name="set">
<set>
<value>121</value>
<value>你好1</value>
<value>是的1</value>
<value>我不是1</value>
<value>你就是1</value>
</set>
</property>
<!-- Map集合 -->
<property name="map">
<map>
<entry key="name1" value="Lucy"></entry>
<entry key="name2" value="Jack"></entry>
<entry>
<key><value>name3</value></key>
<value>李白</value>
</entry>
</map>
</property>
</bean>
- 測試類
@Test
public void testBean() {
User user = (User) applicationContext.getBean("user");
System.out.println("user = " + user);
User userOther = (User) applicationContext.getBean("userOther");
System.out.println("userOther = " + userOther);
Person person = (Person) applicationContext.getBean("person");
System.out.println("person = " + person);
System.out.println("person 中的數組 = " + Arrays.toString(person.getArrData()));
System.out.println("person 中的數組 = " + Arrays.toString(person.getArrData()));
System.out.println("person 中的List數組 = " + person.getList());
System.out.println("person 中的Map = " + person.getMap());
}
測試結果
結論:
上面所提到的都是手動裝配的方式。當一個對象的屬性是另一個對象時,實例化時,需要爲這個對象屬性進行實例化,這就是裝配。如果一個對象只通過接口來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行切換。但是這樣會存在一個問題,在傳統的依賴注入配置中,我們必須要明確要給屬性裝配哪一個bean的引用,一旦bean很多,就不好維護了(**配置文件就會很臃腫 **)。基於這種場景,spring使用註解來進行自動裝配,解決這個問題。自動裝配就是開發人員不必知道具體要裝配哪個bean的引用,這個識別的工作會由spring來完成。與自動裝配配合的還有“自動檢測”,這 個動作會自動識別哪些類需要被配置成bean,進而來進行裝配。這樣我們就明白了,自動裝配是爲了將依賴注入“自動化”的一個簡化配置的操作。
2.1.5 裝配方式
2.1.5.1 裝配分四種:byName, byType, constructor, autodetect。 【可以使用autowire進行配置,不配置,默認使用byName】
比如:
配置文件:
<bean id="userDao" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoII" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!--使用 property 進行裝配-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" >
<property name="userDao" ref="userDaoll"></property>
</bean>
-
byName:根據屬性名自動裝配。此選項將檢查容器並根據名字查找 ,與屬性完全一致的bean,並將其與屬性自動裝配。
例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byName" />
-
byType 如果容器中存在一個與指定屬性類型相同的bean,那麼將與 該屬性自動裝配;如果存在多個該類型bean,那麼拋出異常, 並指出不能使用byType方式進行自動裝配;如果沒有找 到相匹配的bean,則什麼事都不發生,也可以通過設置 。
例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byType">
-
constructor 就是通過構造器來將類型與參數相同的bean進行裝配。
-
autodetect 是constructor與byType的組合,會先進行constructor,如果不成功,再進行byType。
2.1.5.2 註解開啓與一些自動裝配的註解
- 開啓註解 :
<context:annotation-config />
- 掃描註解包路徑:
<context:component-scan base-package="com.mall.spring" />
2.1.5.2.1 常用的自動裝配註解有以下幾種:@Autowired,@Resource,@Inject,@Qualifier,@Named
- @Autowired 使用
解釋:@Autowired註解是byType類型的,這個註解可以用在屬性上面,setter方面上面以及構造器上面。使用這個註解時,就不需要在類中爲屬性添加setter方法了。但是這個屬性是強制性的,也就是說必須得裝配上,如果沒有找到合適的bean能夠裝配上,就會拋出異常。
這時可以使用required=false來允許可以不被裝配上,默認值爲true。
當required=true時,@Autowired要求必須裝配,但是在沒有bean能裝配上時,就會拋出異常:NoSuchBeanDefinitionException,如果required=false時,則不會拋出異常。 - @Qualifier註解
解釋:@Qualifier註解使用byName進行裝配,這樣可以在多個類型一樣的bean中,明確使用哪一個名字的bean來進行裝配。@Qualifier註解起到了縮小自動裝配候選bean的範圍的作用,@Qualifier不能單獨使用。
代碼展示:
@Autowired
@Qualifier(value = "animal")
private Animal animal;
- @Resource 註解
解釋:@Resource如有指定的name屬性,先按該屬性進行byName方式查找裝配;其次再進行默認的byName方式進行裝配;如果以上都不成功,則按byType的方式自動裝配。都不成功,則報異常。(註解也是java ee的)
如代碼:
@Resource(name = "person")
private Person person;
@Resource
private User user;
- @Inject註解
解釋:與@Autowired註解作用一樣,也是byType類型,而且是java ee提供的,完全可以代替@Autowired註解,但是@Inject必須是強制裝配的,沒有required屬性,也就是不能爲null,如果不存在匹配的bean,會拋出異常。
@Inject也有一個組合的註解,就是@Named註解,與@Qualifier作用一樣,也是byName,但是不是spring的,是java ee標準的。這樣就出現了兩套自動裝配的註解組合,@Autowired與@Qualifier是spring提供的,@Inject與@Named是java ee的
2.1.5.2.2 構造器註解:@Controller,@Components,@Service,@Repository和使用@Component標註的自定義註解
作用:生成的bean的ID默認爲類的非限定名,也就是把類的名字的首字母換成小寫。可以在這些註解的值中寫名bean id的值,比如:@Component(value = "animal")。
幾個註解的簡單說明:
@Controller註解 只能用控制器類上
@Service註解 只能用在業務類上
@Repository註解 只能用在dao類上
@Component註解 無法按照上面三個註解分類,就用此註解
3、Spring AOP 切面
3.1 概念
3.1.1定義:
定義:
AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來建立一種對象層次結構,用於模擬公共行爲的一個集合。不過OOP允許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌代碼往往橫向地散佈在所有對象層次中,而與它對應的對象的核心功能毫無關係對於其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散佈在各處的無關的代碼被稱爲橫切(cross cutting),在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。
AOP技術恰恰相反,它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。
使用"橫切"技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在覈心關注點的多處,而各處基本相似,比如權限認證、日誌、事物。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
3.1.2 基本概念圖
3.1.3 通知類型介紹
介紹
(1)Before:在目標方法被調用之前做增強處理,@Before只需要指定切入點表達式即可
(2)AfterReturning:在目標方法正常完成後做增強,@AfterReturning除了指定切入點表達式後,還可以指定一個返回值形參名returning,代表目標方法的返回值
(3)AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式後,還可以指定一個throwing的返回值形參名,可以通過該形參名
來訪問目標方法中所拋出的異常對象
(4)After:在目標方法完成之後做增強,無論目標方法時候成功完成。@After可以指定一個切入點表達式
(5)Around:環繞通知,在目標方法完成前後做增強處理,環繞通知是最重要的通知類型,像事務,日誌等都是環繞通知,注意編程中核心是一個ProceedingJoinPoint。
3.2 案例
3.2.1 增加包
使用spring的包:
<!-- spring-aop 依賴 start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<!--處理事務和AOP所需的包 切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-aop 依賴 end -->
或許:
<!-- spring-aop 依賴 start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- aspectjweaver 處理事務和AOP所需的包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
aopalliance Spring AOP 的接口支持 三個主要業務實體:Advice 、Interceptor、Joinpoint
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- spring-aop 依賴 end -->
3.2.2 基於XML的切面小案例
- 切入類:
public class TimeHandler {
public void printTime() {
System.out.println("當前時間是:" + System.currentTimeMillis());
}
}
- 接口類:前面例子的接口類 【interface UserService】
- 實現類: 前面例子的類 【class UserServiceImpl implements UserService】
- 配置文件:
<aop:config>
<aop:aspect id = "time" ref="timeHandler">
<!--切入Service的所有方法-->
<aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<!--切入Service的以add開頭的方法-->
<!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
<aop:before method="printTime" pointcut-ref="timeMethod" />
<aop:after method="printTime" pointcut-ref="timeMethod" />
</aop:aspect>
</aop:config>
- 測試類
@Test
public void test() {
//獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
System.out.println("------------------------------------------------------------------");
userService.updateUser("name");
}
- 運行結果:
3.2.3 基於XML的切面小案例增加橫切點,關鍵字 order 使用
order屬性的數字就是橫切關注點的順序
- 新增日誌模擬類:
public class LogHamdler {
public void logBefore() {
System.out.println("日誌打印前.....before.....");
}
public void logAfter() {
System.out.println("日誌打印後.....after.....");
}
}
- xml配置
<aop:config>
<aop:aspect id = "time" ref="timeHandler" order="1">
<!--切入Service的所有方法-->
<aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<!--切入Service的以add開頭的方法-->
<!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
<aop:before method="printTime" pointcut-ref="timeMethod" />
<aop:after method="printTime" pointcut-ref="timeMethod" />
</aop:aspect>
<aop:aspect id = "log" ref="logHandler" order="2">
<aop:pointcut id="logMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<aop:before method="logBefore" pointcut-ref="logMethod" />
<aop:after method="logAfter" pointcut-ref="logMethod" />
</aop:aspect>
</aop:config>
- 測試類運行後結果得到
3.2.4 基於註解方式小案例
- 配置文件
<!--配置自動匹配 aspectJ 註解的 Java 類生成代理對象 -->
<aop:aspectj-autoproxy />
- 接口類:
public interface PersonService {
Person addPerson(Person person);
void delPerson(String code);
}
- 接口實現類
@Service("personService")
public class PersonServiceImpl implements PersonService {
@Resource(name = "person")
private Person person;
@Resource
private User user;
@Override
public Person addPerson(Person person) {
System.out.println("進入了PersonServiceImpl類的addPerson()...............");
return person;
}
@Override
public void delPerson(String code) {
int i = 10;
int count = i / 0 ;
System.out.println("進入了PersonServiceImpl類的delPerson()...............");
}
}
- 切面類 一:
package com.mall.spring.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author 超
* Create by fengc on 2018/12/2 22:54
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定義一個方法, 用於聲明切入點表達式. 一般地, 該方法中再不需要添入其他的代碼.
* 使用 @Pointcut 來聲明切入點表達式.
* 後面的其他通知直接使用方法名來引用當前的切入點表達式.
*/
@Pointcut("execution(* com.mall.spring.service.PersonService.*(..))")
public void declareJointPointExpression(){}
/**
* 在 com.mall.spring.service.PersonService 接口的每一個實現類的每一個方法開始之前執行一段代碼
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
Object object = joinPoint.getTarget();
System.out.println("The object = " +object+ ",The method = " + methodName + ", begins with = " + Arrays.asList(args) );
}
/**
* 在方法執行之後執行的代碼. 無論該方法是否出現異常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
/**
* 在方法法正常結束受執行的代碼
* 返回通知是可以訪問到方法的返回值的! result
*/
@AfterReturning(value = "declareJointPointExpression()",returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 在目標方法出現異常時會執行的代碼.
* 可以訪問到異常對象; 且可以指定在出現特定異常時在執行通知代碼
*/
@AfterThrowing(value="declareJointPointExpression()",throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}
/**
* 環繞通知需要攜帶 ProceedingJoinPoint 類型的參數.
* 環繞通知類似於動態代理的全過程: ProceedingJoinPoint 類型的參數可以決定是否執行目標方法.
* 且環繞通知必須有返回值, 返回值即爲目標方法的返回值
*/
/* @Around("execution(* com.mall.spring.service.PersonService.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//執行目標方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//異常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//後置通知
System.out.println("The method " + methodName + " ends");
return result;
}*/
}
- 切面類二 【爲了測試 order關鍵字 order越小,橫切關注點的順序越先】
@Order(1)
@Aspect
@Component
public class VlidationAspect {
@Before("com.mall.spring.config.LoggingAspect.declareJointPointExpression()")
public void validateArgs(JoinPoint joinPoint) {
System.out.println("-------validateArgs:【Arrays.asList(joinPoint.getArgs())】:" + Arrays.asList(joinPoint.getArgs()));
}
}
測試類:
package com.mall.spring.service;
import com.mall.spring.bean.Person;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 超
* Create by fengc on 2018/12/2 23:06
*/
public class PersonTest {
//獲取Spring的容器
ApplicationContext applicationContext = null;
@Before
public void getApplicationContextInstance() {
applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
}
@Test
public void test() {
PersonService personService = (PersonService) applicationContext.getBean("personService");
personService.addPerson(new Person());
System.out.println("---------------------------------------------------------------------------");
personService.delPerson("21321");
}
}
運行結果:
總結論
上面總結了Spring的一些基本用法,當做筆記。
總的來說,Spring是一個十分優秀的框架。它顛覆了我們對編程的一些傳統觀念。
上面主要讓大家瞭解了Spring的主要核心分別是:Spring的IOC(控制反轉)和DI(依賴注入),
Spring的AOP 切面的簡單效果。