本文全文手寫,代碼全部手寫,也希望大家,可以做一遍,最起碼調試一遍,這樣比看的效果好的多,本文的spring使用的是5.0.4版本,ide使用的是IntelliJ IDEA,不足和錯誤之處還請大家指出,謝謝!!
一、spring是什麼
Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的鬆耦合問題,因此它將面向接口的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架
- spring百度百科
- spring-framework
二、spring快速入門
什麼是spring?
- 首先我們瞭解到struts是web框架(jsp/action/actionform)
- hibernate是 orm框架處於持久層
spring是容器框架,用於配置bean,並維護bean之間的關係的框架
- spring中的bean:是一個很重要的概念,這裏的bean可以是Java中的任何一種對象:JavaBean/service/action/數據源/dao等等
- spring中的ioc(inverse of control 控制反轉)
spring中的di(dependency injection 依賴注入)
接下來看一個層次框架圖:
說明:
- web層: struts充當web層,接管jsp,action,表單,主要體現出mvc的數據輸入,數據的處理,數據的顯示分離
- model層: model層在概念上可以理解爲包含了業務層,dao層,持久層,需要注意的是,一個項目中,不一定每一個層次都有
- 持久層:體現oop,主要解決關係模型和對象模型之間的阻抗
入門項目:
- 創建java項目(web中也可以使用)
- 創建lib文件夾引入spring的開發最小包(最小配置,spring.jar(該包把最常用的包都包括),commons-logging.jar(日誌包))
- 創建配置文件,一般在src目錄下
- 配置bean
說明:<bean></bean>
這對標籤元素的作用:當我們加載spring框架時,spring就會自動創建一個bean對象,並放入內存相當於我們常規的new一個對象,而<property></property>
中的value則是實現了“對象.set方法”,這裏也體現了注入了概念 - 然後在java文件(測試文件)中調用
接下來看具體的項目:
說明:這是我的目錄結構,其中我使用了ide整合了jar包,如果是手動創建時只需將jar包導入到項目裏即可
User.java
package com.nuc.Bean;
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String sayHello(){
System.out.println("hello"+ name);
return "true";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
拋開spring框架,使用傳統方式實現在測試類中調用sayHello方法:
這樣,沒有異議吧。
接下來使用spring調用該方法
結果爲小強,是因爲上面的配置文件中配置value爲小強
這樣一個基本的項目就完成了~
接下來是細節:
創建User2這個類
package com.nuc.Bean;
public class User2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayBye(){
System.out.println("bye"+name);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
private User2 user2;
並在sayHello中調用sayBye方法 注意點細節都已經在圖中註釋表明
spring運行原理圖
入門項目小結:
spring實際上是容器框架,可以配置各種bean,並可以維護bean與bean的關係,當我們需要使用某個bean的時候,我們可以直接getBean(id),使用即可
現在我們來回答什麼是spring這個問題
- spring是一個容器框架,它可以接管web層,業務層,dao層,持久層的各個組件,並且可以配置各種bean, 並可以維護bean與bean的關係,當我們需要使用某個bean的時候,我們可以直接getBean(id),使用即可
接下來對幾個重要的概念做說明:
- ioc是什麼?
- ioc(inverse of control)控制反轉:所謂反轉就是把創建對象(bean)和維護對象(bean)的關係的權利從程序轉移到spring的容器(spring-config.xml)
- di是什麼?
- di(dependency injection)依賴注入:實際上di和ioc是同一個概念,spring的設計者,認爲di更準確的表示spring的核心
實質上學習框架就是,最重要的就是學習各個配置
三、接口編程
- spring就提倡接口編程,在配合di技術就可以達到層與層解耦的目的
舉案例說明:
這個項目實現的是大小寫轉換
基本思路:
- 創建一個接口
- 創建兩個類實現接口
- 配置bean
- 使用
下面是我的項目目錄
ChangeLetter.java
package com.nuc;
public interface ChangeLetter {
public String change();
}
- 1
- 2
- 3
- 4
- 5
- 6
LowerLetter.java
package com.nuc;
public class LowerLetter implements ChangeLetter {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String change(){
//大小字母轉小寫
return str.toLowerCase();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
UpperLetter.java
package com.nuc;
public class UpperLetter implements ChangeLetter {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String change(){
//把小寫字母轉成大寫
return str.toUpperCase();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
spring-config.xml
<?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">
<!--<bean id="changeLetter" class="com.nuc.UpperLetter">-->
<!--<property name="str">-->
<!--<value>sjt</value>-->
<!--</property>-->
<!--</bean>-->
<bean id="changeLetter" class="com.nuc.LowerLetter">
<property name="str" value="SJT"/>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
說明:其中的兩個bean id名相同是爲了調試方便,可通過註釋來調試
Test.java
package com.nuc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
//調用change不使用接口
// UpperLetter changeLetter = (UpperLetter) ac.getBean("changeLetter");
// System.out.println(changeLetter.change());
//使用接口
ChangeLetter changeLetter = (ChangeLetter)ac.getBean("changeLetter");
System.out.println(changeLetter.change());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
以上這個案例,我們可以初步體會到,di和接口編程,的確可以減少層(web層)和層(業務層)之間的耦合度,儘管看起來似乎沒什麼改變,而且好像麻煩了一些,但是當項目大了以後,這種耦合度的降低就顯得尤爲重要
四、獲取Bean
ApplicationContext 應用上下文容器取
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
當這句代碼被執行,spring-config.xml文件中配置的bean就會被實例化。(但要注意bean的生命週期要爲singleton),也就是說,不管沒有getBean(),使用上下文容器獲取bean,就會實例化該bean
Bean工廠容器取
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
這句代碼被執行,spring-config.xml文件中配置的bean不會被實例化,即光實例化容器,並不會實例化bean
而是在執行以下代碼時纔會被實例化,即使用bean的時候:
factory.getBean("beanId");
如何驗證上述說法呢?每一個java類都有一個默認的構造方法。給這個構造方法輸出一句話。具體如下:
- 創建一個類,類有一個屬性,裝配,該屬性
- 重寫該類的構造方法,輸出bean被創建
- 創建測試類,測試
使用ApplicationContext應用上下文容器
使用bean工廠
可以看到,這一行代碼,並不能時bean實例化,接下來加factory.getBean(“student”);試試
這樣就是bean實例化了
那麼在實際開發中選擇哪種方式?
在移動開發中,即項目運行在移動設備中使用BeanFactory(節約內存,所以,你想節約內存開發就是使用bean工廠,但速度會受影響),但大部分的項目都是使用ApplicationContext(可以提前加載,缺點是消耗一點內存)
貼一張bean的生命週期圖:
接下來我們驗證前兩種作用域:
第一種
結果
可以看到stu1和stu2擁有相同的地址,接下來測試第二種
測試結束!
至於後三種是在web開發中纔有實際意義!
五、三種獲取ApplicationContext對象引用的方法
- ClassPathXmlApplicationContext (從類路徑中加載)
- 這個不在贅述,上面所有例子都是利用這種方式加載的
- FileSystemXmlApplicationContext (從文件系統中加載)
可以看到是沒有問題的,需要注意的是,文件路徑爲絕對路徑,且注意使用轉義符,直接使用“C:\sjt\idea\code\spring\spring-interface\src”,會報錯,需要將“\”轉義,但實際開發中應用不多,瞭解即可 - XmlWebApplicationContext (從web系統中加載)
- 這種方式,注意,在tomcat啓動時就會加載,此處不做說明,在web應用中說明
六、再談Bean的生命週期
生命週期是一個重點嗎?答案是肯定的!!
- 不瞭解生命週期難道不能開發了嗎?那自然是可以的,但如果你想實現更加高級的功能,你不瞭解那可能是會出問題的!而在面試過程中也是經常提及的。
接下里我們舉例子說明
生命週期分爲以下幾步:
- 1、實例化
- 當我們加載sping-config.xml文件時,bean就會被實例化到內存(前提是scope=singleton)
- 2、設置屬性值
- 調用set方法設置屬性,前提是有對應的set方法
- 3、如果你調用BeanNameAware的set’Bean’Name()方法
- 這是個接口,該方法可以給出正在被調用的bean的id
- 4、如果你調用BeanFactoryAware的setBeanFactory()方法
- 這也是個接口,該方法可以傳遞beanFactory
- 5、如果你調用了ApplicationContextAeare的setApplicationContext()方法
- 同樣爲接口,該方法傳遞一個ApplicationContext
- 6、BeanPostProcessor的預初始化方法Before
- 這個東西很厲害了,可以叫做後置處理器,它不是接口,具體細節,代碼體現
- 7、如果你調用了InitializingBean的afterPropertiesSet()方法
- 8、調用自己的init方法,具體爲在bean中有一個屬性inin-method=”init”
- 9、BeanPostProcessor的方法After
- 10、使用bean,體現爲調用了sayHi()方法
- 11、容器關閉
- 12、可以實現DisposableBean接口的destory方法
- 13、可以在調用自己的銷燬方法,類似於8
實際開發過程中,並沒有這麼複雜,常見過程爲,1,2,6,9,10,11
接下來看代碼
MyBeanPostProcessor.java
package com.nuc.BeanLife;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第九步,postProcessAfterInitialization方法被調用");
return null;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第六步,postProcessBeforeInitialization方法被調用");
System.out.println("第六步,"+bean+"被創建的時間爲"+new java.util.Date());
/*
在這裏,能做的事情可就不止上面的這麼簡單的一句輸出了,它還可以過濾每個對象的ip
還可以給所有對象添加屬性或者函數,總之就是所有對象!
其實,這裏體現了AOP編程的思想,AOP呢就是面向切成編程(針對所有對象編程)
*/
return null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
PersonService.java
package com.nuc.BeanLife;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class PersonService implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("第二步調用set方法");
}
public void sayHi(){
System.out.println("第十步,hi"+ name);
}
public PersonService(){
System.out.println("第一步,實例化bean");
}
@Override
public void setBeanName(String arg0){
//該方法可以給出正在被調用的bean的id
System.out.println("第三步,setBeanName被調用,調用的id名爲:"+arg0);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//該方法可以傳遞beanFactory
System.out.println("第四步,setBeanFactory被調用,beanFactory爲:"+beanFactory);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//該方法傳遞一個ApplicationContext
System.out.println("第五步,調用setApplicationContext方法:"+applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第七步,調用afterPropertiesSet()方法");
}
public void init(){
System.out.println("第八步、調用我自己的init()方法");
}
@Override
public void destroy() throws Exception {
//關閉數據連接,socket,文件流,釋放資源
//這個函數的打印你看不到,應爲
System.out.println("第十步,銷燬方法(但不建議使用這種方式釋放資源)");
}
public void destory(){
// 也看到不
System.out.println("銷燬");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
Test.java
package com.nuc.BeanLife;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
PersonService person1= (PersonService) ac.getBean("personService");
person1.sayHi();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
spring-config.xml
<?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">
<bean id="personService" init-method="init" destroy-method="destroy" scope="singleton" class="com.nuc.BeanLife.PersonService">
<property name="name" value="sjt"></property>
</bean>
<bean id="personService2" class="com.nuc.BeanLife.PersonService">
<property name="name" value="sjt2"></property>
</bean>
<!--配置自己的後置處理器,優點類似filter-->
<bean id="myBeanPostProcessor" class="com.nuc.BeanLife.MyBeanPostProcessor">
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
測試結果
4月 17, 2018 4:57:26 下午
信息: Loading XML bean definitions from class path resource [spring-config.xml]
第一步,實例化bean
第二步調用set方法
第三步,setBeanName被調用,調用的id名爲:personService
第四步,setBeanFactory被調用,beanFactory爲:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,調用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被調用
第六步,com.nuc.BeanLife.PersonService@2e6a8155被創建的時間爲Tue Apr 17 16:57:27 CST 2018
第七步,調用afterPropertiesSet()方法
第八步、調用我自己的init()方法
第九步,postProcessAfterInitialization方法被調用
第一步,實例化bean
第二步調用set方法
第三步,setBeanName被調用,調用的id名爲:personService2
第四步,setBeanFactory被調用,beanFactory爲:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,調用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被調用
第六步,com.nuc.BeanLife.PersonService@6221a451被創建的時間爲Tue Apr 17 16:57:27 CST 2018
第七步,調用afterPropertiesSet()方法
第九步,postProcessAfterInitialization方法被調用
第十步,hisjt
Process finished with exit code 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
動手做一遍是最好的選擇!!
使用bean工廠獲取bean對象,生命週期是和上下文獲取的不一樣的,如下圖
其中我只裝配了一個bean,可見執行步驟的短缺
七、裝配Bean
使用xml裝配
- 上下文定義文件的根元素是
<beans></beans>
,有多個子元素<bean></bean>
,每個<bean>
元素定義了bean如何被裝配到spring容器中 - 對子元素bean最基本的配置包括bean的ID和它的全稱類名
- 對bean的scope裝配,默認情況下爲單例模式,具體情況上面已經說過,建議查看文檔,更加具體,儘量不要使用原型bean,即scope設置爲propotype,這樣子會對性能有較大的影響
bean的init-metho和destory-method的書寫,在生命週期那一塊兒已經很清楚了,此處不再贅述,需要說明的是,可以通過註解的方式來配置,而不是在bean中使用init-metho和destory-method屬性
注入集合類型的數據,例如,map,set,list,數組,Properties….
接下來舉例子
目錄結構:
Department.java
package com.nuc;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Department {
private String name;
private String []empName;//這裏int的數組也可以注入成功
private List<Employee> empList;
private Map<String,Employee> empMap;
private Properties pp;
public Properties getPp() {
return pp;
}
public void setPp(Properties pp) {
this.pp = pp;
}
public Set<Employee> getEmpSet() {
return empSet;
}
public void setEmpSet(Set<Employee> empSet) {
this.empSet = empSet;
}
private Set<Employee> empSet;
public List<Employee> getEmpList() {
return empList;
}
public void setEmpList(List<Employee> empList) {
this.empList = empList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getEmpName() {
return empName;
}
public void setEmpName(String[] empName) {
this.empName = empName;
}
public Map<String, Employee> getEmpMap() {
return empMap;
}
public void setEmpMap(Map<String, Employee> empMap) {
this.empMap = empMap;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
Employee.java
package com.nuc;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
Test.java
package com.nuc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
Department department = (Department)ac.getBean("department");
System.out.println(department.getName());
// 取集合
for(String empName:department.getEmpName()){
System.out.println(empName);
}
System.out.println("取list...");
for (Employee e:department.getEmpList()){
System.out.println("name="+e.getName());
}
System.out.println("取set...");
for (Employee e:department.getEmpSet()){
System.out.println("name="+e.getName());
}
System.out.println("迭代器取map...");
//1.迭代器
Map<String,Employee> employeeMap = department.getEmpMap();
Iterator iterator = employeeMap.keySet().iterator();
while (iterator.hasNext()){
String key = (String)iterator.next();
Employee employee=employeeMap.get(key);
System.out.println("key="+key+" "+ employee.getName());
}
System.out.println("entry取map...");
//2.簡潔(建議使用這種方式)
for (Entry<String,Employee> entry:department.getEmpMap().entrySet()){
System.out.println(entry.getKey()+" "+entry.getValue().getName());
}
}
System.out.println("通過properties取數據");
Properties properties = department.getPp();
for (Entry<Object,Object> entry:properties.entrySet()){
System.out.println(entry.getKey().toString()+" "+entry.getValue());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
spring-config.xml
<?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">
<bean id="department" class="com.nuc.Department">
<property name="name" value="財務部"></property>
<!--給數組注入-->
<property name="empName">
<list>
<value>小明</value>
<value>小花</value>
</list>
</property>
<!--給list注入-->
<!--list可以存放相同的對象,並當作不同對象輸出-->
<property name="empList">
<list>
<ref bean="employee1"></ref>
<ref bean="employee2"></ref>
</list>
</property>
<!--給set注入-->
<!--set集合不可以存放相同對象-->
<property name="empSet">
<set>
<ref bean="employee1"></ref>
<ref bean="employee2"></ref>
</set>
</property>
<!--給map注入-->
<!--輸出的對象取決於key值,key值不同,對象相同也可以打出-->
<!--當key值相同時,對象相同或者不同都打出最後一個key所對應的對象-->
<property name="empMap">
<map>
<entry key="1" value-ref="employee1"></entry>
<entry key="2" value-ref="employee2"></entry>
<entry key="3" value-ref="employee2"></entry>
</map>
</property>
<!--給屬性集合注入-->
<property name="pp">
<props>
<prop key="1">hello</prop>
<prop key="2">world</prop>
</props>
</property>
</bean>
<bean id="employee1" class="com.nuc.Employee">
<property name="name" value="北京"></property>
</bean>
<bean id="employee2" class="com.nuc.Employee">
<property name="name" value="太原"></property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
測試結果:
注意點,細節都已在代碼中註釋!
- 內部bean
- 具體自行了解,實際中應用不多,不符合重用度高的原則
- 繼承配置bean
- 舉例說明:
結構圖:
Student.java
- 舉例說明:
package com.nuc.inherit;
public class Student {
protected String name;
protected int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
Gradate.java
package com.nuc.inherit;
public class Gradate extends Student {
private String degree;
public String getDegree() {
return degree;
}
public void setDegree(String degree) {
this.degree = degree;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
spring-config.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">
<!--配置一個學生對象-->
<bean id="student" class="com.nuc.inherit.Student">
<property name="name" value="sjt"></property>
<property name="age" value="22"></property>
</bean>
<!--配置gradate對象-->
<bean id="gradate" parent="student" class="com.nuc.inherit.Gradate">
<!--如果子類重新賦值,則覆蓋父類的-->
<property name="name" value="小明"></property>
<property name="degree" value="博士"></property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Test2.java
package com.nuc.inherit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/inherit/spring-config.xml");
Gradate gradate = (Gradate) ac.getBean("gradate");
System.out.println(gradate.getName()+" "+gradate.getAge()+" "+gradate.getDegree());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 以上我們都是用set注入依賴的,下面介紹構造函數注入依賴
<bean name="user" class="com.nuc.Bean.User">
<!--通過constructor-arg標籤完成了對構造方法的傳參-->
<!--如果是屬性是類類型,則使用ref=""-->
<constructor-arg index="0" type="java.lang.String" value="小強"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="男"></constructor-arg>
<constructor-arg index="2" type="int" value="20"></constructor-arg>
</bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
當然對應的User要有相應的構造方法。
set注入的缺點是無法清晰的表達哪個屬性是必須的,哪些是可選的,構造器注入的優勢,是可以通過構造強制依賴關係,不可能實例化不完全或者不能使用的bean
但其實實際開發中還是set注入較多,即property注入
- bean的自動裝配:
接下來是實例:
目錄圖
Dog.java
package com.nuc.autowire;
public class Dog {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
Master.java
package com.nuc.autowire;
public class Master {
private String name;
private Dog dog;
private Master(Dog dog){
//爲了自動裝配的constructor
this.dog= dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
Test.java
package com.nuc.autowire;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com\\nuc\\autowire\\beans.xml");
Master master = (Master)ac.getBean("master");
System.out.println(master.getName()+"養了只狗,它的名字叫"+ master.getDog().getName()+",他今年"+master.getDog().getAge()+"歲了");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
beans.xml
<?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">
<!--配置master對象-->
<bean id="master" class="com.nuc.autowire.Master" autowire="constructor">
<property name="name" value="sjt"></property>
<!--傳統方式-->
<!--<property name="dog" ref="dog"></property>-->
</bean>
<!--配置dog對象,byName時使用-->
<!--<bean id="dog" class="com.nuc.autowire.Dog">-->
<!--<property name="name" value="小黃"></property>-->
<!--<property name="age" value="2"></property>-->
<!--</bean>-->
<!--配置dog對象,byType時使用-->
<!--<bean id="dog11" class="com.nuc.autowire.Dog">-->
<!--<property name="name" value="小黃"></property>-->
<!--<property name="age" value="2"></property>-->
<!--</bean>-->
<!--配置dog對象,constructor時使用-->
<bean id="dog22" class="com.nuc.autowire.Dog">
<property name="name" value="小黃"></property>
<property name="age" value="2"></property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- autodetect:是在constructor和byType之間選一種
default:這種方式在文檔中沒有提及,需要在beans中指定,當你在beans中指定以後,所有的bean都是你所指定的裝配方式,如果沒有指定,則默認爲no,所以,no之所以爲默認指定裝配方式,其實是從beans那裏來的
其實在實際開發中,很少用到自動裝配, 一般都是手動set裝配的(property),而且自動裝配也是在bean中沒有配置才取執行自動裝配的
- spring本身提供的bean
- 分散配置
八、AOP編程(難點)
aop:aspect oriented programming(面向切面編程),它是對一類對象或所有對象編程。
- 核心:在不增加代碼的基礎上,還增加新功能
- 提醒:aop編程,實際上是開發框架本身用的多,開發中不是很多,將來會很多
- 初步理解:面向切面:其實是,把一些公共的“東西”拿出來,比如說,事務,安全,日誌,這些方面,如果你用的到,你就引入。
接下來通過例子來理解這個抽象的概念,概念稍後再說
步驟:
拿前置通知打比方,後來還會有,後置通知,環繞通知,異常通知,引入通知
- 定義接口
- 編寫對象(被代理對象=目標對象)
- 編寫通知(前置通知目標方法調用前調用)
- 在beans.xml中配置
- 配置被代理對象
- 配置通知
- 配置代理對象(是proxyFactoryBean的對象實例)
- 配置代理接口集
- 織入通知
- 配置被代理對象
接下來看代碼:
目錄結構:
TestServiceInter.java(interface)
package com.nuc.Aop;
public interface TestServiceInter {
public void sayHello();
}
- 1
- 2
- 3
- 4
- 5
- 6
TestServiceInter2.java(interface)
package com.nuc.Aop;
public interface TestServiceInter2 {
public void sayBye();
}
- 1
- 2
- 3
- 4
- 5
TestService.java
package com.nuc.Aop;
public class TestService implements TestServiceInter,TestServiceInter2{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello(){
System.out.println("hi "+name);
}
@Override
public void sayBye() {
System.out.println("bye "+name);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
MyMethodBeforeAdvice.java
package com.nuc.Aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
//前置通知
@Override
public void before(Method method, Object[] objects, Object o)
throws Throwable {
//method:被調用方法的名字
//objects:給method傳遞的參數
//o:目標對象
System.out.println("記錄日誌。。。"+method.getName());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
beans.xml
<?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">
<!--配置被代理的對象-->
<bean id="testService" class="com.nuc.Aop.TestService">
<property name="name" value="sjt"/>
</bean>
<!--配置前置通知-->
<bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
<!--配置代理對象-->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置代理接口-->
<property name="proxyInterfaces">
<list>
<value>com.nuc.Aop.TestServiceInter</value>
<value>com.nuc.Aop.TestServiceInter2</value>
</list>
</property>
<!--把通知織入到代理對象-->
<property name="interceptorNames">
<!--相當於把myMethodBeforeAdvice前置通知和代理對象關聯起來-->
<value>myMethodBeforeAdvice</value>
</property>
<!--配置被代理對象,可以指定-->
<property name="target" ref="testService"></property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
Test.java
package com.nuc.Aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/Aop/beans.xml");
TestServiceInter testService = (TestServiceInter)ac.getBean("proxyFactoryBean");
testService.sayHello();
//當一個類繼承多個接口,那麼他們之間可以互轉
((TestServiceInter2)testService).sayBye();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
測試結果
AOP的術語
- 切面:要實現交叉功能,是系統模塊化的一個切面領域,如記錄日誌
- 連接點:應用程序執行過程中插入切面的地點,可以是方法調用,異常拋出,或者要修改的字段
- 連接點是一個靜態的概念
- 通知: 切面的實際實現,它通知系統的新行爲,如日誌通知包含了實現日誌功能的代碼,如向日志文件寫日誌,通知在連接點插入應用系統中。
- 切入點:定義了通知應該應用在哪些連接點,通知可以應用到AOP框架支持的任何連接點
- 切入點是動態概念,當通知應用了連接點,連接點就變成了切入點
- 引入:爲類添加新方法和屬性
- 目標對象:被通知的對象,既可以是你編寫的類,也可以是第三方類
- 代理:將通知應用到目標對象後創建後的對象,應用系統的其他部分不用爲了支持代理對象而改變
- spring的兩種代理:
- 若目標對象實現了若干個接口,spring使用JDK的java.lang.reflect.Proxy類代理
- 若目標對象沒有實現任何接口,spring使用CGLIB庫生成目標對象的子類
- spring的兩種代理:
- 織入:將切面應用到目標對象從而創建一個新代理對象的過程,織入發生在目標對象生命週期的多個點上
- 編譯期:切面在目標對象編譯時織入,這需要一個特使的編譯器
- 類裝載期:切面在目標對象被載入jvm時織入,這需要一個特殊的類加載器
- 運行期:切面在應用系統運行時切入
接下來引入後置通知,環繞通知,異常通知,引用通知
類似於前置通知,前三者需要繼承一種接口,引用通知直接配置
MyAfterReturningAdvice.java
package com.nuc.Aop;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects,
Object o1)
throws Throwable {
//後置通知
//o:前面函數的返回值
//method:哪個方法被調用
//objects:調用方法的參數
//o1:目標對象
System.out.println("後置通知:調用結束,關閉資源。");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
MyMethodInterceptor.java
package com.nuc.Aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//環繞通知
System.out.println("環繞通知:進入函數體,調用方法前");
Object obj = methodInvocation.proceed();
System.out.println("環繞通知:完成調用");
return obj;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
MyThrowsAdvice.java
package com.nuc.Aop;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class MyThrowsAdvice implements ThrowsAdvice {
//異常通知
//ThrowsAdvice這個接口是標識性接口,沒有任何方法
public void afterThrowing(Method m,Object[] os,Object target,Exception e){
System.out.println("異常通知:出問題了:"+e.getMessage());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
beans.xml
<?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">
<!--配置被代理的對象-->
<bean id="testService" class="com.nuc.Aop.TestService">
<property name="name" value="sjt"/>
</bean>
<!--配置前置通知-->
<bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
<!--配置後置通知-->
<bean id="myAfterReturningAdvice" class="com.nuc.Aop.MyAfterReturningAdvice"></bean>
<!--配置環繞通知-->
<bean id="myMethodInterceptor" class="com.nuc.Aop.MyMethodInterceptor"></bean>
<!--配置異常通知-->
<bean id="myThrowsAdvice" class="com.nuc.Aop.MyThrowsAdvice"></bean>
<!--定義前置通知的切入點(引用通知)-->
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"></property>
<property name="mappedNames">
<list>
<!--這裏支持使用正則表達式匹配-->
<!--配置了sayHello使用前置通知過濾-->
<value>sayHello</value>
</list>
</property>
</bean>
<!--配置代理對象-->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置代理接口-->
<property name="proxyInterfaces">
<list>
<value>com.nuc.Aop.TestServiceInter</value>
<value>com.nuc.Aop.TestServiceInter2</value>
</list>
</property>
<!--把通知織入到代理對象-->
<property name="interceptorNames">
<list>
<!--相當於把myMethodBeforeAdvice前置通知和代理對象關聯起來-->
<!--使用自定義切入點-->
<value>myMethodBeforeAdviceFilter</value>
<!--織入後置通知-->
<value>myAfterReturningAdvice</value>
<!--織入環繞通知-->
<value>myMethodInterceptor</value>
<!--織入異常通知-->
<value>myThrowsAdvice</value>
</list>
</property>
<!--配置被代理對象,可以指定-->
<property name="target" ref="testService"></property>
</bean>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
TestService.java
如圖這一處變動
總之呢就是一個配置 -> 織入的過程
運行結果:
可以看到前置通知和後置通知,似乎能夠識別方法,事實上也是這樣的(spring框架內置)。而且sayBay()也得到了應用。這正是,我們前面所提到了,AOP是對一類或所有對象編程的體現,又由於異常通知的配置,有了異常,由於引用通知的配置,致使sayBay的前置通知及後續無法通知。
正常結果(配置引用通知):
總結
spring框架呢,其實就是學習了一大堆的配置,還有幾個spring比較新穎的“思想”,IOC,AOP,這些技術。主要還是spring-config.xml文件的配置,之所以後來改成beans,是因爲,這個文件就是對bean的配置!這套教程,是根據韓老師的視頻總結的,全文基本都是要點,沒有水貨。。。(自我認爲),因爲韓老師將的很到位,很深刻,所以教程自然差不了
爲時5天的總結就結束了。筆者後期還會推出springMVC,Hibernate,望支持~