tags: Spring
前言
在Spring的第二篇中主要講解了Spring Core模塊的使用IOC容器創建對象的問題,Spring Core模塊主要是解決對象的創建和對象之間的依賴關系,因此本博文主要講解如何使用IOC容器來解決對象之間的依賴關系!
回顧以前對象依賴
我們來看一下我們以前關于對象依賴,是怎么的歷程
直接new對象
- 在最開始,我們是直接new對象給serice的userDao屬性賦值...
class UserService{UserDao userDao = new UserDao();
}復制代碼
寫DaoFactory,用字符串來維護依賴關系
-
后來,我們發現service層緊緊耦合了dao層。我們就寫了DaoFactory,在service層只要通過字符串就能夠創建對應的dao層的對象了。
-
DaoFactory
public class DaoFactory {private static final DaoFactory factory = new DaoFactory();private DaoFactory(){}public static DaoFactory getInstance(){return factory;}public <T> T createDao(String className,Class<T> clazz){try{T t = (T) Class.forName(className).newInstance();return t;}catch (Exception e) {throw new RuntimeException(e);}}}復制代碼
- serivce
private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);復制代碼
DaoFactory讀取配置文件
-
再后來,我們發現要修改Dao的實現類,還是得修改service層的源代碼呀..于是我們就在DaoFactory中讀取關于daoImpl的配置文件,根據配置文件來創建對象,這樣一來,創建的是哪個daoImpl對service層就是透明的
-
DaoFactory
public class DaoFactory {private UserDao userdao = null;private DaoFactory(){try{InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");Properties prop = new Properties();prop.load(in);String daoClassName = prop.getProperty("userdao");userdao = (UserDao)Class.forName(daoClassName).newInstance();}catch (Exception e) {throw new RuntimeException(e);}}private static final DaoFactory instance = new DaoFactory();public static DaoFactory getInstance(){return instance;}public UserDao createUserDao(){return userdao;}}復制代碼
- service
UserDao dao = DaoFactory.getInstance().createUserDao();
復制代碼
Spring依賴注入
通過上面的歷程,我們可以清晰地發現:對象之間的依賴關系,其實就是給對象上的屬性賦值!因為對象上有其他對象的變量,因此存在了依賴...
Spring提供了好幾種的方式來給屬性賦值
- 1) 通過構造函數
- 2) 通過set方法給屬性注入值
-
- p名稱空間
- 4)自動裝配(了解)
- 5) 注解
搭建測試環境
-
UserService中使用userDao變量來維護與Dao層之間的依賴關系
-
UserAction中使用userService變量來維護與Service層之間的依賴關系
-
userDao
public class UserDao {public void save() {System.out.println("DB:保存用戶");}
}復制代碼
- userService
public class UserService {private UserDao userDao; public void save() {userDao.save();}
}復制代碼
- userAnction
public class UserAction {private UserService userService;public String execute() {userService.save();return null;}
}
復制代碼
構造函數給屬性賦值
其實我們在講解創建帶參數的構造函數的時候已經講過了...我們還是來回顧一下唄..
我們測試service和dao的依賴關系就好了....在serice中加入一個構造函數,參數就是userDao
public UserService(UserDao userDao) {this.userDao = userDao;//看看有沒有拿到userDaoSystem.out.println(userDao);}復制代碼
applicationContext.xml配置文件
<!--創建userDao對象--><bean id="userDao" class="UserDao"/><!--創建userService對象--><bean id="userService" class="UserService"><!--要想在userService層中能夠引用到userDao,就必須先創建userDao對象--><constructor-arg index="0" name="userDao" type="UserDao" ref="userDao"></constructor-arg></bean>復制代碼
- 測試:可以成功獲取到userDao對象
// 創建容器對象ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//得到service對象UserService userService = (UserService) ac.getBean("userService");
復制代碼
通過set方法給屬性注入值
我們這里也是測試service和dao層的依賴關系就好了...在service層通過set方法來把userDao注入到UserService中
- 為UserService添加set方法
public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;//看看有沒有拿到userDaoSystem.out.println(userDao);}public void save() {userDao.save();}
}復制代碼
applicationContext.xml配置文件:通過property節點來給屬性賦值
- 引用類型使用ref屬性
- 基本類型使用value屬性
<!--創建userDao對象--><bean id="userDao" class="UserDao"/><!--創建userService對象--><bean id="userService" class="UserService"><property name="userDao" ref="userDao"/></bean>復制代碼
- 測試:
內部Bean
我們剛才是先創建userDao對象,再由userService對userDao對象進行引用...我們還有另一種思維:先創建userService,發現userService需要userDao的屬性,再創建userDao...我們來看看這種思維方式是怎么配置的:
applicationContext.xml配置文件:property節點內置bean節點
<!--1.創建userService,看到有userDao這個屬性2.而userDao這個屬性又是一個對象3.在property屬性下又內置了一個bean4.創建userDao--><bean id="userService" class="UserService"><property name="userDao"><bean id="userDao" class="UserDao"/></property></bean>復制代碼
- 測試
我們發現這種思維方式和服務器訪問的執行順序是一樣的,但是如果userDao要多次被其他service使用的話,就要多次配置了...
p 名稱空間注入屬性值
p名稱控件這種方式其實就是set方法的一種優化,優化了配置而已...p名稱空間這個內容需要在Spring3版本以上才能使用...我們來看看:
applicationContext.xml配置文件:使用p名稱空間
<bean id="userDao" class="UserDao"/><!--不用寫property節點了,直接使用p名稱空間--><bean id="userService" class="UserService" p:userDao-ref="userDao"/>復制代碼
- 測試
自動裝配
Spring還提供了自動裝配的功能,能夠非常簡化我們的配置
自動裝載默認是不打開的,自動裝配常用的可分為兩種:
- 根據名字來裝配
- 根據類型類裝配
XML配置根據名字
applicationContext.xml配置文件:使用自動裝配,根據名字
<bean id="userDao" class="UserDao"/><!--1.通過名字來自動裝配2.發現userService中有個叫userDao的屬性3.看看IOC容器中沒有叫userDao的對象4.如果有,就裝配進去--><bean id="userService" class="UserService" autowire="byName"/>復制代碼
- 測試
XML配置根據類型
applicationContext.xml配置文件:使用自動裝配,根據類型
值得注意的是:如果使用了根據類型來自動裝配,那么在IOC容器中只能有一個這樣的類型,否則就會報錯!
<bean id="userDao" class="UserDao"/><!--1.通過名字來自動裝配2.發現userService中有個叫userDao的屬性3.看看IOC容器UserDao類型的對象4.如果有,就裝配進去--><bean id="userService" class="UserService" autowire="byType"/>復制代碼
- 測試:
我們這只是測試兩個對象之間的依賴關系,如果我們有很多對象,我們也可以使用默認自動分配
###使用注解來實現自動裝配###
@Autowired注解來實現自動裝配:
- 可以在構造器上修飾
- 也可以在setter方法上修飾
- 來自java的@Inject的和@AutoWired有相同的功能
如果沒有匹配到bean,又為了避免異常的出現,我們可以使用required屬性上設置為false。【謹慎對待】
- 測試代碼
@Component
public class UserService {private UserDao userDao ;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
復制代碼
順利拿到userDao的引用
注解
自從jdk5有了注解這個新特性,我們可以看到Struts2框架、Hibernate框架都支持使用注解來配置信息...
通過注解來配置信息就是為了簡化IOC容器的配置,注解可以把對象添加到IOC容器中、處理對象依賴關系,我們來看看怎么用吧:
使用注解步驟:
- 1)先引入context名稱空間
-
xmlns:context="http://www.springframework.org/schema/context" 復制代碼
-
- 2)開啟注解掃描器
-
`<context:component-scan base-package=""></context:component-scan>` 復制代碼
- 第二種方法:也可以通過自定義掃描類以@CompoentScan修飾來掃描IOC容器的bean對象。。如下代碼:
-
//表明該類是配置類
@Configuration//啟動掃描器,掃描bb包下的//也可以指定多個基礎包//也可以指定類型
@ComponentScan("bb")
public class AnnotationScan {}
復制代碼
在使用@ComponentScan()這個注解的時候,在測試類上需要@ContextConfiguration這個注解來加載配置類...
- @ContextConfiguration這個注解又在Spring的test包下..
創建對象以及處理對象依賴關系,相關的注解:
- @ComponentScan掃描器
- @Configuration表明該類是配置類
- @Component 指定把一個對象加入IOC容器--->@Name也可以實現相同的效果【一般少用】
- @Repository 作用同@Component; 在持久層使用
- @Service 作用同@Component; 在業務邏輯層使用
- @Controller 作用同@Component; 在控制層使用
- @Resource 依賴關系
- 如果@Resource不指定值,那么就根據類型來找,相同的類型在IOC容器中不能有兩個
- 如果@Resource指定了值,那么就根據名字來找
測試代碼
- UserDao
package aa;import org.springframework.stereotype.Repository;/*** Created by ozc on 2017/5/10.*///把對象添加到容器中,首字母會小寫
@Repository
public class UserDao {public void save() {System.out.println("DB:保存用戶");}}復制代碼
- userService
package aa;import org.springframework.stereotype.Service;import javax.annotation.Resource;//把UserService對象添加到IOC容器中,首字母會小寫
@Service
public class UserService {//如果@Resource不指定值,那么就根據類型來找--->UserDao....當然了,IOC容器不能有兩個UserDao類型的對象//@Resource//如果指定了值,那么Spring就在IOC容器找有沒有id為userDao的對象。@Resource(name = "userDao")private UserDao userDao;public void save() {userDao.save();}
}復制代碼
- userAction
package aa;import org.springframework.stereotype.Controller;import javax.annotation.Resource;/*** Created by ozc on 2017/5/10.*///把對象添加到IOC容器中,首字母會小寫
@Controller
public class UserAction {@Resource(name = "userService")private UserService userService;public String execute() {userService.save();return null;}
}復制代碼
- 測試
package aa;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Created by ozc on 2017/5/10.*/
public class App {public static void main(String[] args) {// 創建容器對象ApplicationContext ac = new ClassPathXmlApplicationContext("aa/applicationContext.xml");UserAction userAction = (UserAction) ac.getBean("userAction");userAction.execute();}
}復制代碼
注解和XML配置是可以混合使用的...
如果您覺得這篇文章幫助到了您,可以給作者一點鼓勵