多種方式實現依賴注入
構造注入
編寫測試類
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元素表示構造方法的一個參數,且使用時不區分順序。
2 通過constructor-arg元素的index 屬性可以指定該參數的位置索引,位置從0 開始。
3 constructor-arg元素還提供了type 屬性用來指定參數的類型,避免字符串和基本數據類型的混淆。
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="pipi@anxin.com" />
<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文件的特殊字符(& < > " '),則注入需要進行處理,通常可以采用兩種辦法,使用標記或把特殊字符替換為實體引用.
<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組件
<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類型的屬性,使用
<!-- 注入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指示符我們上一篇文章已經說話大家可以先看一下上一篇文章
使用aop:after-throwing元素可以定義異常拋出增強。如果需要獲取拋出的異常,可以為增強方法聲明相關類型的參數,並通過aop:after-throwing元素的throwing的屬性指定該參數名稱,Spring會為其注入從目標方法拋出的異常實例.
最終增強
最終增強的特點是無論拋出異常還是正常退出,該增強都會得到執行,類似於異常處理機制中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>
使用aop:after元素即可定義最終增強
環繞增強
環繞增強在目標方法的前后都可以織入增強處理.環繞增強是功能最強大的增強處理,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>
使用aop:around元素可以定義環繞增強,通過為增強方法聲明ProceedingJoinPoint類型的參數,可以獲得連接點信息,所用方法與JoinPoint相同.ProceedingJoinPoint是JoinPoint的子接口,其不但封裝目標方法及其入參數組,還封裝了被代理的目標對象,通過它的proceed()方法可以調用其真正的目標方法,從而達到對連接點的完全控制
常用的增強處理類型
| 增強處理類型 | 特 點 |
|---|---|
| Before | 前置增強處理,在目標方法前織入增強處理 |
| AfterReturning | 后置增強處理,在目標方法正常執行(不出現異常)后織入增強處理 |
| AfterThrowing | 異常增強處理,在目標方法拋出異常后織入增強處理 |
| After | 最終增強處理,不論方法是否拋出異常,都會在目標方法最后織入增強處理 |
| Around | 環繞增強處理,在目標方法的前后都可以織入增強處理 |
Spring AOP配置元素
| AOP配置元素 | 描 述 |
|---|---|
| aop:config | AOP配置的頂層元素,大多數的aop:*元素必須包含在aop:config元素內 |
| 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的Bean@Autowired的作用和在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命名空間,只需要在配置文件中添加aop:aspect-autoproxy/元素,
就可以啟用對於@AspecJ注解的支持,Spring將自動為匹配的Bean創建代理
為了注冊定義好的切面,還要在Spring配置文件中是聲明UserServiceLogger的一個示例,如果不需要被其他的Bean引用,可以不指定id屬性
使用注解定義其他類型增強
異常拋出增強
/**
* 通過注解實現異常拋出增強
*/
@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(最終增強)
在配置文件中添加aop:aspectj-autoproxy/元素,啟用對於@Aspect注解的支持
寫的不好還有不懂的地方,大家可以留言一下 我會盡量解決
by安心
