Spring的三種注入方式
Spring使用注入方式,爲什麼使用注入方式,這系列問題實際歸結起來就是一句話,Spring的注入和IoC(本人關於IoC的闡述)反轉控制是一回事。下面我們詳細來了解一下
AD:
1. 接口注入(不推薦)
2. getter,setter方式注入(比較常用)
3. 構造器注入(死的應用)
關於getter和setter方式的注入
autowire="defualt"
autowire=“byName”
autowire="bytype"
例如:有如下兩個類需要注入
第一個類:
package org.jia;
public class Order {
private String orderNum;
@SuppressWarnings("unused")
private OrderItem orderitem;
public OrderItem getOrderitem() {
return orderitem;
}
public void setOrderitem(OrderItem orderitem) {
this.orderitem = orderitem;
}
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
}
第二個類:
package org.jia;
public class OrderItem {
private String orderdec;
public String getOrderdec() {
return orderdec;
}
public void setOrderdec(String orderdec) {
this.orderdec = orderdec;
}
}
常用getter&&setter方式介紹
方式第一種注入:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="orderItem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" >
<!-----注入變量 名字必須與類中的名字一樣------->
<property name="orderNum" value="order000007"></property>
<!--注入對象 名字爲orderitem,所屬的類的應用id爲orderItem-->
<property name="orderitem" ref="orderItem"></property>
--></bean>
</beans>
方式第二種注入:byName
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--此時的id就必須與Order.java中所定義的OrderItem的對象名稱一樣了,不然就會找不到-->
<bean id="orderitem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order"<span style="color:ff0000;"> autowire="byName"</span>>
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
方式第三種注入:byType
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--按照byType注入則就與id沒有關係,可以隨便定義id !!!但是不能出現多個此類的id-->
<bean id="orderitdfadafaem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" <span style="color:ff0000;">autowire="byType"</span>>
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
autowire="constructor"
需要在Order.java中加入一個構造器
public Order(OrderItem item )
{
orderitem = item;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="orderItem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" autowire="constructor">
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
三種注入方式比較
接口注入:
接口注入模式因爲具備侵入性,它要求組件必須與特定的接口相關聯,因此並不被看好,實際使用有限。
Setter 注入:
對於習慣了傳統 javabean 開發的程序員,通過 setter 方法設定依賴關係更加直觀。
如果依賴關係較爲複雜,那麼構造子注入模式的構造函數也會相當龐大,而此時設值注入模式則更爲簡潔。
如果用到了第三方類庫,可能要求我們的組件提供一個默認的構造函數,此時構造子注入模式也不適用。
構造器注入:
在構造期間完成一個完整的、合法的對象。
所有依賴關係在構造函數中集中呈現。
依賴關係在構造時由容器一次性設定,組件被創建之後一直處於相對“不變”的穩定狀態。
只有組件的創建者關心其內部依賴關係,對調用者而言,該依賴關係處於“黑盒”之中。
總結
Spring使用注入方式,爲什麼使用注入方式,這系列問題實際歸結起來就是一句話,Spring的注入和IoC(本人關於IoC的闡述)反轉控制是一回事。
理論上:第三種注入方式(構造函數注入)在符合java使用原則上更加合理,第二種注入方式(setter注入)作爲補充。
實際上:我個人認爲第二種注入方式(setter注入)可以取得更加直觀的效果,在使用工作上有不可比擬的優勢,所以setter注入依賴關係應用更加廣泛。
使用p命名空間實現屬性注入(通過setter()方法注入)
作用:使用p命名空間可以簡化配置方法
特點:使用屬性而不是子元素的的形式配置Bean的屬性,從而簡化了Bean的配置。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!--使用p命名空間注入屬性值-->
<bean id="zhanShenArmet" class="cn.homework06.model.Equip"
p:name="戰神頭盔" p:type="頭盔" p:speedPlus="2" p:attackPlus="4"
p:defencePlus="6" />
<bean id="lianHuanLoricae" class="cn.homework06.model.Equip"
p:name="連環鎖子甲" p:type="盔甲" p:speedPlus="6" p:attackPlus="4"
p:defencePlus="15" />
<bean id="bosiBoot" class="cn.homework06.model.Equip"
p:name="波斯追風靴" p:type="靴子" p:speedPlus="8" p:attackPlus="2"
p:defencePlus="3" />
<bean id="lanMoRing" class="cn.homework06.model.Equip"
p:name="藍魔指環" p:type="指環" p:speedPlus="8" p:attackPlus="12"
p:defencePlus="2" />
<bean id="zhangsan" class="cn.homework06.model.Player"
p:armet-ref="zhanShenArmet" p:loricae-ref="lianHuanLoricae"
p:boot-ref="bosiBoot" p:ring-ref="lanMoRing" />
</beans>
注意:這裏記得導入p命名空間的聲明.
使用方式
- 對於直接量(基本數據類型、字符串)
p:屬性名=”屬性值” - 對於引用Bean的屬性
p:屬性名-ref=”Bean的id”
注入不同的數據類型(對於設置注入和構造注入都適用)
1.注入直接量(基本數據類型、字符串)
<!-- 使用<value>子元素注入字符串、基本數據類型及其包裝類 -->
<bean id="user" class="entity.User">
<property name="userame">
<value>張三</value>
</property>
<property name="age">
<value>23</value>
</property>
</bean>
<!-- 使用<![CDATA[]]>標記處理XML特 殊字符 -->
<property name="specialCharacter1">
<value><![CDATA[P&G]]></value>
</property>
<!-- 把XML特殊字符替換爲實體引用 -->
<property name="specialCharacter2">
<value>P&G</value>
</property>
2.引用其他的Bean組件
<!--使用ref屬性-->
<bean>
<property name="dao">
<ref bean="userDao"/>
</property>
</bean>
<!--使用local屬性-->
<bean>
<property name="dao">
<ref local="userDao"/>
</property>
</bean>
雖然以上這兩個屬性都是用來指定Bean的id。但區別在於,使用local屬性只能在同一個配置文件中檢索Bean的id,而使用bean屬性可以在其他配置文件中檢索id
3.使用內部Bean
<!-- 定義內部Bean -->
<property name="innerBean">
<bean class="entity.User">
<property name="username">
<value>Mr. Inner</value>
</property>
</bean>
</property>
4.注入集合類型的屬性
<!-- 注入List類型 -->
<property name="list">
<list>
<!-- 定義List中的元素 -->
<value>足球</value>
<value>籃球</value>
</list>
</property>
<!-- 注入數組類型 -->
<property name="array">
<list>
<!-- 定義數組中的元素 -->
<value>足球</value>
<value>籃球</value>
</list>
</property>
<!-- 注入Set類型 -->
<property name="set">
<set>
<!-- 定義Set或數組中的元素 -->
<value>足球</value>
<value>籃球</value>
</set>
</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>
<!-- 注入Properties類型 -->
<property name="props">
<props>
<!-- 定義Properties中的鍵值對 -->
<prop key="football">足球</prop>
<prop key="basketball">籃球</prop>
</props>
</property>
5.注入null和空字符串
<!-- 注入空字符串值 -->
<property name="emptyValue">
<value></value>
</property>
<!-- 注入null值 -->
<property name="nullValue">
<null/>
</property>
AOP 增強類型
- 前置增強:在原對象的某方法之前插入的增強處理爲前置增強,
- 後置增強:在原對象的某方法正常執行完以後插入的增強處理爲後置增強
- 異常拋出增強:特點是在目標方法拋出異常時織入增強處理,使用異常拋出增強,可以爲各功能提供統一的、可拔插的異常處理方案。
- 最終增強:特點是無論方法拋出異常還是正常退出,該增強都會得到執行,類似於異常處理機制中的finally塊的作用,一般用於釋放資源。使用最終增強,可以爲各功能提供統一的、可拔插的異常處理方案。
- 環繞增強:在目標方法的前後都可以織入增強處理。環繞增強是功能最強大的的增強處理,Spring把目標方法的控制權全部交給了它。在環繞增強處理中,可以獲取或者修改目標方法的參數、返回值,可以對它進行異常處理,甚至可以決定目標方法是否被執行。
使用註解實現loC的配置
1.使用註解定義Bean
package dao.impl;
import org.springframework.stereotype.Component;
import dao.UserDao;
import entity.User;
@Component("userDao")
//此處的@Component("userDao")的作用於在XML配置文件中編寫 <bean id="userDao" class="dao.impl.UserDaoImpl " />等效
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
System.out.println("保存用戶信息到數據庫");
//模擬發生異常
throw new RuntimeException("發生錯誤異常");
}
}
- @Component:用於標註組件。
- @Repository:用來標註DAO類。
- @Service:用來標註業務類。
- @Controller:用來標註控制器類。
2.使用註解實現Bean組件裝配
package service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dao.UserDao;
import entity.User;
import service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
//@Autowired(required=false) required默認爲true,即必須找到匹配的
//Bean完成裝配,否則拋出異常。
@Qualifier("userDao")
private UserDao dao;
//還可以使用setter()方法和構造方法注入
// public void setDao(@Qualifier("userDao") UserDao dao) {
// this.dao = dao;
// }
//
// public UserServiceImpl(){
//
// }
// public UserServiceImpl(@Qualifier("userDao") UserDao dao){
//
// }
@Override
public void addNewUser(User user) {
dao.save(user);
}
}
@Autowired採用類型匹配的方式爲屬性自動裝配合適的依賴對象,即容器會查找和屬性類型相匹配的Bean組件,並自動爲屬性注入。
若容器中有一個以上類型相匹配的Bean時,則可以使用@Qulifier指定所需的Bean的名稱。
3.加載註解定義的Bean
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 掃描包中註解標註的類 -->
<context:component-scan base-package="service,dao" />
</beans>
使用Java標準註解完成裝配
除了提供 @Autowired註解,Spring還支持使用JSR-250中定義的@Resource註解實現組件裝配。(JSR的全稱是 Java Specification Requests 是用來規範功能和接口的標準)
package service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dao.UserDao;
import entity.User;
import service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name="userDao")
//如果沒有顯示地指定Bean的名稱,@Resource註解將根據字段名或者setter方法名產生默認的名稱:如果註解與字段,將使用字段名作爲Bean的名稱
//如果沒有顯示地指定Bean的名稱,且無法找到與默認Bean名稱匹配的Bean組件,@Resource註解會由按名稱查找的方式自動變爲按類型匹配的方式進行裝配。
private UserDao dao;
@Override
public void addNewUser(User user) {
dao.save(user);
}
}
使用註解定義切面
1.AspecJ是一個面向切面的框架,它擴展了Java語言,定義了AOP語法,能夠在編譯期提供代碼的織入,所以他有一個專門的編譯器用來生成遵守字節編碼規範的Class文件。
2.在使用@AspecJ之前要保證,使用的JDK是5.0或其以上版本,否則無法使用註解技術。
/**
* 使用註解定義切面
*/
@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);
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!-- 掃描包中註解標註的類 -->
<context:component-scan base-package="service,dao" />
<bean class="aop.UserServiceLogger "></bean>
<aop:aspectj-autoproxy />
</beans>
使用註解定義其他類型的增強
package aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
/**
*使用註解實現最終增強
*/
@Aspect
public class AfterLogger {
private static final Logger log=Logger.getLogger(ErrorLogger.class);
@After("execution(* service.UserService.*(..))")
public void afterLogger(JoinPoint jp){
log.info(jp.getSignature().getName()+"方法執行結束。");
}
}
package aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
/**
* 註解實現異常拋出增強
*/
@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);
}
}
package aop;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
*通過註解實現環繞增強
*/
@Aspect
public class AroundLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
@Around("execution(* service.UserService.*(..))")
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) {
// TODO: handle exception
log.error(jp.getSignature().getName()+"方法發生異常:"+e);
throw e;
}finally{
log.info(jp.getSignature().getName()+"方法結束執行.");
}
}
}
簡述Spring實現AOP的幾種方式及各自的使用場合?
- 通過接口實現、通過註解實現、通過schema形式將POJO方法定義爲增強。
- 通過接口實現增強處理是較低版本Spring AOP的做法,如果在一個使用低版本Spring AOP開發的項目上進行升級,可以考慮使用複用已經存在的增強類;如果項目採用JDK 5.0以上版本,可以考慮使用@AspectJ註解方式,減少配置的工作量;如果不願意使用註解或項目採用的JDK版本較低無法使用註解,則可以選擇使用配合普通JavaBean的形式。