Spring基礎:屬於一個應用型框架,輕量級框架,可以說是一個大雜會,是一個大型的工廠。
Spring核心類庫:
1、spring依賴庫
* SPRING_HOME/dist/spring.jar
* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
2、拷貝spring配置文件到src下
3、拷貝log4j配置文件到src下
4、在UserManagerImpl中提供構造函數或setter方法,spring將實例化好的UserDao實現注入給我們
5、讓spring管理我們的對象創建和依賴,必須在spring配置中進行定義
6、編寫客戶端
IOC容器:控制反轉,控制反轉給我們的spring容器去管理,以前是我們來控制的,是我們自己來new對象等。
DI:注入技術,依賴的注入(還有一種實現控制反轉的技術實現叫依賴查找)
普通類型的注入配置:
<bean id="testBean" class="com.spring.dao.testBean">
<property name="strValue" value="string"></property>
<property name="intValue" value="1"></property>
<property name="setValue">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="listValue">
<list>
<value>list1</value>
<value>list2></value>
</list>
</property>
<property name="arrayValue">
<list>
<value>arry1</value>
<value>arry2</value>
</list>
</property>
<property name="mapValue">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
</map>
</property>
</bean>
什麼是屬性編輯器,作用?
* 自定義屬性編輯器,spring配置文件中的字符串轉換成相應的對象進行注入
spring已經有內置的屬性編輯器,我們可以根據需求自己定義屬性編輯器
* 如何定義屬性編輯器?
* 繼承PropertyEditorSupport類,覆寫setAsText()方法,參見:UtilDatePropertyEditor.java
* 將屬性編輯器註冊到spring中,參見:applicationContext-editor.xml
依賴對象的注入方式,可以採用:
* ref屬性
* <ref>標籤
* 內部<bean>來定義
日期類型的注入:spring不識別日期類型,需要自己系一個屬性編輯器來轉換,繼承類PropertyEditorSupport
例如:
public class UtilDatePropertyEditor extends PropertyEditorSupport {
private String format;
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date date = sdf.parse(text);
this.setValue(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
<!--內部<bean>來定義-->
<bean id="CustomEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="com.spring.util.UtilDatePropertyEditor">
<property name="format" value="yyyy-MM-dd"/>
</bean>
</entry>
</map>
</property>
</bean>
<!-- 也可以如下的寫法
<bean id="CustomEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value-ref="UtilDatePropertyEditor"/>
</map>
</property>
</bean>
<bean id="UtilDatePropertyEditor" class="com.spring.util.UtilDatePropertyEditor">
<property name="format" value="yyyy-MM-dd"/>
</bean>
-->
如何將公共的注入定義描述出來?
* 通過<bean>標籤定義公共的屬性,指定abstract=true
* 具有相同屬性的類在<bean>標籤中指定其parent屬性
參見:applicationContext-other.xml
<!-- 公共屬性的配置 -->
<bean id="BeanAbstruct" abstract="true">
<property name="id" value="100"/>
<property name="name" value="zhangshan"/>
</bean>
<!-- 使用parent屬性來制定公共屬性 -->
<bean id="bean3" parent="BeanAbstruct" class="com.spring.dao.Bean3">
<!-- 也可以覆蓋繼承來的公共屬性 -->
<property name="name" value="lisi"/>
<property name="pwd" value="123"></property>
</bean>
<bean id="bean4" parent="BeanAbstruct" class="com.spring.dao.Bean4"></bean>
Bean的作用域
當多次使用BeanFactory。getBean()的時候,當get同一個bean的時候,他們的應用時相等的(默認是相等的),但是可以改變:
<!-- bean id="testBean" scope="prototype" class="com.spring.dao.testBean" -->
<bean id="testBean" scope="singleton" class="com.spring.dao.testBean">
scope可以取值:
* singleton:每次調用getBean的時候返回相同的實例
* prototype:每次調用getBean的時候返回不同的實例
Spring的裝配方式
*根據名稱自動裝配 default-autowire="byName"
*根據類型自動裝配 default-autowire="byType"
AOP:提供了聲明式服務。提供代理服務。
靜態代理:
比如對UserManager的安全性檢查(UserManager的每個方法都要先進行安全檢查),
代理類必須要與原類實現同樣的接口,即有共同的接口,它的缺點是,代理類的每個方法都必須加一個安全性檢查,安全性檢查散步在代理類裏了。
IOC管理對象,但是AOP不管對象,只管橫切性的東西拿出來,在運行時加進去。
基於jdk的動態代理:
基於靜態代理的缺點,如我除了安全性檢查,還想加一個日子呢,這樣導致代理類的每個方法都要去更改了。
public class SecurityHandler implements InvocationHandler{
//目標對象
private Object targetObject;
//創建代理類
public Object newProxy(Object obj) {
this.targetObject = obj;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
checkSecurity();
Object ret = null;
try {
ret = method.invoke(this.targetObject, args);
} catch(Exception e) {
e.printStackTrace();
}
return ret;
}
public void checkSecurity() {
System.out.println("checkSecurity()");
}
}
test:
public class Client {
public static void main(String[]args) {
SecurityHandler handler = new SecurityHandler();
UserManager usermanager =(UserManager) handler.newProxy(new UserManagerImpl());
usermanager.addUser("zhu", "123");
}
}
注意:也可以配置,當方法(Method)是delete的時候,才做安全性檢查。method.getName()可以獲取當前調用的方法名稱。
橫切性的問題,它會散步在代碼的各個角落,如安全性檢查,我們叫橫切性關注點(Cross cutting concern),advice(橫切點的實現)()Before Advice 、after advice,橫切類(如剛剛的SecurityHandler類),JoinPoint(連接點,spring只支持方法的連接點),Weave(植入,把切面應用到目標對象上的這樣的一個過程叫植入)
Spring對AOP的支持方式:
(採用Annotation的方式)
1、spring依賴庫
* SPRING_HOME/dist/spring.jar
* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
* SPRING_HOME/lib/aspectj/*.jar
2、採用Aspect定義切面
3、在Aspect定義Pointcut和Advice
4、啓用AspectJ對Annotation的支持並且將Aspect類和目標對象配置到Ioc容器中
<aop:aspectj-autoproxy/>
注意:在這種方法定義中,切入點的方法是不被執行的,它存在的目的僅僅是爲了重用切入點
即Advice中通過方法名引用這個切人點
AOP:
* Cross cutting concern
* Aspect
* Advice
* Pointcut
* Joinpoint
* Weave
* Target Object
* Proxy
* Introduction
/**
* 定義Aspect
* @author Administrator
*
*/
@Aspect
public class SecurityHandler {
/**
* 定義Pointcut,Pointcut的名稱就是allAddMethod,此方法不能有返回值和參數,該方法只是一個
* 標識
*
* Pointcut的內容是一個表達式,描述那些對象的那些方法(訂閱Joinpoint)
*/
@Pointcut("execution(* add*(..)) || execution(* del*(..))")
private void allAddMethod(){};
/**
* 定義Advice,標識在那個切入點何處織入此方法
*/
@Before("allAddMethod()")
private void checkSecurity() {
System.out.println("----------checkSecurity()---------------");
}
}
spring對AOP的只是(採用配置文件的方式)
1、spring依賴庫
* SPRING_HOME/dist/spring.jar
* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
* SPRING_HOME/lib/aspectj/*.jar
2、配置如下
<aop:config>
<aop:aspect id="security" ref="securityHandler">
<aop:pointcut id="allAddMethod" expression="execution(* com.bjsxt.spring.UserManagerImpl.add*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="allAddMethod"/>
</aop:aspect>
</aop:config>
spring對AOP的支持
Aspect默認情況下不用實現接口,但對於目標對象(UserManagerImpl.java),在默認情況下必須實現接口
如果沒有實現接口必須引入CGLIB庫
我們可以通過Advice中添加一個JoinPoint參數,這個值會由spring自動傳入,從JoinPoint中可以取得
參數值、方法名等等
1、如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
如何強制使用CGLIB實現AOP?
* 添加CGLIB庫,SPRING_HOME/cglib/*.jar
* 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動態代理和CGLIB字節碼生成的區別?
* JDK動態代理只能對實現了接口的類生成代理,而不能針對類
* CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
因爲是繼承,所以該類或方法最好不要聲明成final
採用編程式事務
1、getCurrentSession()與openSession()的區別?
* 採用getCurrentSession()創建的session會綁定到當前線程中,而採用openSession()
創建的session則不會
* 採用getCurrentSession()創建的session在commit或rollback時會自動關閉,而採用openSession()
創建的session必須手動關閉
2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
* 如果使用的是本地事務(jdbc事務)
<property name="hibernate.current_session_context_class">thread</property>
* 如果使用的是全局事務(jta事務)
<property name="hibernate.current_session_context_class">jta</property>
採用聲明式事務
1、聲明式事務配置
* 配置SessionFactory
* 配置事務管理器
* 事務的傳播特性
* 那些類那些方法使用事務
2、編寫業務邏輯方法
* 繼承HibernateDaoSupport類,使用HibernateTemplate來持久化,HibernateTemplate是
Hibernate Session的輕量級封裝
* 默認情況下運行期異常纔會回滾(包括繼承了RuntimeException子類),普通異常是不會滾的
* 編寫業務邏輯方法時,最好將異常一直向上拋出,在表示層(struts)處理
* 關於事務邊界的設置,通常設置到業務層,不要添加到Dao上
3、瞭解事務的幾種傳播特性
1. PROPAGATION_REQUIRED: 如果存在一個事務,則支持當前事務。如果沒有事務則開啓
2. PROPAGATION_SUPPORTS: 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行
3. PROPAGATION_MANDATORY: 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。
4. PROPAGATION_REQUIRES_NEW: 總是開啓一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
5. PROPAGATION_NOT_SUPPORTED: 總是非事務地執行,並掛起任何存在的事務。
6. PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則拋出異常
7. PROPAGATION_NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務,
則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行
4、Spring事務的隔離級別
1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
另外四個與JDBC的隔離級別相對應
2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
這種隔離級別會產生髒讀,不可重複讀和幻像讀。
3. ISOLATION_READ_COMMITTED: 保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據
4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。
它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重複讀)。
5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理爲順序執行。
除了防止髒讀,不可重複讀外,還避免了幻像讀。
spring+struts的集成(第一種集成方案):缺點,在action中要取得beanfactory,必然依賴了beanfactory。
原理:在Action中取得BeanFactory對象,然後通過BeanFactory獲取業務邏輯對象
1、spring和struts依賴庫配置
* 配置struts
--拷貝struts類庫和jstl類庫
--修改web.xml文件來配置ActionServlet
--提供struts-config.xml文件
--提供國際化資源文件
* 配置spring
--拷貝spring類庫
--提供spring配置文件
2、在struts的Action中調用如下代碼取得BeanFactory
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession().getServletContext());
需要配置listener及參數:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3、通過BeanFactory取得業務對象,調用業務邏輯方法
spring+struts的集成(第二種集成方案)
原理:將業務邏輯對象通過spring注入到Action中,從而避免了在Action類中的直接代碼查詢
1、spring和struts依賴庫配置
* 配置struts
--拷貝struts類庫和jstl類庫
--修改web.xml文件來配置ActionServlet
--提供struts-config.xml文件
--提供國際化資源文件
* 配置spring
--拷貝spring類庫
--提供spring配置文件
2、因爲Action需要調用業務邏輯方法,所以需要在Action中提供setter方法,讓spring將業務邏輯對象注入過來
3、在struts-config.xml文件中配置Action
* <action>標籤中的type屬性需要修改爲org.springframework.web.struts.DelegatingActionProxy
DelegatingActionProxy是一個Action,主要作用是取得BeanFactory,然後根據<action>中的path屬性值
到IoC容器中取得本次請求對應的Action
4、在spring配置文件中需要定義struts的Action,如:
<bean name="/login" class="com.bjsxt.usermgr.actions.LoginAction" scope="prototype">
<property name="userManager" ref="userManager"/>
</bean>
* 必須使用name屬性,name屬性值必須和struts-config.xml文件中<action>標籤的path屬性值一致
* 必須注入業務邏輯對象
* 建議將scope設置爲prototype,這樣就避免了struts Action的線程安全問題
Struts + Spring + Hibernate集成
基本配置:ApplicationContext-common.xml:
<!--配置Hibernate的SessionFactory-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">classpath:hibernate.hcf.xml</property>
</bean>
<!--配置事物管理器-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTranscationManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<!-- 配置事務的傳播特性 -->
<tx:advice id="txAdvice" transcation-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="modify*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--哪些類的那些方法參與事物-->
<aop:config>
<aop:pointcut id="allManagerMethod" expression="execution(* xxx.xxx.*.*(..))"/>
<aop:advisor pointcut-ref="allManagerMethod" advice-ref="txAdvice"/>
</aop:config>
在web.xml中加入相應的配置:
配置spring的bean配置文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext-*.xml</param-value>
</context-param>
配置LoaderListern:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
配置spring提供的字符轉換filter:
<filter>
<filter-name>springCharacterEncoding</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>