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元素表示構造方法的一個參數,且使用時不區分順序。
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&amp;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和空字符串值
可以使用 注入空字符串,使用 注入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安心


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM