1.1.1 Spring簡介
Spring是一個(gè)開源的控制反轉(zhuǎn)(IoC)和面向且切面(AOP)的容器框架斑响。
IOC控制反轉(zhuǎn):應(yīng)用本身不負(fù)責(zé)以來對象的創(chuàng)建和維護(hù),以來對象的創(chuàng)建及維護(hù)由外部容器負(fù)責(zé)钳榨。這樣控制權(quán)就轉(zhuǎn)移到了外部容器。
Dependency Injection依賴注入:在運(yùn)行期纽门,由外部容器動態(tài)地將以來對象注入到組件中薛耻。
事務(wù)控制全部交給spring處理,不用手工編寫事務(wù)的創(chuàng)建和提交
1.1.2 Spring配置和搭建
Eclipse下的配置
下載了spring-framework-2.5.6包赏陵,把dist文件夾下的spring.jar添加到工程饼齿,還要添加一個(gè)jar是common-logging的饲漾,在hibernate學(xué)習(xí)的時(shí)候下載過了,也添加進(jìn)去缕溉。
在src目錄下新建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-2.5.xsd">
</beans>
初始化spring容器
ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"});//可以通過數(shù)組傳遞多個(gè)配置文件考传。
下面使用JUnit4進(jìn)行測試,new->JUnit Test
(不要勾選setUpBeforeClass)
SprintTest.java
SprintTest.java
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
}
}
運(yùn)行以后測試成功
下面寫一個(gè)bean
PersonService.java 放在src中service包下
package service.impl;
public interface PersonService {
public abstract void save();
}
PersonServiceBean.java 放在src中service.impl包下
package service;
import service.impl.PersonService;
public class PersonServiceBean implements PersonService {
/* (non-Javadoc)
* @see service.impl.PersonService#save()
*/
public void save(){
System.out.println("save方法");
}
}
下面配置beans.xml
在沒聯(lián)網(wǎng)的情況下需要添加提示文件才會出現(xiàn)關(guān)鍵字提示证鸥。
windows->perferences->XML->XML Catalog
add
Location:在spring包下找到dist/resources/spring-beans-2.5.xsd
Key Type:Namespace Name
Key:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
完成添加僚楞。
在beans標(biāo)簽中間添加
<bean id="personService" class="service.PersonServiceBean"></bean>
使用bean標(biāo)簽的時(shí)候,id和name的區(qū)別:id不可以包含特殊字符枉层,name可以泉褐。
SpringTest.java
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.impl.PersonService;
public class SpringTest {
@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)context.getBean("personService");
personService.save();
}
}
運(yùn)行成功
1.1.3 Spring管理bean的原理
簡單的一套管理bean的代碼,就是一個(gè)模擬的ApplicationContext類鸟蜡。
主要方法有兩個(gè)
this.readXML(filename);//讀取XML配置文件
this.instanceBeans();//實(shí)例化bean
主要成員變量
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();//保存XML中的bean信息膜赃,包括id和class,組成BeanDefinition類
private Map<String, Object> sigletons = new HashMap<String, Object>();//保存實(shí)例化以后的bean
具體的代碼實(shí)現(xiàn)不細(xì)化研究了揉忘,大概流程:
首先使用dom4j讀取XML中的bean標(biāo)簽跳座,把id和class保存在beanDifines數(shù)組中,然后在instanceBeans()方法中遍歷beanDifines數(shù)組泣矛,取出類名疲眷,利用發(fā)射技術(shù)進(jìn)行實(shí)例化,如Class.forName(beanDefinition.getClassName()).newInstance());并把實(shí)例化對象保存在sigletons的哈希表中乳蓄。完成實(shí)例化
接下來就是簡單的get方法來取出對應(yīng)的bean供其他類調(diào)用咪橙。
可以發(fā)現(xiàn)bean的實(shí)例化就是在創(chuàng)建ApplicationContext對象的時(shí)候,通過構(gòu)造方法進(jìn)行實(shí)例化的虚倒。
代碼:
ItcastClassPathApplicationContext.java
package junit.test;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
public class ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
public ItcastClassPathXMLApplicationContext(String filename){
this.readXML(filename);
this.instanceBeans();
}
/**
* 完成bean的實(shí)例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 讀取xml配置文件
* @param filename
*/
private void readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document=null;
try{
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空間
XPath xsub = document.createXPath("http://ns:beans/ns:bean");//創(chuàng)建beans/bean查詢路徑
xsub.setNamespaceURIs(nsMap);//設(shè)置命名空間
List<Element> beans = xsub.selectNodes(document);//獲取文檔下所有bean節(jié)點(diǎn)
for(Element element: beans){
String id = element.attributeValue("id");//獲取id屬性值
String clazz = element.attributeValue("class"); //獲取class屬性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
beanDefines.add(beanDefine);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 獲取bean實(shí)例
* @param beanName
* @return
*/
public Object getBean(String beanName){
return this.sigletons.get(beanName);
}
}
BeanDefinition.java
package junit.test;
public class BeanDefinition {
private String id;
private String className;
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
測試代碼
ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService");
personService.save();
1.1.4 三種實(shí)例化bean的方式
1.使用類構(gòu)造器實(shí)例化
<bean id="personService" class="service.impl.PersonServiceBean"></bean>
2.使用靜態(tài)工廠方法實(shí)例化
beans.xml
<bean id="personService2" class="service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"></bean>
PersonServiceBeanFactory.java
1. public static PersonServiceBean createPersonServiceBean(){
2. return new PersonServiceBean();
3. }
3.使用實(shí)例工廠方法實(shí)例化
beans.xml
<bean id="personServiceFactory" class="service.impl.PersonServiceBeanFactory"></bean>
<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"></bean>
PersonServiceBeanFactory.java
1. public PersonServiceBean createPersonServiceBean2(){
2. return new PersonServiceBean();
3. }
三種方式測試成功
絕大部分都使用第一種實(shí)例化方法
1.1.5 spring管理bean的作用域
來看一段代碼
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService1 = (PersonService)context.getBean("personService");
PersonService personService2 = (PersonService)context.getBean("personService");
System.out.println(personService1==personService2);
輸出結(jié)果為true美侦,說明getBean方法獲取的是單實(shí)例
那么就來看bean的作用域
bean標(biāo)簽的scope屬性來設(shè)置
singleton
默認(rèn)情況下的單例模式,每次調(diào)用getBean方法獲取的都是同一個(gè)bean對象
默認(rèn)情況喜愛會在容器啟動時(shí)初始化bean魂奥,但我們可以指定Bean節(jié)點(diǎn)的lazy-init屬性來延遲初始化bean菠剩,這時(shí)候,只有第一次獲取bean才會初始化bean耻煤。
<bean id="xxx" class="xxx" lazy_init="true"/>
如果對所有bean都應(yīng)用延遲初始化
<beans default-lazy-init="true"...>
prototype
每次從容器獲取bean都是一個(gè)新的對象
request
session
global session
1.1.6 spring管理bean的生命周期
從前面可以看到具壮,
singleton模式下bean的實(shí)例化的時(shí)機(jī)是在ApplicationContext實(shí)例化的時(shí)候進(jìn)行實(shí)例化,然而設(shè)置lazy-init=true的情況下在getBean方法調(diào)用的時(shí)候進(jìn)行實(shí)例化哈蝇。
prototype模式下bean的實(shí)例化時(shí)機(jī)是在getBean方法調(diào)用的時(shí)候進(jìn)行實(shí)例化棺妓。
如果我們需要在bean初始化的時(shí)候,打開數(shù)據(jù)庫資源等連接炮赦,那么指定一個(gè)初始化方法怜跑,這樣在bean實(shí)例化的時(shí)候就會自動初始化。同樣的指定一個(gè)銷毀方法。
beans.xml
<bean id="personService" class="service.impl.PersonServiceBean" init-method="init" destroy-method="destroy"></bean>
PersonServiceBean.java
public void init(){
System.out.println("inti初始化方法");
}
public void destroy(){
System.out.println("destroy銷毀方法");
}
SprintTest.java
1.AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- context.close();
測試結(jié)果輸出
inti初始化方法destroy銷毀方法
lazy-init應(yīng)該是一個(gè)優(yōu)化spring很好的東西性芬,就像hibernate里的懶加載峡眶,有些bean可能一直沒有用到過,根本沒必要初始化植锉,但是視頻里說盡量使用lazy-init=false辫樱,為了發(fā)現(xiàn)所有bean可能出現(xiàn)的錯(cuò)誤,難道測試的時(shí)候要這么做俊庇?另外JUnit的測試也不是很明白狮暑。。暇赤。感覺和一般的差不多啊
1.1.7 剖析Spring依賴注入的原理
通過set方法注入
package dao.impl;
import dao.PersonDao;
public class PersonDaoBean implements PersonDao {
/* (non-Javadoc)
* @see dao.impl.PersonDao#add()
*/
public void add(){
System.out.println(this.getClass().getName()+" add方法");
}
}
PersonServiceBean.java
? package service.impl;
?
? import dao.PersonDao;
? import service.PersonService;
?
?
? public class PersonServiceBean implements PersonService {
? private PersonDao personDao;
?
? public PersonDao getPersonDao() {
? return personDao;
? }
?
? public void setPersonDao(PersonDao personDao) {
? this.personDao = personDao;
? }
?
? public void save(){
? personDao.add();
? }
?
? public void init(){
? System.out.println("inti初始化方法");
? }
?
? public void destroy(){
? System.out.println("destroy銷毀方法");
? }
? }
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-2.5.xsd">
?
? <bean id="personDao" class="dao.impl.PersonDaoBean"></bean>
? <bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
? <property name="personDao" ref="personDao"></property>
? </bean>
?
? </beans>
SpringTest.java
? package junit.test;
?
? import org.junit.Test;
? import org.springframework.context.support.AbstractApplicationContext;
? import org.springframework.context.support.ClassPathXmlApplicationContext;
?
? import service.PersonService;
?
? public class SpringTest {
? @Test
? public void instanceSpring(){
? AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
? PersonService personService = (PersonService)context.getBean("personService");
? personService.save();
? context.close();
?
? }
?
? }
運(yùn)行結(jié)果:
dao.impl.PersonDaoBean add方法
還有一種內(nèi)部bean的注入方式
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
· <property name="personDao">
· <bean class="dao.impl.PersonDaoBean"></bean>
· </property>
· </bean>
即在property標(biāo)簽內(nèi)部添加bean標(biāo)簽心例,在bean標(biāo)簽里寫出類名即可。效果一樣鞋囊。
實(shí)際上spring的操作流程是吧bean中的property也保存在一個(gè)數(shù)組中止后,初始化的時(shí)候遍歷數(shù)組,找到需要注入的bean溜腐,利用反射技術(shù)調(diào)用set方法译株,參數(shù)為配置文件中的ref應(yīng)用的bean,就完成了注入挺益。
1.1.8 Spring裝配基本屬性的原理
上一篇的注入都是對象的注入歉糜,這篇來分析基本屬性的注入。
以String類型為例
PersonServiceBean.java中加入
private String name;
beans.xml
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
· <property name="personDao">
· <bean class="dao.impl.PersonDaoBean"></bean>
· </property>
· <property name="name" value="pf"></property>
· </bean>
其他基本類型類似望众,均采用value屬性方式賦值匪补。
1.1.9 Spring如何裝配各種集合類型的屬性
set:
private Set<String> set;
· <property name="set">
· <set>
· <value>第一個(gè)</value>
· <value>第二個(gè)</value>
· <value>第三個(gè)</value>
· </set>
· </property>
list:
private List<String> list;
· <property name="list">
· <list>
· <value>第一個(gè)</value>
· <value>第二個(gè)</value>
· <value>第三個(gè)</value>
· </list>
· </property>
properties:
private Properties properties;
· <property name="properties">
· <props>
· <prop key="key1">value1</prop>
· <prop key="key2">value2</prop>
· <prop key="key3">value3</prop>
· </props>
· </property>
map:
peivate Map<String,String> map;
· <property name="map">
· <map>
· <entry key="key1" value="value1"></entry>
· <entry key="key2" value="value2"></entry>
· <entry key="key3" value="value3"></entry>
· </map>
· </property>
1.1.10 使用構(gòu)造器裝配屬性
PersonServiceBean.java
? package service.impl;
?
? import java.util.Set;
?
? import dao.PersonDao;
? import service.PersonService;
?
?
? public class PersonServiceBean implements PersonService {
? private PersonDao personDao;
? private String name;
?
? public PersonServiceBean(PersonDao personDao, String name) {
? this.personDao = personDao;
? this.name = name;
? }
? @Override
? public String getName() {
? // TODO Auto-generated method stub
? return this.name;
? }
? @Override
? public PersonDao getPersonDao() {
? // TODO Auto-generated method stub
? return this.personDao;
? }
? @Override
? public void save() {
? // TODO Auto-generated method stub
? System.out.println(this.getClass().getName()+" save方法");
? }
? public void setPersonDao(PersonDao personDao) {
? this.personDao = personDao;
? }
? public void setName(String name) {
? this.name = name;
? }
?
? }
beans.xml
? <bean id="personService" class="service.impl.PersonServiceBean">
?
? <constructor-arg index="0" ref="personDao"></constructor-arg>
? <constructor-arg index="1" value="pf"></constructor-arg>
?
? </bean>
index指明了參數(shù)的位置,加了index就不用加type來說明了烂翰。
1.1.11 用@Resource注解完成屬性裝配
java代碼注入配置夯缺,需要spring解壓文件夾下lib/j2ee/common-annotation.jar這個(gè)庫文件,添加玩以后甘耿,修改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"
? 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:annotation-config/>
? </beans>
由于現(xiàn)在家里上spring的網(wǎng)站總是上不去踊兜,如果要出現(xiàn)標(biāo)簽提示,那么和前面一樣在本地添加spring-context-2.5.xsd
現(xiàn)在配置工作完成了佳恬,現(xiàn)在開始用java代碼來完成注入
@Autowired方式:默認(rèn)按類型裝配捏境,默認(rèn)情況下它要求以來對象必須存在,如果允許null值毁葱,可以設(shè)置它required屬性為false垫言。如果我們想使用按名稱裝配,可以結(jié)合@Qualifier注解一起使用
@Autowired @Qualifier("personDaoBean")
private PersonDao personDao;
@Resource方式:默認(rèn)按名稱裝配倾剿,名稱可以通過@Resource的name屬性指定筷频,如果沒有指定的name屬性,當(dāng)注解標(biāo)注在字段上,即默認(rèn)取字段的名稱作為bean名稱尋找以來對象截驮,當(dāng)注解標(biāo)注在屬性的setter方法上,即迷人取屬性名作為bean名稱尋找以來對象际度。
@Resource(name="personDaoBean")
private PersonDao personDao;
推薦使用@Resource方式葵袭,因?yàn)锧Resource是j2ee里的一個(gè)注解,而@AutoWired是spring里的注解乖菱,使用@Resource可以降低與框架的耦合度坡锡。
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"
? 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:annotation-config/>
?
? <bean id="personDao" class="dao.impl.PersonDaoBean"/>
? <bean id="personService" class="service.impl.PersonServiceBean"/>
?
? </beans>
PersonServiceBean.java
? package service.impl;
?
? import javax.annotation.Resource;
?
? import dao.PersonDao;
? import service.PersonService;
?
?
? public class PersonServiceBean implements PersonService {
? @Resource private PersonDao personDao;
? private String name;
?
? public PersonServiceBean() {
?
? }
? @Override
? public String getName() {
? // TODO Auto-generated method stub
? return this.name;
? }
? @Override
? public PersonDao getPersonDao() {
? // TODO Auto-generated method stub
? return this.personDao;
? }
? @Override
? public void save() {
? // TODO Auto-generated method stub
? System.out.println(this.getClass().getName()+" save方法");
? }
? public void setPersonDao(PersonDao personDao) {
? this.personDao = personDao;
? }
? public void setName(String name) {
? this.name = name;
? }
? }
運(yùn)行發(fā)現(xiàn)注入成功,string類型就不需要用注解注入了窒所,直接賦值就可以了鹉勒。
另外吧@Resource放在setter方法上也是可以的,效果一樣吵取。
1.1.12 編碼剖析@Resource注解的實(shí)現(xiàn)原理
ItcastResource.java
? package junit.test;
?
? import java.lang.annotation.ElementType;
? import java.lang.annotation.Retention;
? import java.lang.annotation.RetentionPolicy;
? import java.lang.annotation.Target;
?
? @Retention(RetentionPolicy.RUNTIME)
? @Target({ElementType.FIELD, ElementType.METHOD})
? public @interface ItcastResource {
? public String name() default "";
? }
PropertyDefinition .java
? package junit.test;
?
? public class PropertyDefinition {
? private String name;
? private String ref;
? private String value;
?
? public String getValue() {
? return value;
? }
?
? public void setValue(String value) {
? this.value = value;
? }
?
? public PropertyDefinition(String name, String ref, String value) {
? this.name = name;
? this.ref = ref;
? this.value = value;
? }
?
? public String getName() {
? return name;
? }
? public void setName(String name) {
? this.name = name;
? }
? public String getRef() {
? return ref;
? }
? public void setRef(String ref) {
? this.ref = ref;
? }
?
? }
BeanDefinition.java
? package junit.test;
?
? import java.util.ArrayList;
? import java.util.List;
?
? public class BeanDefinition {
? private String id;
? private String className;
? private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>();
?
? public BeanDefinition(String id, String className) {
? this.id = id;
? this.className = className;
? }
? public String getId() {
? return id;
? }
? public void setId(String id) {
? this.id = id;
? }
? public String getClassName() {
? return className;
? }
? public void setClassName(String className) {
? this.className = className;
? }
? public List<PropertyDefinition> getPropertys() {
? return propertys;
? }
? public void setPropertys(List<PropertyDefinition> propertys) {
? this.propertys = propertys;
? }
? }
ItcastClassPathXMLApplicationContext.java
? package junit.test;
?
? import java.beans.Introspector;
? import java.beans.PropertyDescriptor;
? import java.lang.reflect.Field;
? import java.lang.reflect.Method;
? import java.net.URL;
? import java.util.ArrayList;
? import java.util.HashMap;
? import java.util.List;
? import java.util.Map;
?
? import org.apache.commons.beanutils.ConvertUtils;
? import org.dom4j.Document;
? import org.dom4j.Element;
? import org.dom4j.XPath;
? import org.dom4j.io.SAXReader;
? public class ItcastClassPathXMLApplicationContext {
? private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
? private Map<String, Object> sigletons = new HashMap<String, Object>();
?
? public ItcastClassPathXMLApplicationContext(String filename){
? this.readXML(filename);
? this.instanceBeans();
? this.annotationInject();
? this.injectObject();
? }
? /**
? * 通過注解實(shí)現(xiàn)注入依賴對象
? */
? private void annotationInject() {
? for(String beanName : sigletons.keySet()){
? Object bean = sigletons.get(beanName);
? if(bean!=null){
? try {
? PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
? for(PropertyDescriptor properdesc : ps){
? Method setter = properdesc.getWriteMethod();//獲取屬性的setter方法
? if(setter!=null && setter.isAnnotationPresent(ItcastResource.class)){
? ItcastResource resource = setter.getAnnotation(ItcastResource.class);
? Object value = null;
? if(resource.name()!=null && !"".equals(resource.name())){
? value = sigletons.get(resource.name());
? }else{
? value = sigletons.get(properdesc.getName());
? if(value==null){
? for(String key : sigletons.keySet()){
? if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){
? value = sigletons.get(key);
? break;
? }
? }
? }
? }
? setter.setAccessible(true);
? setter.invoke(bean, value);//把引用對象注入到屬性
? }
? }
? Field[] fields = bean.getClass().getDeclaredFields();
? for(Field field : fields){
? if(field.isAnnotationPresent(ItcastResource.class)){
? ItcastResource resource = field.getAnnotation(ItcastResource.class);
? Object value = null;
? if(resource.name()!=null && !"".equals(resource.name())){
? value = sigletons.get(resource.name());
? }else{
? value = sigletons.get(field.getName());
? if(value==null){
? for(String key : sigletons.keySet()){
? if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){
? value = sigletons.get(key);
? break;
? }
? }
? }
? }
? field.setAccessible(true);//允許訪問private字段
? field.set(bean, value);
? }
? }
? } catch (Exception e) {
? e.printStackTrace();
? }
? }
? }
? }
?
? /**
? * 為bean對象的屬性注入值
? */
? private void injectObject() {
? for(BeanDefinition beanDefinition : beanDefines){
? Object bean = sigletons.get(beanDefinition.getId());
? if(bean!=null){
? try {
? PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
? for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
? for(PropertyDescriptor properdesc : ps){
? if(propertyDefinition.getName().equals(properdesc.getName())){
? Method setter = properdesc.getWriteMethod();//獲取屬性的setter方法 ,private
? if(setter!=null){
? Object value = null;
? if(propertyDefinition.getRef()!=null && !"".equals(propertyDefinition.getRef().trim())){
? value = sigletons.get(propertyDefinition.getRef());
? }else{
? value = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());
? }
? setter.setAccessible(true);
? setter.invoke(bean, value);//把引用對象注入到屬性
? }
? break;
? }
? }
? }
? } catch (Exception e) {
? }
? }
? }
? }
? /**
? * 完成bean的實(shí)例化
? */
? private void instanceBeans() {
? for(BeanDefinition beanDefinition : beanDefines){
? try {
? if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
? sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
? } catch (Exception e) {
? e.printStackTrace();
? }
? }
?
? }
? /**
? * 讀取xml配置文件
? * @param filename
? */
? private void readXML(String filename) {
? SAXReader saxReader = new SAXReader();
? Document document=null;
? try{
? URL xmlpath = this.getClass().getClassLoader().getResource(filename);
? document = saxReader.read(xmlpath);
? Map<String,String> nsMap = new HashMap<String,String>();
? nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空間
? XPath xsub = document.createXPath("http://ns:beans/ns:bean");//創(chuàng)建beans/bean查詢路徑
? xsub.setNamespaceURIs(nsMap);//設(shè)置命名空間
? List<Element> beans = xsub.selectNodes(document);//獲取文檔下所有bean節(jié)點(diǎn)
? for(Element element: beans){
? String id = element.attributeValue("id");//獲取id屬性值
? String clazz = element.attributeValue("class"); //獲取class屬性值
? BeanDefinition beanDefine = new BeanDefinition(id, clazz);
? XPath propertysub = element.createXPath("ns:property");
? propertysub.setNamespaceURIs(nsMap);//設(shè)置命名空間
? List<Element> propertys = propertysub.selectNodes(element);
? for(Element property : propertys){
? String propertyName = property.attributeValue("name");
? String propertyref = property.attributeValue("ref");
? String propertyValue = property.attributeValue("value");
? PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref, propertyValue);
? beanDefine.getPropertys().add(propertyDefinition);
? }
? beanDefines.add(beanDefine);
? }
? }catch(Exception e){
? e.printStackTrace();
? }
? }
? /**
? * 獲取bean實(shí)例
? * @param beanName
? * @return
? */
? public Object getBean(String beanName){
? return this.sigletons.get(beanName);
? }
? }
實(shí)際上也就是通過了反射技術(shù)來構(gòu)造對象并且賦值禽额,只是用到了注解的方法,并且利用@interface來構(gòu)造自定義的注解類型皮官。
1.1.13 @Autowire注解與自動裝配
使用了@Autowired的注解方式脯倒,這種默認(rèn)按類型查找符合的bean注入
@Autowired **private** PersonDao personDao;
使用@Qualifier注明bean名稱注入
@Autowired @Qualifier("personDao") **private** PersonDao personDao;
還可以添加required屬性,在沒找到bean的情況下捺氢,如果required為false藻丢,則注入null,required為true摄乒,則報(bào)錯(cuò)悠反。
@Autowired(required=true) @Qualifier("personDao") private PersonDao personDao;
自動裝配:
通過bean標(biāo)簽的autowire屬性來配置,有5種值
no 不使用自動裝配馍佑,必須通過ref元素指定依賴斋否,默認(rèn)設(shè)置。
byName 根據(jù)屬性名自動裝配挤茄。此選項(xiàng)將檢查容器并根據(jù)名字查找與
屬性完全一致的bean如叼,并將其與屬性自動裝配。
byType 如果容器中存在一個(gè)與指定屬性類型相同的bean穷劈,那么將與
該屬性自動裝配笼恰;如果存在多個(gè)該類型bean,那么拋出異
常歇终,并指出不能使用byType方式進(jìn)行自動裝配社证;如果沒有找
到相匹配的bean,則什么事都不發(fā)生评凝,也可以通過設(shè)置
dependency-check="objects"讓Spring拋出異常追葡。
constructor 與byType方式類似,不同之處在于它應(yīng)用于構(gòu)造器參數(shù)。如
果容器中沒有找到與構(gòu)造器參數(shù)類型一致的bean宜肉,那么拋出
異常匀钧。
autodetect 通過bean類的自省機(jī)制(introspection)來決定是使用
constructor還是byType方式進(jìn)行自動裝配。如果發(fā)現(xiàn)默認(rèn)的
構(gòu)造器谬返,那么將使用byType方式之斯。
1.1.14 讓Spring自動掃描和管理Bean
讓Spring自動掃描和管理Bean
<context:component-scan base-package="cn.test"></context:component-scan>
其中base-package為需要掃描的包(含子包)
@Service用于標(biāo)注業(yè)務(wù)層組件,@Controller用于標(biāo)注控制層組件(如struts中的action),@Repository用于標(biāo)注數(shù)據(jù)訪問組件遣铝,即DAO組件佑刷,而@Component泛指組件,當(dāng)組件不好歸類的時(shí)候酿炸,我們可以使用這個(gè)注解進(jìn)行標(biāo)注瘫絮。
bean的默認(rèn)名稱是類名,然后把第一個(gè)字母改為小寫填硕÷笥可以通過@Service("xxx")修改bean名稱。
這種bean默認(rèn)是單例的廷支,如果想改變频鉴,可以使用@Service(“aaaaa”) @Scope(“prototype”)來改變。
還可以通過@PostConstruct @PreDestroy設(shè)置初始化和銷毀的函數(shù)
? @PostConstruct
? public void init(){
? System.out.println("初始化");
? }
?
?
?
? @PreDestroy
? public void destory(){
? System.out.println("開閉資源");
? }
1.1.15 使用JDK中的Proxy技術(shù)實(shí)現(xiàn)AOP功能
通過代理對象來調(diào)用對象的方法恋拍,從而做出權(quán)限控制垛孔。
目標(biāo)對象必須實(shí)現(xiàn)接口才能使用proxy技術(shù)創(chuàng)建代理對象。
PersonService.java
? package cn.pf.aop.service;
?
? public interface PersonService {
? public void save(String name);
? public void update(String name, Integer personId);
? public String getName(Integer personId);
? }
PersonServiceBean.java
? package cn.pf.aop.service.impl;
?
? import cn.pf.aop.service.PersonService;
?
? public class PersonServiceBean implements PersonService {
? private String user = null;
?
? public PersonServiceBean() {
?
? }
?
? public PersonServiceBean(String user) {
? this.setUser(user);
? }
?
? @Override
? public String getName(Integer personId) {
? System.out.println(this.getClass().getName()+" getName方法");
? return "pf";
? }
?
? @Override
? public void save(String name) {
? System.out.println(this.getClass().getName()+" save方法");
? }
?
? @Override
? public void update(String name, Integer personId) {
? System.out.println(this.getClass().getName()+" update方法");
? }
?
? public void setUser(String user) {
? this.user = user;
? }
?
? public String getUser() {
? return user;
? }
?
? }
JDKProxyFactory.java
? package cn.pf.aop;
?
? import java.lang.reflect.InvocationHandler;
? import java.lang.reflect.Method;
? import java.lang.reflect.Proxy;
?
? import cn.pf.aop.service.impl.PersonServiceBean;
?
? public class JDKProxyFactory implements InvocationHandler {
? private Object targetObject;
?
? public Object createProxyIntance(Object targetObject){
? this.targetObject = targetObject;
? return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
? this.targetObject.getClass().getInterfaces(), this);
? }
?
? @Override
? public Object invoke(Object proxy, Method method, Object[] arg2)
? throws Throwable {
? PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
? Object result = null;
? if(personServiceBean.getUser() != null){
? result = method.invoke(targetObject, arg2);
? }
? return null;
? }
? }
AOPTest.java
? package junit.test;
?
? import org.junit.Test;
?
? import cn.pf.aop.JDKProxyFactory;
? import cn.pf.aop.service.PersonService;
? import cn.pf.aop.service.impl.PersonServiceBean;
?
?
? public class AOPTest {
? @Test public void proxyTest(){
? JDKProxyFactory factory = new JDKProxyFactory();
? PersonService personService = (PersonService) factory.createProxyIntance(new PersonServiceBean());
? personService.save("111");
? }
? }
Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);
創(chuàng)建代理對象的時(shí)候施敢,加入了該目標(biāo)對象所有的接口周荐,即對所有的方法進(jìn)行監(jiān)聽,任何一個(gè)方法的調(diào)用都會觸發(fā)代理對象的invoke方法僵娃。this表示觸發(fā)哪個(gè)代理對象的invoke方法概作,這里我們設(shè)置當(dāng)前代理對象。
調(diào)用personService的save方法的時(shí)候默怨,可以理解為讯榕,save方法被監(jiān)聽,進(jìn)入代理對象的invoke方法匙睹,如果user愚屁!=null,則invoke方法中調(diào)用了personService的save方法痕檬,如果user==null霎槐,則什么也不做。
通過反射技術(shù)調(diào)用方法其實(shí)可以簡單的理解為
xxx.invoke(obj,args)返回的結(jié)果是obj.xxx(args)
1.1.16 使用CGLIB實(shí)現(xiàn)AOP功能與AOP概念解釋
前面的proxy技術(shù)必須在類實(shí)現(xiàn)了接口的前提下才可以實(shí)現(xiàn)權(quán)限的控制梦谜,cglb可以在類不實(shí)現(xiàn)接口的情況下完成丘跌。
在spring文件夾下lib/cglib下找到cglib的jar庫文件袭景,加入工程。
CGlibProxyFactory.java
? package cn.pf.aop;
?
? import java.lang.reflect.Method;
?
? import cn.pf.aop.service.impl.PersonServiceBean;
?
? import net.sf.cglib.proxy.Enhancer;
? import net.sf.cglib.proxy.MethodInterceptor;
? import net.sf.cglib.proxy.MethodProxy;
?
? public class CGlibProxyFactory implements MethodInterceptor{
? private Object targetObject;
?
? public Object createProxyIntance(Object targetObject){
? this.targetObject = targetObject;
? Enhancer enhancer = new Enhancer();
? enhancer.setSuperclass(this.targetObject.getClass());
? enhancer.setCallback(this);
? return enhancer.create();
? }
?
? @Override
? public Object intercept(Object proxy, Method method, Object[] arg2,
? MethodProxy arg3) throws Throwable {
? PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
? Object result = null;
? if(personServiceBean.getUser() != null){
? result = method.invoke(targetObject, arg2);
? }
? return result;
? }
? }
AOPTest.java
? @Test public void proxyTest2(){
? CGlibProxyFactory factory = new CGlibProxyFactory();
? PersonServiceBean personServiceBean = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("1"));
? personServiceBean.save("111");
? }
CGlib的enhance繼承了目標(biāo)類所有非final方法闭树,對這些方法進(jìn)行覆蓋耸棒。創(chuàng)建的代理對象是目標(biāo)對象的子類
1.1.17 使用Spring的注解方式實(shí)現(xiàn)AOP入門
首先添加包
``
/spring.jar
/lib/aspectj/aspectjrt.jar
/lib/aspectj/aspectjweaver.jar
/lib/j2ee/common-annotations.jar
/lib/jakarta-commons/common_logging.jar
/lib/cglib/cglib-nodep-2.1-3.jar
``
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"
? xmlns:aop="http://www.springframework.org/schema/aop"
? xsi:schemaLocation="http://www.springframework.org/schema/beans
? http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
? http://www.springframework.org/schema/aop
? http://www.springframework.org/schema/context/spring-aop-2.5.xsd">
?
? <aop:aspectj-autoproxy/>
? </beans>
PersonService.java和PersonServiceBean.java和上篇一樣
MyInterceptor.java
@Ascept聲明了切面,即進(jìn)行攔截的類报辱。
@Pointcut聲明了切入點(diǎn)榆纽,即進(jìn)行攔截的方法。
@Pointcut("execution(* cn.itcast.service...(..))")
- 代表返回值類型
cn.pf.service 需要攔截的包名
.. 代表隊(duì)子包的類進(jìn)行攔截
代表進(jìn)行攔截的類
代表進(jìn)行攔截的方法
(..) 代表方法的參數(shù)隨意
(*代表任意)
下面來測試前置通知捏肢,后置通知,最終通知饥侵,例外通知以及環(huán)繞通知鸵赫。
MyInterceptor.java
? package cn.pf.aop.service;
?
? import org.aspectj.lang.ProceedingJoinPoint;
? import org.aspectj.lang.annotation.After;
? import org.aspectj.lang.annotation.AfterReturning;
? import org.aspectj.lang.annotation.AfterThrowing;
? import org.aspectj.lang.annotation.Around;
? import org.aspectj.lang.annotation.Aspect;
? import org.aspectj.lang.annotation.Before;
? import org.aspectj.lang.annotation.Pointcut;
?
? @Aspect
? public class MyInterceptor {
? @Pointcut("execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))")
? private void anyMethod(){}
?
? @Before("anyMethod()")
? public void doAccessCheck(){
? System.out.println("前置通知");
? }
?
? @AfterReturning("anyMethod()")
? public void doAfterReturning(){
? System.out.println("后置通知");
? }
?
? @After("anyMethod()")
? public void doAfter(){
? System.out.println("最終通知");
? }
?
? @AfterThrowing("anyMethod()")
? public void doAfterThrowing(){
? System.out.println("例外通知");
? }
?
? @Around("anyMethod()")
? public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
? System.out.println("進(jìn)入環(huán)繞方法");
? Object result = pjp.proceed();
? System.out.println("退出環(huán)繞方法");
? return result;
? }
? }
SpringAOPTest.java
? import org.junit.Test;
? import org.springframework.context.ApplicationContext;
? import org.springframework.context.support.ClassPathXmlApplicationContext;
?
? import cn.pf.aop.service.PersonService;
?
?
?
? public class SpringAOPTest {
? @Test public void interceptorTest(){
? ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
? PersonService personService = (PersonService)context.getBean("personService");
? personService.save("1");
? }
? }
控制臺輸出:
前置通知
進(jìn)入環(huán)繞方法
cn.pf.aop.service.impl.PersonServiceBean save方法
后置通知
最終通知
退出環(huán)繞方法
那么如何獲得輸入?yún)?shù),返回值躏升,異常呢辩棒,那么稍作修改
? @Before("anyMethod() && args(name)")
? public void doAccessCheck(String name) {
? System.out.println("前置通知:"+ name);
? }
? @AfterReturning(pointcut="anyMethod()",returning="result")
? public void doAfterReturning(String result) {
? System.out.println("后置通知:"+ result);
? }
? @After("anyMethod()")
? public void doAfter() {
? System.out.println("最終通知");
? }
? @AfterThrowing(pointcut="anyMethod()",throwing="e")
? public void doAfterThrowing(Exception e) {
? System.out.println("例外通知:"+ e);
? }
其實(shí)切面就感覺像servlet里面的過濾器,在方法的前后加上一些關(guān)卡膨疏,進(jìn)行篩選一睁,判定權(quán)限,通過指定好的一些切面后佃却,才可以真正調(diào)用目標(biāo)對象的方法者吁。
1.1.18 基于XML配置方式聲明切面
基于XML配置方式聲明切面
與注釋方法沒什么太大的區(qū)別
? <bean id=”orderservice” class=”cn.service.OrderServiceBean” />
?
? <bean id=”log” class=”cn.service.LogPrint” />
?
? <aop:config>
?
? <aop:aspect id=”myaop” ref=”log”>
?
? <aop:pointcut id=”mycut” expression=”execution(* cn.service..*.*(..))”/>
?
? <aop:before pointcut-ref=”mycut” method=”doAccessCheck” />
?
? <aop:after-returning pointcut-ref=”mycut” method=”doReturnCheck” />
?
? <aop:after-throwing pointcut-ref=”mycut” method=”doExceptionAction” />
?
? <aop:after pointcut-ref=”mycut” method=”doReleaseAction” />
?
? <aop:around pointcut-ref=”mycut” method=”doBasicProfiling” />
?
? </aop:aspect>
?
? </aop:config>
1.1.19 aspectj的切入點(diǎn)語法定義細(xì)節(jié)
execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))
所有非final方法
execution(!void cn.pf.aop.service.impl.PersonServiceBean.*(..))
非void非final方法
execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(..))
非final且返回類型為String的方法
execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(java.lang.String,..))
第一個(gè)參數(shù)為String的非final方法
execution(* cn.pf.aop.service.impl..*.*(..))
對包下所有類進(jìn)行攔截