IOC和AOP使用拓展
多種方式實現依賴注入
構造注入
編寫測試類
public class UserServiceImpl implements UserService { // 宣告介面型別的引用,和具體實現類解耦合 private UserDao dao; // 無參構造 public UserServiceImpl() { } // 用於為dao屬性賦值的構造方法 public UserServiceImpl(UserDao dao) { this.dao = dao; } public void addNewUser(User user) { // 呼叫使用者DAO的方法儲存使用者資訊 dao.save(user); } }
在使用設值注入時,Spring通過JavaBean無參構造方法例項化物件,當我們編寫帶參構造方法後,java虛擬機器不會再提供預設的無參構造方法,為了保證使用的靈活性,建議自行新增一個無參構造方法 配置檔案程式碼如下:
<?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 "> <!-- 定義UserDaoImpl物件,並指定id為userDao --> <bean id="userDao" class="dao.impl.UserDaoImpl" /> <!-- 定義UserServiceImpl物件,並指定id為userService --> <bean id="userService" class="service.impl.UserServiceImpl"> <!-- 通過定義的單參構造為userService的dao屬性賦 值 --> <constructor-arg> <!-- 引用id為userDao的物件為userService的dao屬性賦值 --> <ref bean="userDao" /> </constructor-arg> </bean> </beans>
1 一個
constructor-arg節點下的四個屬性
- index是索引,指定注入的屬性,從0開始,如:0代表personDao,1代表str屬性;
- type是指該屬性所對應的型別,如Persondao對應的是com.aptech.dao.PersonDAO;
- ref 是指引用的依賴物件;
- value 當注入的不是依賴物件,而是基本資料型別時,就用value;
比如:
<bean id="Rod" class="cn.springdemo.Greeting"> <constructor-arg index="1"> <value>Rod</value> </constructor-arg> <constructor-arg index="0"> <value>世界上有10種人</value> </constructor-arg> </bean>
使用p名稱空間實現屬性注入
p名稱空間的特點:使用屬性而不是子元素的形式配置Bean的屬性,從而簡化了配置程式碼 語法: 對於直接量(基本資料型別、字串)屬性:p:屬性名="屬性值" 對於引用Bean的屬性:p:屬性名-ref="Bean的id" 使用前先要在Spring配置檔案中引入p名稱空間
xmlns:p="http://www.springframework.org/schema/p"
示例:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用p名稱空間注入屬性值 -->
<bean id="user" class="entity.User" p:username="皮皮" p:age="21"
p:email="[email protected]" />
<bean id="userDao" class="dao.impl.UserDaoImpl" />
<bean id="userService" class="service.impl.UserServiceImpl" p:dao-ref="userDao" />
</beans>
注入不同資料型別
注入直接量
使用
<bean id="user" class="entity.User">
<property name="username">
<value>張三</value>
</property>
<property name="age">
<value>23</value>
</property>
</bean>
如果屬性值中包含了XML檔案的特殊字元(& < > " '),則注入需要進行處理,通常可以採用兩種辦法,使用<[CDATA[]]>標記或把特殊字元替換為實體引用.
<bean id="product" class="entity.Product">
<!-- 使用<![CDATA[]]>標記處理XML特 殊字元 -->
<property name="specialCharacter1">
<value><![CDATA[P&G]]></value>
</property>
<!-- 把XML特殊字元替換為實體引用 -->
<property name="specialCharacter2">
<value>P&G</value>
</property>
<bean>
符號 | 實體引用 |
---|---|
< | & lt; |
> | & gt; |
& | & amp; |
' | & apos; |
" | & quot; |
注意:在XML檔案中字元"<"和“&”是非法的,其他3個符號是合法的,但是將它們替換為實體引用是個好習慣
引用其他Bean元件
Spring中定義的Bean可以互相引用,從而建立依賴關係,除了使用ref屬性,還可以通過
<bean id="userDao" class="dao.impl.UserDaoImpl"/>
<bean id="userService" class="service.impl.UserServiceImpl">
<property name="dao">
<ref bean="userDao"/>
</property>
</bean>
使用內部Bean
<!-- 定義內部Bean -->
<bean id="userService" class="service.impl.UserServiceImpl">
<property name="dao">
<bean class="dao.impl.UserDaoImpl"/>
</property>
</bean>
這樣這個UserDaoImpl型別的Bean就只能被userUservice使用,其他的Bean則無法使用
注入集合型別的屬性
對於List或陣列型別的屬性,可以使用
<!-- 注入List型別 -->
<property name="list">
<list>
<!-- 定義List中的元素 -->
<value>足球</value>
<value>籃球</value>
</list>
</property>
<!-- 注入Map型別 -->
<property name="map">
<map>
<!-- 定義Map中的鍵值對 -->
<entry>
<key>
<value>football</value>
</key>
<value>足球</value>
</entry>
<entry>
<key>
<value>basketball</value>
</key>
<value>籃球</value>
</entry>
</map>
</property>
注入null和空字串值 可以使用注入空字串,使用
<!-- 注入空字串值 -->
<property name="emptyValue">
<value></value>
</property>
<!-- 注入null值 -->
<property name="nullValue">
<null/>
</property>
其他增強型別
Spring支援多種增強型別,除了我們上一篇文章說的前置增強和後置增強,在這裡我們在補充幾種常用的增強型別
異常丟擲增強
異常丟擲增強的特點是在目標方法丟擲異常時織入增強處理。使用異常丟擲增強,可以為各功能模組提供統一的,可撥插的異常處理方案
/**
* 定義包含增強方法的JavaBean
*/
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
public void afterThrowing(JoinPoint jp, RuntimeException e) {
log.error(jp.getSignature().getName() + " 方法發生異常:" + e);
}
}
Spring配置檔案
<!-- 宣告增強方法所在的Bean -->
<bean id="theLogger" class="aop.ErrorLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定義切入點 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增強方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 將afterThrowing()方法定義為異常丟擲增強並引用pointcut切入點 -->
<!-- 通過throwing屬性指定為名為e的引數注入異常例項 -->
<aop:after-throwing method="afterThrowing"
pointcut-ref="pointcut" throwing="e" />
</aop:aspect>
</aop:config>
</beans>
expression指示符我們上一篇文章已經說話大家可以先看一下上一篇文章 使用
最終增強
最終增強的特點是無論丟擲異常還是正常退出,該增強都會得到執行,類似於異常處理機制中finally塊的作用,一般用於釋放資源,使用最終增強,就可以為各功能模組提供統一的,可撥插的處理方案.
/**
* 定義包含增強方法的JavaBean
*/
public class AfterLogger {
private static final Logger log = Logger.getLogger(AfterLogger.class);
public void afterLogger(JoinPoint jp) {
log.info(jp.getSignature().getName() + " 方法結束執行。");
}
}
Spring配置檔案
<!-- 宣告增強方法所在的Bean -->
<bean id="theLogger" class="aop.AfterLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定義切入點 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增強方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 將afterLogger()方法定義為最終增強並引用pointcut切入點 -->
<aop:after method="afterLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
使用
環繞增強
環繞增強在目標方法的前後都可以織入增強處理.環繞增強是功能最強大的增強處理,Spring把目標方法的控制權全部交給它,在環繞增強處理中,可以獲取或修改目標方法的引數,返回值可以對它進行異常處理,甚至可以決定目標方法是否被執行.
/**
* 定義包含增強方法的JavaBean
*/
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);
public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
log.info("呼叫 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入參:" + Arrays.toString(jp.getArgs()));
try {
Object result = jp.proceed();
log.info("呼叫 " + jp.getTarget() + " 的 "
+ jp.getSignature().getName() + " 方法。方法返回值:" + result);
return result;
} catch (Throwable e) {
log.error(jp.getSignature().getName() + " 方法發生異常:" + e);
throw e;
} finally {
log.info(jp.getSignature().getName() + " 方法結束執行。");
}
}
}
Spring配置檔案
<!-- 宣告增強方法所在的Bean -->
<bean id="theLogger" class="aop.AroundLogger"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 定義切入點 -->
<aop:pointcut id="pointcut" expression="execution(* service.UserService.*(..))" />
<!-- 引用包含增強方法的Bean -->
<aop:aspect ref="theLogger">
<!-- 將aroundLogger()方法定義為環繞增強並引用pointcut切入點 -->
<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
使用
增強處理型別 | 特 點 |
---|---|
Before | 前置增強處理,在目標方法前織入增強處理 |
AfterReturning | 後置增強處理,在目標方法正常執行(不出現異常)後織入增強處理 |
AfterThrowing | 異常增強處理,在目標方法丟擲異常後織入增強處理 |
After | 最終增強處理,不論方法是否丟擲異常,都會在目標方法最後織入增強處理 |
Around | 環繞增強處理,在目標方法的前後都可以織入增強處理 |
Spring AOP配置元素
AOP配置元素 | 描 述 |
---|---|
aop:config | AOP配置的頂層元素,大多數的<aop:*>元素必須包含在 |
aop:pointcut | 定義切點 |
aop:aspect | 定義切點 |
aop:after | 定義最終增強(不管被通知的方法是否執行成功) |
aop:after-returning | 定義after-returning增強 |
aop:after-throwing | 定義after-throwing增強 |
aop:around | 定義環繞增強 |
aop:before | 定義前置增強 |
aop:aspectj-autoproxy | 啟動@AspectJ註解驅動的切面 |
使用註解實現IOC的配置
前面我們說過用xml的形式配置IOC,那種方式是比較麻煩的,在Spring2.0以後的版本我們就可以使用註解來配置,進一步減少了配置檔案的程式碼 使用註解定義Bean
/**
* 使用者業務類,實現對User功能的業務管理
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired // 預設按型別匹配
@Qualifier("userDao") // 按指定名稱匹配
private UserDao dao;
// 使用@Autowired直接為屬性注入,可以省略setter方法
/*public void setDao(UserDao dao) {
this.dao = dao;
}*/
public void addNewUser(User user) {
// 呼叫使用者DAO的方法儲存使用者資訊
dao.save(user);
}
}
上面程式碼我們通過註解定義了一個名為userDao的[email protected]的作用和在xml檔案中編寫
<!--掃描包中註解標註的類-->
<context:component-scan base-package="service,dao"/>
<!--多個包之前用逗號隔開-->
Spring還提供了其他的註解 @Componet:實現Bean元件的定義 @Repository:用於標註DAO類 @Service:用於標註業務類 @Controller:用於標註控制器類 @Autowired:實現Bean的自動裝配 @Qualifier:指定Bean的名稱 @Resource:實現Bean的元件裝配 大家可以檢視Spring的開發手冊 進一步瞭解他們的用法
使用註解定義切面
AspectJ 面向切面的框架,它擴充套件了Java語言,定義了AOP 語法,能夠在編譯期提供程式碼的織入 @AspectJ AspectJ 5新增的功能,使用JDK 5.0 註解技術和正規的AspectJ切點表示式語言描述切面 Spring通過整合AspectJ實現了以註解的方式定義增強類,大大減少了配置檔案中的工作量 利用輕量級的位元組碼處理框架asm處理@AspectJ中所描述的方法引數名 使用註解定義切面實現日誌功能
/**
* 使用註解定義切面
*/
@Aspect
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);
@Pointcut("execution(* service.UserService.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void before(JoinPoint jp) {
log.info("呼叫 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入參:" + Arrays.toString(jp.getArgs()));
}
@AfterReturning(pointcut = "pointcut()", returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue) {
log.info("呼叫 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法返回值:" + returnValue);
}
}
切入點表示式使用@Pointcut註解來表示,而切入點簽名則需要一個普通的方法定義來提供, 如上面程式碼中的pointcut()方法,作為切入點簽名的方法必須返回void型別,切入點定義好後,就可以使用pointcut()簽名進行引用 定義完切面後,還需要在Spring配置檔案中完成織入工作
<context:component-scan base-package="service,dao" />
<bean class="aop.UserServiceLogger"></bean>
<aop:aspectj-autoproxy />
配置檔案中首先要匯入aop名稱空間,只需要在配置檔案中新增
使用註解定義其他型別增強
異常丟擲增強
/**
* 通過註解實現異常丟擲增強
*/
@Aspect
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
@AfterThrowing(pointcut = "execution(* service.UserService.*(..))", throwing = "e")
public void afterThrowing(JoinPoint jp, RuntimeException e) {
log.error(jp.getSignature().getName() + " 方法發生異常:" + e);
}
}
使用AfterThrowing註解可以定義異常丟擲增強,如果需要獲取丟擲的異常,可以為增強方法宣告相關型別的引數,並通過@AfterThrowing註解的throwing屬性指定該引數名稱,Spring會為其注入從目標方法丟擲的異常例項 其他的方法都是大同小異,大家可以自己動手試一試 @Aspect(定義一個切面 ) @Before(前置增強) @AfterReturning(後置增強) @Around (環繞增強) @AfterThrowing(異常丟擲增強) @After(最終增強)
在配置檔案中新增
寫的不好還有不懂的地方,大家可以留言一下 我會盡量解決 by安心