Spring知識點筆記
1.1.1 Spring簡介
Spring是一個開源的控制反轉(IoC)和麵向且切面(AOP)的容器框架。
IOC/">IOC控制反轉:應用本身不負責以來物件的建立和維護,以來物件的建立及維護由外部容器負責。這樣控制權就轉移到了外部容器。
Dependency Injection依賴注入:在執行期,由外部容器動態地將以來物件注入到元件中。
事務控制全部交給spring處理,不用手工編寫事務的建立和提交
1.1.2 Spring配置和搭建
Eclipse下的配置
下載了spring-framework-2.5.6包,把dist資料夾下的spring.jar新增到工程,還要新增一個jar是common-logging的,在hibernate學習的時候下載過了,也新增進去。
在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"});//可以通過陣列傳遞多個配置檔案。
下面使用JUnit4進行測試,new->JUnit Test

image.png
(不要勾選setUpBeforeClass)
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"); } }
執行以後測試成功
下面寫一個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
在沒聯網的情況下需要新增提示檔案才會出現關鍵字提示。
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標籤中間新增
<bean id="personService" class="service.PersonServiceBean"></bean>
使用bean標籤的時候,id和name的區別: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(); } }
執行成功
1.1.3 Spring管理bean的原理
簡單的一套管理bean的程式碼,就是一個模擬的ApplicationContext類。
主要方法有兩個
this.readXML(filename);//讀取XML配置檔案 this.instanceBeans();//例項化bean
主要成員變數
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();//儲存XML中的bean資訊,包括id和class,組成BeanDefinition類 private Map<String, Object> sigletons = new HashMap<String, Object>();//儲存例項化以後的bean
具體的程式碼實現不細化研究了,大概流程:
首先使用dom4j讀取XML中的bean標籤,把id和class儲存在beanDifines陣列中,然後在instanceBeans()方法中遍歷beanDifines陣列,取出類名,利用發射技術進行例項化,如Class.forName(beanDefinition.getClassName()).newInstance());並把例項化物件儲存在sigletons的雜湊表中。完成例項化
接下來就是簡單的get方法來取出對應的bean供其他類呼叫。
可以發現bean的例項化就是在建立ApplicationContext物件的時候,通過構造方法進行例項化的。
程式碼:
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的例項化 */ 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("//ns:beans/ns:bean");//建立beans/bean查詢路徑 xsub.setNamespaceURIs(nsMap);//設定名稱空間 List<Element> beans = xsub.selectNodes(document);//獲取文件下所有bean節點 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例項 * @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 三種例項化bean的方式
1.使用類構造器例項化
<bean id="personService" class="service.impl.PersonServiceBean"></bean>
2.使用靜態工廠方法例項化
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.使用例項工廠方法例項化
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.}
三種方式測試成功
絕大部分都使用第一種例項化方法
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);
輸出結果為true,說明getBean方法獲取的是單例項
那麼就來看bean的作用域
bean標籤的scope屬性來設定
singleton
預設情況下的單例模式,每次呼叫getBean方法獲取的都是同一個bean物件
預設情況喜愛會在容器啟動時初始化bean,但我們可以指定Bean節點的lazy-init屬性來延遲初始化bean,這時候,只有第一次獲取bean才會初始化bean。
<bean id="xxx" class="xxx" lazy_init="true"/>
如果對所有bean都應用延遲初始化
<beans default-lazy-init="true"...>
prototype
每次從容器獲取bean都是一個新的物件
request
session
global session
1.1.6 spring管理bean的生命週期
從前面可以看到,
singleton模式下bean的例項化的時機是在ApplicationContext例項化的時候進行例項化,然而設定lazy-init=true的情況下在getBean方法呼叫的時候進行例項化。
prototype模式下bean的例項化時機是在getBean方法呼叫的時候進行例項化。
如果我們需要在bean初始化的時候,開啟資料庫資源等連線,那麼指定一個初始化方法,這樣在bean例項化的時候就會自動初始化。同樣的指定一個銷燬方法。
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();
測試結果輸出
inti初始化方法destroy銷燬方法
lazy-init應該是一個優化spring很好的東西,就像hibernate裡的懶載入,有些bean可能一直沒有用到過,根本沒必要初始化,但是視訊裡說盡量使用lazy-init=false,為了發現所有bean可能出現的錯誤,難道測試的時候要這麼做?另外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(); · · } · · }
執行結果:
dao.impl.PersonDaoBean add方法
還有一種內部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標籤內部新增bean標籤,在bean標籤裡寫出類名即可。效果一樣。
實際上spring的操作流程是吧bean中的property也儲存在一個數組中,初始化的時候遍歷陣列,找到需要注入的bean,利用反射技術呼叫set方法,引數為配置檔案中的ref應用的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>第一個</value> · <value>第二個</value> · <value>第三個</value> · </set> · </property>
list:
private List<String> list;
· <property name="list"> · <list> · <value>第一個</value> · <value>第二個</value> · <value>第三個</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 使用構造器裝配屬性
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指明瞭引數的位置,加了index就不用加type來說明了。
1.1.11 用@Resource註解完成屬性裝配
java程式碼注入配置,需要spring解壓資料夾下lib/j2ee/common-annotation.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: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>
由於現在家裡上spring的網站總是上不去,如果要出現標籤提示,那麼和前面一樣在本地新增spring-context-2.5.xsd
現在配置工作完成了,現在開始用java程式碼來完成注入
@Autowired方式:預設按型別裝配,預設情況下它要求以來物件必須存在,如果允許null值,可以設定它required屬性為false。如果我們想使用按名稱裝配,可以結合@Qualifier註解一起使用
@Autowired @Qualifier("personDaoBean") private PersonDao personDao;
@Resource方式:預設按名稱裝配,名稱可以通過@Resource的name屬性指定,如果沒有指定的name屬性,當註解標註在欄位上,即預設取欄位的名稱作為bean名稱尋找以來物件,當註解標註在屬性的setter方法上,即迷人取屬性名作為bean名稱尋找以來物件。
@Resource(name="personDaoBean") private PersonDao personDao;
推薦使用@Resource方式,因為@Resource是j2ee裡的一個註解,而@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; · } · }
執行發現注入成功,string型別就不需要用註解注入了,直接賦值就可以了。
另外吧@Resource放在setter方法上也是可以的,效果一樣。
1.1.12 編碼剖析@Resource註解的實現原理
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(); · } · /** · *通過註解實現注入依賴物件 · */ · 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的例項化 · */ · 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("//ns:beans/ns:bean");//建立beans/bean查詢路徑 · xsub.setNamespaceURIs(nsMap);//設定名稱空間 · List<Element> beans = xsub.selectNodes(document);//獲取文件下所有bean節點 · 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);//設定名稱空間 · 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例項 · * @param beanName · * @return · */ · public Object getBean(String beanName){ · return this.sigletons.get(beanName); · } · }
實際上也就是通過了反射技術來構造物件並且賦值,只是用到了註解的方法,並且利用@interface來構造自定義的註解型別。
1.1.13 @Autowire註解與自動裝配
使用了@Autowired的註解方式,這種預設按型別查詢符合的bean注入
@Autowired **private** PersonDao personDao;
使用@Qualifier註明bean名稱注入
@Autowired @Qualifier("personDao") **private** PersonDao personDao;
還可以新增required屬性,在沒找到bean的情況下,如果required為false,則注入null,required為true,則報錯。
@Autowired(required=true) @Qualifier("personDao") private PersonDao personDao;
自動裝配:
通過bean標籤的autowire屬性來配置,有5種值
no 不使用自動裝配,必須通過ref元素指定依賴,預設設定。
byName 根據屬性名自動裝配。此選項將檢查容器並根據名字查詢與
屬性完全一致的bean,並將其與屬性自動裝配。
byType 如果容器中存在一個與指定屬性型別相同的bean,那麼將與
該屬性自動裝配;如果存在多個該型別bean,那麼丟擲異
常,並指出不能使用byType方式進行自動裝配;如果沒有找
到相匹配的bean,則什麼事都不發生,也可以通過設定
dependency-check="objects"讓Spring丟擲異常。
constructor 與byType方式類似,不同之處在於它應用於構造器引數。如
果容器中沒有找到與構造器引數型別一致的bean,那麼丟擲
異常。
autodetect 通過bean類的自省機制(introspection)來決定是使用
constructor還是byType方式進行自動裝配。如果發現預設的
構造器,那麼將使用byType方式。
1.1.14 讓Spring自動掃描和管理Bean
讓Spring自動掃描和管理Bean
<context:component-scan base-package="cn.test"></context:component-scan>
其中base-package為需要掃描的包(含子包)
@Service用於標註業務層元件,@Controller用於標註控制層元件(如struts中的action),@Repository用於標註資料訪問元件,即DAO元件,而@Component泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行標註。
bean的預設名稱是類名,然後把第一個字母改為小寫。可以通過@Service("xxx")修改bean名稱。
這種bean預設是單例的,如果想改變,可以使用@Service(“aaaaa”) @Scope(“prototype”)來改變。
還可以通過@PostConstruct @PreDestroy設定初始化和銷燬的函式
· @PostConstruct · public void init(){ · System.out.println("初始化"); · } · · · · @PreDestroy · public void destory(){ · System.out.println("開閉資源"); · }
1.1.15 使用JDK中的Proxy技術實現AOP功能
通過代理物件來呼叫物件的方法,從而做出許可權控制。
目標物件必須實現接口才能使用proxy技術建立代理物件。
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);
建立代理物件的時候,加入了該目標物件所有的介面,即對所有的方法進行監聽,任何一個方法的呼叫都會觸發代理物件的invoke方法。this表示觸發哪個代理物件的invoke方法,這裡我們設定當前代理物件。
呼叫personService的save方法的時候,可以理解為,save方法被監聽,進入代理物件的invoke方法,如果user!=null,則invoke方法中呼叫了personService的save方法,如果user==null,則什麼也不做。
通過反射技術呼叫方法其實可以簡單的理解為
xxx.invoke(obj,args)返回的結果是obj.xxx(args)
1.1.16 使用CGLIB實現AOP功能與AOP概念解釋
前面的proxy技術必須在類實現了介面的前提下才可以實現許可權的控制,cglb可以在類不實現介面的情況下完成。
在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繼承了目標類所有非final方法,對這些方法進行覆蓋。建立的代理物件是目標物件的子類
1.1.17 使用Spring的註解方式實現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聲明瞭切面,即進行攔截的類。
@Pointcut聲明瞭切入點,即進行攔截的方法。
@Pointcut("execution(* cn.itcast.service.. . (..))")
- 代表返回值型別
cn.pf.service 需要攔截的包名
.. 代表隊子包的類進行攔截
-
代表進行攔截的類
-
代表進行攔截的方法
(..) 代表方法的引數隨意
(*代表任意)
下面來測試前置通知,後置通知,最終通知,例外通知以及環繞通知。
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("進入環繞方法"); · Object result = pjp.proceed(); · System.out.println("退出環繞方法"); · 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"); · } · }
控制檯輸出:
前置通知
進入環繞方法
cn.pf.aop.service.impl.PersonServiceBean save方法
後置通知
最終通知
退出環繞方法
那麼如何獲得輸入引數,返回值,異常呢,那麼稍作修改
· @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); · }
其實切面就感覺像servlet裡面的過濾器,在方法的前後加上一些關卡,進行篩選,判定許可權,通過指定好的一些切面後,才可以真正呼叫目標物件的方法。
1.1.18 基於XML配置方式宣告切面
基於XML配置方式宣告切面
與註釋方法沒什麼太大的區別
· <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的切入點語法定義細節
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,..))第一個引數為String的非final方法
execution(* cn.pf.aop.service.impl.. . (..))對包下所有類進行攔截

image.png