(導讀:希望在閱讀的過程中,伴隨着動手 -- 把講述的每一步Demo都自己書寫一遍,若遇到問題一時解決不了,可發郵件給我:[email protected];demo下載地址--http://download.csdn.net/detail/partner4java/4779657 ;也可以結合視頻:http://pan.baidu.com/share/link?shareid=122408&uk=4096755201 ,視頻說實話應該不咋地,是第一次錄製,不存在各種講課技巧 )
第一步:扯蛋
下面緊跟的很長一段,會講一些“大道理”,就當天書看一眼吧
begin….
Spring的設計目標 :
Spring架構圖:
天書告一段落
現在Spring不都分包了麼?
大體看下包的作用
哥,我走了,別說了,嚇到我了,東西太多了
親,別怕,其實Spring就那十個左右的註解就完事了
牛B的框架不會太難用,牛B的代碼不會太難懂。我們要躋身於那批能寫牛B的代碼人羣中。。。
第二步,簡易IoC:
控制反轉IoC
begin:
控制反轉和依賴注入:
不依賴Spring,我們先簡單的實現下控制反轉:
package com.partner4java.nospring.ioc;
public interface Girl {
/**
* 目前只提供簡單服務
*
* @return
*/
public String kiss();
}
package com.partner4java.nospring.ioc;
public interface Boy {
/**
* 好吧,我只想關燈,但是關燈之前,我需要一個女孩
*
*/
public void closeLight();
}
package com.partner4java.nospring.ioc;
/**
*
* simple introduction 知道白富美麼?
* <p>
* detailed comment
*
* @author 王昌龍 2012-9-3
* @see
* @since 1.0
*/
public class Baifumei implements Girl {
@Override
public String kiss() {
return "白富美";
}
}
package com.partner4java.nospring.ioc;
/**
*
* simple introduction 懂蘿莉控麼?
* <p>
* detailed comment
*
* @author 王昌龍 2012-9-3
* @see
* @since 1.0
*/
public class Luoli implements Girl {
@Override
public String kiss() {
return "小蘿莉";
}
}
package com.partner4java.nospring.ioc;
/**
*
* simple introduction 對不起了哥們們,我自己寫的代碼,我就先自己當一把男一號了
* <p>
* detailed comment
*
* @author 王昌龍 2012-9-3
* @see
* @since 1.0
*/
public class XiaoLong implements Boy {
private Girl girl;
public void setGirl(Girl girl) {
this.girl = girl;
}
@Override
public void closeLight() {
System.out.println(this.getClass().getSimpleName() + " closeLight " + girl.getClass().getSimpleName() + " " + girl.kiss());
}
}
類路徑下創建一個properties文件:boy2girl.properties
baifumei=com.partner4java.nospring.ioc.Baifumei
luoli=com.partner4java.nospring.ioc.Luoli
longge=com.partner4java.nospring.ioc.XiaoLong
工廠:
package com.partner4java.nospring.ioc;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
private Map<String, String> beanDefinitions;
public BeanFactory(String beanDefinitionSource) {
readBeanDefinitions(beanDefinitionSource);
}
private void readBeanDefinitions(String beanDefinitionSource) {
Properties props = new Properties();
InputStream is = BeanFactory.class.getResourceAsStream(beanDefinitionSource);
if (is == null) {
throw new IllegalArgumentException("Could not load properties file " + beanDefinitionSource);
}
try {
props.load(is);
is.close();
beanDefinitions = new HashMap<String, String>();
for (Map.Entry<Object, Object> bean : props.entrySet()) {
beanDefinitions.put((String) bean.getKey(), (String) bean.getValue());
}
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getBean(String name) {
String className = beanDefinitions.get(name);
if (className == null) {
return null;
}
try {
return Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
開始幹大事了:
package com.partner4java.nospring.ioc;
import java.util.concurrent.TimeUnit;
public class BeanFactoryTest {
public static void main(String[] args) throws InterruptedException {
BeanFactory beanFactory = new BeanFactory("/boy2girl.properties");
Girl baifumei = (Girl) beanFactory.getBean("baifumei");
Girl luoli = (Girl) beanFactory.getBean("luoli");
XiaoLong longge = (XiaoLong) beanFactory.getBean("longge");
longge.setGirl(baifumei);
longge.closeLight();
System.out.println("休息下");
TimeUnit.SECONDS.sleep(2);
longge.setGirl(luoli);
longge.closeLight();
}
}
怎麼樣?我們這工廠。是不是想找什麼女人都可以了?
使用Spring簡單IoC:
這倆簡單例子所用就是真實的Spring解析核心類,雖然我們平時不直接接觸,但是可以自己私下研究下
(我們還是藉助上面已經完成的男孩、女孩類 -- BeanFactory使用Spring提供的)新建文件boy2girlspring1.properties:
baifumei.(class)=com.partner4java.nospring.ioc.Baifumei
luoli.(class)=com.partner4java.nospring.ioc.Luoli
longge.(class)=com.partner4java.nospring.ioc.XiaoLong
package com.partner4java.spring.ioc;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
import com.partner4java.nospring.ioc.Girl;
import com.partner4java.nospring.ioc.XiaoLong;
public class HelloWorld1 {
public static void main(String[] args) throws InterruptedException {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(
beanFactory);
beanDefinitionReader.loadBeanDefinitions("boy2girlspring1.properties");
Girl baifumei = (Girl) beanFactory.getBean("baifumei");
Girl luoli = (Girl) beanFactory.getBean("luoli");
XiaoLong longge = (XiaoLong) beanFactory.getBean("longge");
longge.setGirl(baifumei);
longge.closeLight();
System.out.println("休息下");
TimeUnit.SECONDS.sleep(2);
longge.setGirl(luoli);
longge.closeLight();
}
}
新建文件boy2girlspring2.properties:
baifumei.(class)=com.partner4java.nospring.ioc.Baifumei
luoli.(class)=com.partner4java.nospring.ioc.Luoli
longge1.(class)=com.partner4java.nospring.ioc.XiaoLong
longge2.(class)=com.partner4java.nospring.ioc.XiaoLong
longge1.girl(ref)=baifumei
longge2.girl(ref)=luoli
package com.partner4java.spring.ioc;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
import com.partner4java.nospring.ioc.Boy;
public class HelloWorld2 {
public static void main(String[] args) throws InterruptedException {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions("boy2girlspring2.properties");
Boy longge1 = (Boy) beanFactory.getBean("longge1");
longge1.closeLight();
TimeUnit.SECONDS.sleep(2);
Boy longge2 = (Boy) beanFactory.getBean("longge2");
longge2.closeLight();
}
}
基礎類:
小結(白話):
再議Spring IoC,前面我們簡單的helloworld了下,先有個直觀的認識 下面我們繼續IoC (通過這幾個demo是不是對IoC有些直觀的認識了?)
控制反轉類型:
依賴拖拽HelloWorld:
上下文依賴查找(CDL):
依賴注入方式:
在構造方法依賴注入中,組件的依賴從他們的構造方法中提供。組件聲明一個或一組構造方法,將他們的依賴作爲參數,控制反轉容器就會降依賴在組件實例化時傳給他。
在設置方法依賴注入中,控制反轉容器通過JavaBean風格的設置方法爲組件注入依賴。一個組件的設置方法向反轉容器公開一組依賴。
XmlBeanFactory:
我們前面利用Properties形式的配置改成XML格式:
在類路徑下創建文件:
/META-INF/spring/helloworld.xml
1、交給spring管理的基本單位<bean>;
2、一般需要一個標識id屬性來進行區分;
3、然後class指定<bean>標籤要交給Spring管理哪個類;
4、當你要利用依賴注入來實現某屬性的賦值,可利用<property>標籤;
5、<property>標籤的name屬性爲對應調用的POJO的set方法,ref爲一個bean的id(也就是傳入的賦值類 -- 此類也是Spring管理的)。
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="baifumei" class="com.partner4java.nospring.ioc.Baifumei" />
<bean id="luoli" class="com.partner4java.nospring.ioc.Luoli" />
<bean id="xiaoLong" class="com.partner4java.nospring.ioc.XiaoLong">
<property name="girl" ref="baifumei" />
</bean>
</beans>
調用:
package com.partner4java.spring.ioc;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import com.partner4java.nospring.ioc.Boy;
public class XmlBeanFactoryDemo {
public static void main(String[] args) {
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(
"/META-INF/spring/helloworld.xml"));
Boy xiaoLong = beanFactory.getBean("xiaoLong", Boy.class);
xiaoLong.closeLight();
}
}
叨叨兩句配置文件:
再來倆demo,構造器注入和set注入
public class Person {
private String personName;
private String address;
public Person(String personName, String address) {
super();
this.personName = personName;
this.address = address;
}
public void setPersonName(String personName) {
this.personName = personName;
}
....
創建文件:/META-INF/spring/constructor.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" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="person" class="com.partner4java.spring.ioc.constructor.Person">
<constructor-arg name="address" value="高鐵1號1號線"/>
<constructor-arg name="personName" value="高鐵1號"/>
</bean>
</beans>
調用: public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/constructor.xml");
System.out.println(applicationContext.getBean("person"));
}
爲Bean配置集合:
爲集合元素指定數據類型 :
默認情況下,Spring將集合中所有元素作爲字符串對待。
如果你不打算將集合元素作爲字符串使用,就必須爲他們指定數據類型。
可以使用<value>的type指定,也可以在集合標記中指定value-type
使用工廠Bean和Utility Schema定義集合 :
使用基本集合標記定義集合時,你不能指定集合的實體類,例如LinkedList、TreeSet或TreeMap,而且,你不能通過將集合定義爲可供其他Bean引用的單獨Bean在不同的Bean中共享集合。
兩種方式
1、使用對應的集合工廠Bean,如ListFactoryBean、SetFactoryBean和MapFactoryBean。
2、引入util schema中使用集合標記,如<util:list>、<util:set>和<util:map>。
bean的實例化模式:
非單例實例化模式:
選擇一種實例化模式:
使用非單例:
親,IoC知道是啥了麼? 下面倆小時我們來具體說下各種基本使用 begin…
用依賴檢查屬性:
解決方案(依賴檢查):
用@Required註解檢查屬性:
Spring的依賴檢查功能僅能檢查某些類型的所有屬性。他的靈活性不夠,不能僅檢查特定的屬性。
RequiredAnnotationBeanPostProcessor是一個Spring bean後處理器,檢查帶有@Required註解的所有bean屬性是否設置。
bean後處理器是一類特殊的Spring bean,能夠在每個Bean初始化之前執行附加的工作。
爲了啓用這個Bean後處理器進行屬性檢查,必須在Spring IoC容器中註冊他。
(後處理器是什麼我們後面會講到)
檢查不合格後報錯:
Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'username'is required for bean 'user'
用法:
1、xml加上<context:annotation-config />
2、在你要檢測的字段上加上註解
package com.partner4java.spring.ioc.required;
import org.springframework.beans.factory.annotation.Required;
public class User {
private String username;
private String password;
public User() {
super();
}
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
@Required
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}
<?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:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config />
<bean id="user" class="com.partner4java.spring.ioc.required.User">
</bean>
</beans>
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions("/META-INF/spring/required.xml");
System.out.println(beanFactory.getBean("user"));
(插曲)引入ApplicationContext:
ApplicationContext的實現類:
package com.partner4java.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RequiredDemo {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/required.xml");
System.out.println(applicationContext.getBean("user"));
}
}
這下是不是看到報錯了?
(插曲) ApplicationContext不多說(有興趣看前頁地址),記住就行了,他的Bean容器還是藉助了我們前面的DefaultListableBeanFactory,然後自己又加了一些高級特性。
使用ApplicationContextAware:
替換@Required:
package com.partner4java.spring.ioc.required;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NotNull {
}
修改POJO裏的註解:
// @Required
@NotNull
public void setUsername(String username) {
this.username = username;
}
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
<property name="requiredAnnotationType" value="com.partner4java.spring.ioc.required.NotNull"/>
</bean>
這樣Spring就會自動掃描這個配置,知道你加了一個自定義註解
解析依賴:
package com.partner4java.spring.ioc.dependson;
public class A {
public A(){
System.out.println("hello A");
}
}
package com.partner4java.spring.ioc.dependson;
public class B {
public B() {
System.out.println("hello B");
}
}
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="b" class="com.partner4java.spring.ioc.dependson.B" depends-on="a"/>
<bean id="a" class="com.partner4java.spring.ioc.dependson.A"/>
</beans>
來,自己寫一個Test加載一下,看看他們的打印順序。
解決構造程序歧義:
當你爲Bean指定一個或者多個構造程序參數時,Spring將視圖在Bean類中查找對應的構造程序,並且傳遞用於Bean實例化的參數。
你可以爲<constructor-arg>元素指定type和index屬性,幫助Spring查找預期的構造程序。
指定Bean引用:
組成應用程序的Bean往往需要互相協作完成應用功能。爲了Bean之間的互相訪問,你必須在Bean配置文件中指定Bean引用。
在Bean配置文件中,你可以用<ref>元素爲Bean屬性或者構造程序參數指定Bean引用。
只需要用<value>元素指定一個值。
自動裝配:
用XML配置自動裝配Bean:
當一個Bean需要訪問另一個Bean時,你可以顯示指定引用裝配他。但是,如果你的容器能夠自動裝配bean,就可以免去手工手工配置裝配的麻煩。
autowire屬性--
no* 不執行自動裝配。你必須顯示的裝配依賴
byName 對於每個Bean屬性,裝配一個同名的bean
byType 對於每個Bean屬性,裝配類型與之兼容的Bean。如果超過一個,將拋出UnsatisfiedDependencyException異常。
Constructor 對於每個構造程序參數,首先尋找與參數兼容的Bean。然後,選擇具有最多參數的構造程序。對於存在歧義的情況,將拋出UnsatisfiedDependencyException異常。
autoetect 如果找到一個沒有參數的默認構造程序,依賴講按照類型自動裝配。否則,將由構造程序自動裝配。
<?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:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="student" class="com.partner4java.spring.ioc.autowire.Student">
<constructor-arg name="name" value="小國"/>
<!-- 此年齡只代表他的智商 -->
<constructor-arg name="age" value="2"/>
</bean>
<bean id="teacher" class="com.partner4java.spring.ioc.autowire.Teacher" autowire="byName"/>
</beans>
在我們的Teacher裏:
public class Teacher {
private Student student;
public void setStudent(Student student) {
this.student = student;
}
我們從Spring中獲取一下teacher會發現,student已經自動傳入。
用@Autowired和@Resource自動裝配Bean :
在Bean配置文件中設置autowire屬性進行的自動裝配將裝配一個Bean的所有屬性。這樣的靈活性不足以緊緊裝配特定的屬性。
而且,你只能通過類型或者名稱自動裝配Bean。
如果這兩種策略都不能滿足你的需求,就必須明確的裝配Bean。
你可以通過@Autowired或者@Resource註解一個設置方法、構造程序、字段甚至任意方法自動裝配特定的屬性。
這意味着你除了設置autowire屬性之外,還有一個能夠滿足需求的選擇。
@Autowired、@Qualifier("mainCatalog")、@Resource(name="myMovieFinder")
public class Teacher {
@Resource(name="student")
private Student student;
public void setStudent(Student student) {
this.student = student;
}
現在試一下,看看效果。
@Autowired
@Autowired
註解可以用於“傳統的”setter 方法,如下例:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
這個註解也可以用於以屬性爲參數/多個參數的方法
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
@Autowired
註解甚至可以用於構造器與字段:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
也可以一種提供來自ApplicationContext
的特殊類型的所有 beans,註解字段或者方法,例如:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
這同樣適用於集合類型:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
甚至是 Maps 也可以這樣註解,只要這個 Map 的 key 類型爲 String
。這個 Map 的 values 應該是已知的類型,並且 keys 應該包含符合 bean 的命名:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
在缺省情況下,當出現0個候選的 beans時自動連接將失敗;缺省行爲把連接方法,構造器,字段假設爲required 的依賴。這樣的行爲如下所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
注意
雖然當 一個類只有一個連接構造器時它將被標記爲 required, 但是還是可以標記多個構造器的。在這種情況下,每一個構造器都有可能被認爲是連接構造器, Spring 將會把依賴關係能夠滿足的構造器認爲是greediest 的構造器。
@Autowired
也能用於總所周知的“可解決的依賴”:BeanFactory
接口,ApplicationContext
接口,ResourceLoader
接口,ApplicationEventPublisher
接口,還有MessageSource
接口。這些接口(還有它們的擴展,例如ConfigurableApplicationContext
或者ResourcePatternResolver
)將可以自動解決依賴,沒有任何特殊必須的其它步驟需要。
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
基於註解的自動連接微調
因爲通過類型的自動連接可能會有多個候選,因此經常需要在選擇過程中加以控制。一種方法去完成這個控制就是使用@Qualifier
註解。在最簡單的情況下,您能夠通過命名方式去實現這個自動連接:
public class MovieRecommender { @Autowired @Qualifier("mainCatalog") private MovieCatalog movieCatalog; // ... }
@Qualifier
註解也能夠被指定爲構造器的參數或者方法的參數:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("mainCatalog") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
@Resource
Spring 也提供了使用 JSR-250 bean 屬性支持的注射方式。這是一種在 Java EE 5 與 Java 6 中普遍使用的方式(例如,在 JSF 1.2 中映射 beans 或者 JAX-WS 2.0 端點),對於Spring 託管的對象 Spring 可以以這種方式支持映射。
@Resource
有一個‘name’屬性,缺省時,Spring 將這個值解釋爲要注射的 bean 的名字。換句話說,如果遵循by-name的語法,如下例:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果沒有顯式地給出名字,缺省的名字將繼承於字段名或者 setter 方法名:如果是字段名,它將簡化或者等價於字段名;如果是 setter 方法名,它將等價於 bean 屬性名。下面這個例子使用名字 "movieFinder" 注射到它的 setter 方法:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
繼承Bean配置:
在Spring IoC容器中配置Bean時,你可能擁有超過一個共享某些公用配置的Bean,比如屬性和<bean>元素中的屬性。你常常必須爲多個Bean重複這些配置
只作爲模板而不能檢索,必須將abstract設置爲true,要求spring不實例化這個bean。
並不是所有在父<bean>元素中定義的屬性都將被繼承,例如,autowire和dependency-check屬性不會從父bean中繼承
親,我還是感覺挺麻煩怎麼辦?還要配置好多XML
從Classpath中掃描組件:
爲了便於Spring IoC容器對組件的管理,你需要在Bean配置中逐個聲明他們。
但是,如果Spring能夠自動地檢測你的組件而不需要手工配置,將大大節省你的工作量。
Spring提供了一個強大的功能--組件掃描
<?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:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:component-scan base-package="com.partner4java.spring.ioc.annotation" />
</beans>
第二步:給自動掃描的文件加上註解package com.partner4java.spring.ioc.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Baby {
@Value("kiss")
private String name;
@Value("2")
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Baby [name=" + name + ", age=" + age + "]";
}
}
package com.partner4java.spring.ioc.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class Mother {
@Resource(name = "baby")
private Baby baby;
public void setBaby(Baby baby) {
this.baby = baby;
}
@Override
public String toString() {
return "Mother [baby=" + baby + "]";
}
}
就可以用了:
package com.partner4java.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationDemo {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/annotation.xml");
System.out.println(applicationContext.getBean("baby"));
System.out.println(applicationContext.getBean("mother"));
// 後臺打印:
// Baby [name=kiss, age=2]
// Mother [baby=Baby [name=kiss, age=2]]
}
}
註解詳解:
@Component
和更多典型化註解
從Spring 2.0開始,引入了@Repository
註解, 用它來標記充當儲存庫(又稱 Data Access Object或DAO)角色或典型的類。
Spring 2.5引入了更多典型化註解(stereotype annotations): @Component
、@Service
和@Controller
。@Component
是所有受Spring管理組件的通用形式; 而@Repository
、@Service
和@Controller
則是@Component
的細化,
用來表示更具體的用例(例如,分別對應了持久化層、服務層和表現層)。也就是說, 你能用@Component
來註解你的組件類, 但如果用@Repository
、@Service
或@Controller
來註解它們,你的類也許能更好地被工具處理,或與切面進行關聯。
例如,這些典型化註解可以成爲理想的切入點目標。當然,在Spring Framework以後的版本中,@Repository
、@Service
和@Controller
也許還能攜帶更多語義。如此一來,如果你正在考慮服務層中是該用@Component
還是@Service
,
那@Service
顯然是更好的選擇。同樣的,就像前面說的那樣,@Repository
已經能在持久化層中進行異常轉換時被作爲標記使用了。
自動檢測組件
Spring可以自動檢測“被典型化”(stereotyped)的類,在ApplicationContext
中註冊相應的BeanDefinition
。例如,下面的這兩個類就滿足這種自動檢測的要求:
@Service public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
要檢測這些類並註冊相應的bean,需要在XML中包含以下元素,其中'basePackage'是兩個類的公共父包 (或者可以用逗號分隔的列表來分別指定包含各個類的包)。
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="org.example"/> </beans>
此外,在使用組件掃描元素時,AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
會隱式地被包括進來。 也就是說,連個組件都會被自動檢測並織入 - 所有這一切都不需要在XML中提供任何bean配置元數據。
注意
通過加入值爲'false'的annotation-config屬性可以禁止註冊這些後置處理器。
使用過濾器自定義掃描
默認情況下,用@Component
、
@Repository
、@Service
或
@Controller
(或本身使用了@Component
註解的自定義註解) 註解的類是唯一會被檢測到的候選組件。但是可以很方便地通過自定義過濾器來改變並擴展這一行爲。 可以用'component-scan
'的include-filter或exclude-filter子元素來進行添加。
每個過濾器元素都要求有'type
'和'expression
'屬性。 下面給出了四個已有的可選過濾器。
表 3.7. 過濾器類型
過濾器類型 | 表達式範例 |
---|---|
annotation |
|
assignable |
|
regex |
org\.example\.Default.* |
aspectj |
|
下面這個XML配置會忽略所有的@Repository
註解並用“stub”儲存庫代替。
<beans ...> <context:component-scan base-package="org.example"> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
注意
你也可以用<component-scan/>元素的use-default-filters="false" 屬性來禁用默認的過濾器。這會關閉對使用了@Component
、@Repository
、@Service
或@Controller
的類的自動檢測。
自動檢測組件的命名
當一個組件在某個掃描過程中被自動檢測到時,會根據那個掃描器的BeanNameGenerator
策略生成它的bean名稱。默認情況下,任何包含name
值的Spring“典型”註解 (@Component
、@Repository
、@Service
和@Controller
)
會把那個名字提供給相關的bean定義。如果這個註解不包含name
值或是其他檢測到的組件 (比如被自定義過濾器發現的),默認bean名稱生成器會返回小寫開頭的非限定(non-qualified)類名。 例如,如果發現了下面這兩個組件,它們的名字會是'myMovieLister'和'movieFinderImpl':
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
注意
如果你不想使用默認bean命名策略,可以提供一個自定義的命名策略。首先實現
BeanNameGenerator
接口,確認包含了一個默認的無參數構造方法。然後在配置掃描器時提供一個全限定(fully-qualified)類名:
<beans ...> <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator" /> </beans>
作爲一條常規,當其他組件可能會顯式地引用一個組件時可以考慮用註解來指定名稱。 另一方面,當容器負責織入時,自動生成的名稱就足夠了。
爲自動檢測的組件提供一個作用域
通常受Spring管理的組件,默認或者最常用的作用域是“singleton”。然而,有時也會需要其他的作用域。 因此Spring 2.5還引入了一個新的@Scope
註解。只要在註解中提供作用域的名稱就行了, 比方說:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
各種特色方式的Bean創建 (因爲有時候,我們的bean並不是 普通的形態):
使用Spring的FactoryBean創建bean:
你可能希望用Spring的工廠Bean在Spring IoC容器中創建Bean。
工廠Bean(Factory Bean)是作爲創建IoC容器中其他Bean的工廠的一個FactoryBean。概念上,工廠Bean與工廠方法非常類似,但是他是Bean構造期間可以Spring IoC容器識別爲Spring專用Bean。
工廠Bean的要求是實現FactoryBean接口。爲了方便,提供了抽象模板類AbstractFactoryBean供你擴展。
工廠Bean主要用於實現框架機制。如:
·在JNDI中查找對象(例如一個數據源)時,你可以使用JndiObjectFactoryBean。
·使用經典Spring AOP爲一個Bean創建代理時,可以使用ProxyFactoryBean。
·在IoC容器中創建一個Hibernate會話工廠時,可以使用LocalSessionFactoryBean。
儘管你很少有必要編寫自定義的工廠Bean,但是會發現通過一個實例來理解其內部機制很有幫助。
通過擴展AbstractFactoryBean類,你的工廠bean能夠重載createInstance()方法以創建目標Bean實例。
此外,你必須getObjectType()方法中返回目標Bean的類型,是自動裝配(Auto-wiring)功能正常工作。
名稱之前添加&,可以得到工廠Bean的實例。
package com.partner4java.spring.ioc.factorybean;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import com.partner4java.spring.ioc.required.User;
public class UserFactoryBean extends AbstractFactoryBean<User> {
private String username;
private String password;
public UserFactoryBean(String username, String password) {
super();
this.username = username;
this.password = password;
}
@Override
public Class<?> getObjectType() {
return User.class.getClass();
}
@Override
protected User createInstance() throws Exception {
return new User(username, password);
}
}
<bean id="userFactoryBean" class="com.partner4java.spring.ioc.factorybean.UserFactoryBean">
<constructor-arg name="password" value="123" />
<constructor-arg name="username" value="hello" />
</bean>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/factorybean.xml");
System.out.println(applicationContext.getBean("userFactoryBean"));
System.out.println(applicationContext.getBean("&userFactoryBean"));
使用FactoryBean接口:
package com.partner4java.spring.ioc.factorybean;
import java.security.MessageDigest;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
public class MessageDigestFactoryBean implements FactoryBean<MessageDigest>,
InitializingBean {
private MessageDigest messageDigest;
private String algorithm;
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
@Override
public void afterPropertiesSet() throws Exception {
this.messageDigest = MessageDigest.getInstance(algorithm);
}
@Override
public MessageDigest getObject() throws Exception {
return (MessageDigest) messageDigest.clone();
}
@Override
public Class<?> getObjectType() {
return messageDigest.getClass();
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="messageDigest" class="com.partner4java.spring.ioc.factorybean.MessageDigestFactoryBean">
<property name="algorithm" value="MD5"/>
</bean>
調用靜態工廠方法創建Bean:
public class ProductCreator {
public static Product createProduct(int productId){
if(1 == productId){
return new Product("xiaomei", 16);
}else if(2 == productId){
return new Product("xiaolang", 15);
}
throw new IllegalArgumentException("Unknown product");
}
}
<bean id="productCreator1" class="com.partner4java.spring.factorymethod.ProductCreator"
factory-method="createProduct">
<constructor-arg value="1" />
</bean>
<bean id="productCreator2" class="com.partner4java.spring.factorymethod.ProductCreator"
factory-method="createProduct">
<constructor-arg value="2" />
</bean>
@Test
public void testFactoryMethod(){
System.out.println(applicationContext.getBean("productCreator1"));
System.out.println(applicationContext.getBean("productCreator2"));
}
調用一個實例工廠方法創建Bean:
package com.partner4java.spring.factorybean;
import java.util.Map;
import com.partner4java.spring.factorymethod.Product;
public class ProductCreator {
private Map<String, Product> products;
public void setProducts(Map<String, Product> products) {
this.products = products;
}
public Product createProduct(String productId){
Product product = products.get(productId);
if(product != null){
return product;
}
throw new IllegalArgumentException("Unknown product");
}
}
<bean id="productCreator" class="com.partner4java.spring.factorybean.ProductCreator">
<property name="products">
<map>
<entry key="gaofumei">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg value="gaofumei" name="name"/>
<constructor-arg value="100" name="price"/>
</bean>
</entry>
<entry key="xiaoneinv">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg value="xiaoneinv" name="name"/>
<constructor-arg value="200" name="price"/>
</bean>
</entry>
</map>
</property>
</bean>
<bean id="gaofumei" factory-bean="productCreator" factory-method="createProduct">
<constructor-arg value="gaofumei"/>
</bean>
<bean id="xiaoneinv" factory-bean="productCreator" factory-method="createProduct">
<constructor-arg value="xiaoneinv"/>
</bean>
@Test
public void testFactoryMethod(){
System.out.println(applicationContext.getBean("gaofumei"));
System.out.println(applicationContext.getBean("xiaoneinv"));
從靜態字段中聲明bean:
工作原理:
public class ProductConstant {
public static Product gaofumei = new Product("gaofumei", 100);
public static Product xiaoneinv = new Product("xiaoneinv", 200);
}
<util:constant id="gaofumei"
static-field="com.partner4java.spring.constant.ProductConstant.gaofumei" />
<util:constant id="xiaoneinv"
static-field="com.partner4java.spring.constant.ProductConstant.xiaoneinv" />
@Test
public void testFactoryMethod(){
System.out.println(applicationContext.getBean("gaofumei"));
System.out.println(applicationContext.getBean("xiaoneinv"));
}
從對象屬性中聲明bean:
工作原理:
public class ProductProperty {
private Product gaofumei;
private Product xiaonennv;
public Product getGaofumei() {
return gaofumei;
}
public void setGaofumei(Product gaofumei) {
this.gaofumei = gaofumei;
}
public Product getXiaonennv() {
return xiaonennv;
}
public void setXiaonennv(Product xiaonennv) {
this.xiaonennv = xiaonennv;
}
}
<bean id="productProperty" class="com.partner4java.spring.property.ProductProperty">
<property name="gaofumei">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg name="name" value="gaofumei"/>
<constructor-arg name="price" value="100.1"/>
</bean>
</property>
<property name="xiaonennv">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg name="name" value="xiaonennv"/>
<constructor-arg name="price" value="200.2"/>
</bean>
</property>
</bean>
<util:property-path id="gaofumei" path="productProperty.gaofumei"/>
<util:property-path id="xiaonennv" path="productProperty.xiaonennv"/>
好吧,親,感覺如何?好玩麼?
下面,我們來看下IoC的高級形態(其實也不是很高級)
Spring對應用程序可移植性的影響:
使Bean感知容器:
工作原理:
public class HelloBeanNameAware implements BeanNameAware {
private String name;
@Override
public void setBeanName(String name) {
this.name = name;
}
@Override
public String toString() {
return "HelloBeanNameAware [name=" + name + "]";
}
}
public class HelloBeanFactoryAware implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public String toString() {
return "HelloBeanFactoryAware [beanFactory=" + beanFactory.getClass().getSimpleName() + "]";
}
}
public class HelloApplicationContextAware implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public String toString() {
return "HelloApplicationContextAware [applicationContext="
+ applicationContext.getClass().getSimpleName() + "]";
}
}
<bean id="helloBeanNameAware" class="com.partner4java.spring.aware.HelloBeanNameAware" />
<bean id="helloBeanFactoryAware" class="com.partner4java.spring.aware.HelloBeanFactoryAware" />
<bean id="helloApplicationContextAware" class="com.partner4java.spring.aware.HelloApplicationContextAware" />
@Test
public void testAware() throws InterruptedException{
System.out.println(applicationContext.getBean("helloBeanNameAware"));
System.out.println(applicationContext.getBean("helloBeanFactoryAware"));
System.out.println(applicationContext.getBean("helloApplicationContextAware"));
// 後臺打印:
// HelloBeanNameAware [name=helloBeanNameAware]
// HelloBeanFactoryAware [beanFactory=DefaultListableBeanFactory]
// HelloApplicationContextAware
// [applicationContext=ClassPathXmlApplicationContext]
}
管理bean的生命週期:
package com.partner4java.spring.ioc.life;
public class SimpleBean {
public void init(){
System.out.println("SimpleBean init");
}
public void des(){
System.out.println("SimpleBean des");
}
}
<bean id="simpleBean" class="com.partner4java.spring.ioc.life.SimpleBean"
init-method="init" destroy-method="des" lazy-init="true"/>
方式二:實現接口
實現InitializingBean接口:
決議順序:
afterPropertiesSet方法和類的繼承:
實現DisposableBean接口
public class Work implements InitializingBean,DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("離職");
}
public void vork(){
System.out.println("working");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("入職");
}
}
<bean id="work" class="com.partner4java.spring.initdes.Work"
scope="prototype" />
創建Bean後處理器:
(具體可參照:http://blog.csdn.net/partner4java/article/details/6973782)
問題:
你希望在Spring IoC容器中註冊自己的插件,在構造期間處理Bean實例。
解決方案:
Bean後處理器允許在初始化回調方法前後進行附加的Bean處理。
Bean後處理器的主要特性是逐個處理IoC容器中的所有Bean實例,而不是單個Bean實例。
一般,Bean後處理器用於檢查Bean屬性有效性,或者根據特殊條件修改Bean屬性。
Bean後處理器的基本要求是實現BeanPostProcessor接口。
你可以實現postProcessBeforeInitialization()和postProcessAfterInitialization()方法,在初始化回調方法前後處理所有Bean。
然後,Spring將在調用初始化回調方法前後向這兩個方法傳遞每個Bean實例。
步驟如下:
1、構造程序或者工廠方法創建Bean實例。
2、爲Bean屬性設置值和Bean引用。
3、調用感知接口中定義的設置方法。
4、將Bean實例傳遞給每個Bean前置處理器中的postProcessBeforeInitialization方法。
5、調用初始化回調方法。
6、講Bean實例傳遞給每個Bean後處理器中的postProcessAfterInitialization方法。
7、Bean準備就緒,可以使用。
8、容器關閉時,調用析構回調方法。
使用Bean工廠爲IoC容器時,Bean後處理器只能編程註冊,更準確的講是通過addBeanPostProcessor()方法註冊。
但是,如果你使用一個應用上下文,註冊將很簡單,只要在Bean配置文件中聲明處理器實例,他就會自動註冊。
工作原理:
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println(beanName + " say hello world!");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println(beanName + " say good buy!");
return bean;
}
}
<!-- 要在應用上線文中註冊一個Bean後處理器,只要在Bean配置文件中聲明他的一個實例就可以了。 應用上下文能夠自動檢測誰實現了BeanPostProcessor接口,並且註冊他一處理容器中的所有其他Bean實例 -->
<bean class="com.partner4java.spring.postprocess.LogBeanPostProcessor" />
<bean id="helloResourceLoader" class="com.partner4java.spring.resource.HelloResourceLoader"
init-method="showResource" />
如果以配置文件的格式設置init-method,對BeanPostProcesser的執行沒有什麼威脅,BeanPostProcesser還是會先執行。
但是如果,以@PreDestroy和@PostConstruct的形式,BeanPostProcesser講不能正常工作,因爲BeanPostProcesser的默認優先級低於CommonAnnotationBeanPostProcesser。
不過可以同時實現PriorityOrdered接口來指定執行順序。
Spring BeanFactoryPostProcessor類 (“排隊”“後”控制修改beanfactory管理的信息--如一些配置信息)
http://blog.csdn.net/partner4java/article/details/6969417
(比較晚了,明早還有事,後面可能整理的有點亂,改天再修改下)
《partner4java 講述Spring入門》之第一步:Spring概述與Spring IoC
http://blog.csdn.net/partner4java/article/details/8194747
《partner4java 講述Spring入門》之第二步:Spring AOP
http://blog.csdn.net/partner4java/article/details/8239721