當前位置:首頁 » 硬碟大全 » spring什麼時候加入二級緩存
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

spring什麼時候加入二級緩存

發布時間: 2022-05-13 04:25:55

❶ spring一級緩存和二級緩存的區別是什麼

一級緩存:
就是Session級別的緩存。一個Session做了一個查詢操作,它會把這個操作的結果放在一級緩存中。
如果短時間內這個session(一定要同一個session)又做了同一個操作,那麼hibernate直接從一級緩存中拿,而不會再去連資料庫,取數據。
它是內置的事務范圍的緩存,不能被卸載。
二級緩存:
就是SessionFactory級別的緩存。顧名思義,就是查詢的時候會把查詢結果緩存到二級緩存中。
如果同一個sessionFactory創建的某個session執行了相同的操作,hibernate就會從二級緩存中拿結果,而不會再去連接資料庫。
這是可選的插件式的緩存,在默認情況下,SessionFactory不會啟用這個插件。
可以在每個類或每個集合的粒度上配置。緩存適配器用於把具體的緩存實現軟體與Hibernate集成。
嚴格意義上說,SessionFactory緩存分為兩類:內置緩存和外置緩存。我們通常意義上說的二級緩存是指外置緩存。
內置緩存與session級別緩存實現方式相似。前者是SessionFactory對象的一些集合屬性包含的數據,後者是指Session的一些集合屬性包含的數據
SessionFactory的內置緩存中存放了映射元數據和預定義sql語句。
映射元數據是映射文件中數據的拷貝;
而預定義SQL語句是在Hibernate初始化階段根據映射元數據推導出來。
SessionFactory的內置緩存是只讀的,應用程序不能修改緩存中的映射元數據和預定義SQL語句,因此SessionFactory不需要進行內置緩存與映射文件的同步。
Hibernate的這兩級緩存都位於持久化層,存放的都是資料庫數據的拷貝。
緩存的兩個特性:
緩存的范圍
緩存的並發訪問策略
1、緩存的范圍
決定了緩存的生命周期以及可以被誰訪問。緩存的范圍分為三類。
事務范圍
進程范圍
集群范圍
註:
對大多數應用來說,應該慎重地考慮是否需要使用集群范圍的緩存,因為訪問的速度不一定會比直接訪問資料庫數據的速度快多少。
事務范圍的緩存是持久化層的第一級緩存,通常它是必需的;進程范圍或集群范圍的緩存是持久化層的第二級緩存,通常是可選的。
2、緩存的並發訪問策略
當多個並發的事務同時訪問持久化層的緩存的相同數據時,會引起並發問題,必須採用必要的事務隔離措施。
在進程范圍或集群范圍的緩存,即第二級緩存,會出現並發問題。
因此可以設定以下四種類型的並發訪問策略,每一種策略對應一種事務隔離級別。
事務型並發訪問策略是事務隔離級別最高,只讀型的隔離級別最低。事務隔離級別越高,並發性能就越低。
A 事務型:僅僅在受管理環境中適用。它提供了Repeatable Read事務隔離級別。
對於經常被讀但很少修改的數據,可以採用這種隔離類型,因為它可以防止臟讀和不可重復讀這類的並發問題。
B 讀寫型:提供了Read Committed事務隔離級別。僅僅在非集群的環境中適用。
對於經常被讀但很少修改的數據,可以採用這種隔離類型,因為它可以防止臟讀這類的並發問題。
C 非嚴格讀寫型:不保證緩存與資料庫中數據的一致性。
如果存在兩個事務同時訪問緩存中相同數據的可能,必須為該數據配置一個很短的數據過期時間,從而盡量避免臟讀。
對於極少被修改,並且允許偶爾臟讀的數據,可以採用這種並發訪問策略。
D 只讀型:對於從來不會修改的數據,如參考數據,可以使用這種並發訪問策略。
什麼樣的數據適合存放到第二級緩存中?
1、很少被修改的數據
2、不是很重要的數據,允許出現偶爾並發的數據
3、不會被並發訪問的數據
4、參考數據
不適合存放到第二級緩存的數據?
1、經常被修改的數據
2、財務數據,絕對不允許出現並發
3、與其他應用共享的數據。
Hibernate的二級緩存策略的一般過程如下:
1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有欄位)這樣的SQL語句查詢資料庫,一次獲得所有的數據對象。
2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麼從二級緩存中查;查不到,再查詢資料庫,把結果按照ID放入到緩存。
4) 刪除、更新、增加數據的時候,同時更新緩存。
註:
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query緩存。
Query緩存策略的過程如下:
1) Hibernate首先根據這些信息組成一個Query Key,Query Key包括條件查詢的請求一般信息:SQL, SQL需要的參數,記錄范圍(起始位置rowStart,最大記錄個數maxRows),等。
2) Hibernate根據這個Query Key到Query緩存中查找對應的結果列表。如果存在,那麼返回這個結果列表;如果不存在,查詢資料庫,獲取結果列表,把整個結果列表根據Query Key放入到Query緩存中。
3) Query Key中的SQL涉及到一些表名,如果這些表的任何數據發生修改、刪除、增加等操作,這些相關的Query Key都要從緩存中清空。

❷ 到底如何在spring中使用redis

1. Redis使用場景

Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。

我們都知道,在日常的應用中,資料庫瓶頸是最容易出現的。數據量太大和頻繁的查詢,由於磁碟IO性能的局限性,導致項目的性能越來越低。

這時候,基於內存的緩存框架,就能解決我們很多問題。例如Memcache,Redis等。將一些頻繁使用的數據放入緩存讀取,大大降低了資料庫的負擔。提升了系統的性能。

其實,對於hibernate的二級緩存,是同樣的道理。利用內存高速的讀寫速度,來解決硬碟的瓶頸。

2. 配置使用redis

首先,我們需要引入基本的jar包。maven中的基本引用如下:

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>

然後,在applicationContext中配置如下:

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
p:pool-config-ref="poolConfig" />
<bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- 開啟事務,可以通過transcational註解控制 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringSerializer" />
<property name="enableTransactionSupport" value="true" />
</bean>

對於hibernate的配置可知,第一個poolconfig是對連接池的配置。包括最大連接數,隊列數,存活時間,最大等待時間等等,還有一些額外的配置,請直接點擊JedisPoolConfig類源碼,進行查看。

這些配置的意思如果不明白的話,一定要去把線程池好好學習下。

第一個配置是連接工廠,顧名思義,最基本的使用一定是對連接的打開和關閉。我們需要為其配置redis伺服器的賬戶密碼,埠號。(這里還可以配置資料庫的index,但是我使用時候一直使用redis的默認資料庫,也就是第0個)

最後一個配置特別重要。這個類似於spring提供的HibernateDaoSupport。

接下來,全部講解都將圍繞這個類展開。

3. RedisTemplate的使用

這個類作為一個模版類,提供了很多快速使用redis的api,而不需要自己來維護連接,事務。

最初的時候,我創建的BaseRedisDao是繼承自這個類的。繼承的好處是我的每個Dao中,都可以自由的控制序列化器,自由的控制自己是否需要事務,這個先不需要了解,跟著我目前的這種配置方法來即可。

template提供了一系列的operation,比如valueOperation,HashOperation,ListOperation,SetOperation等,用來操作不同數據類型的Redis。

並且,RedisTemplate還提供了對應的*OperationsEditor,用來通過RedisTemplate直接注入對應的Operation。我們暫時不講這個。

對於下面的test1方法,我們暫時不用考慮,先了解通過RedisTemplate來使用connection操作Redis。

Test代碼如下:

package cn.test.spjedis;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework..DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.cn.redis2..IncrDao;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestRedis {
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template; // inject the template as ListOperations
//至於這個為什麼可以注入。需要參考AbstractBeanFactory doGetBean
//super.setValue(((RedisOperations) value).opsForValue());就這一行代碼 依靠一個editor
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;

public void testSet(){
template.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte [] key = "tempkey".getBytes();
byte[] value = "tempvalue".getBytes();
connection.set(key, value);
return true;
}
});
}

public void testSet1(){
vOps.set("tempkey", "tempvalue");
}

@Autowired
private IncrDao incr;

@Test
public void addLink() {
System.out.println(incr.incr(13));
System.out.println(incr.get(13));
}

}

這個是對String類型插入的兩個測試。test方法中,使用了模版類提交回調(RedisCallBack)的方法來使用jedis connection操作數據。這一部分,有沒有似曾相識呢?

HibernateTemplate的HibernateCallback,以及Hibernate Session類中的doWork以及doReturningWork方法,都是使用了這樣的機制,方便對於連接或者session的統一管理。

public int excuteHqlUpdate(final String hql,final Object ...params){
return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {
@Override
@SuppressWarnings("unchecked")
public Integer doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
queryObject.setParameter(i, params[i]);
}
}
return queryObject.executeUpdate();
}
});
}

❸ Spring運行原理

1、spring原理

spring的最大作用ioc/di,將類與類的依賴關系寫在配置文件中,

程序在運行時根據配置文件動態載入依賴的類,降低的類與類之間

的藕合度。它的原理是在applicationContext.xml加入bean標記,

在bean標記中通過class屬性說明具體類名、通過property標簽說明

該類的屬性名、通過constructor-args說明構造子的參數。其一切都是

返射,當通過applicationContext.getBean("id名稱")得到一個類實例時,

就是以bean標簽的類名、屬性名、構造子的參數為准,通過反射實例對象,

喚起對象的set方法設置屬性值、通過構造子的newInstance實例化得到對象。

正因為spring一切都是反射,反射比直接調用的處理速度慢,所以這也是spring

的一個問題。

spring第二大作用就是aop,其機理來自於代理模式,代理模式

有三個角色分別是通用介面、代理、真實對象

代理、真實對象實現的是同一介面,將真實對象作為

代理的一個屬性,向客戶端公開的是代理,當客戶端

調用代理的方法時,代理找到真實對象,調用真實對象

方法,在調用之前之後提供相關的服務,如事務、安全、

日誌。其名詞分別是代理、真實對象、裝備、關切點、連接點。

2、動態代理:不用寫代理類,虛擬機根據真實對象實現的介面產生一個類,通過

類實例化一個動態代理,在實例化動態代理時將真實對象

及裝備注入到動態代理中,向客戶端公開的是動態代理,

當客戶端調用動態代理方法時,動態代理根據類的返射得

到真實對象的Method,調用裝備的invoke方法,將動態代理、

Method、方法參數傳與裝備的invoke方法,invoke方法在喚

起method方法前或後做一些處理。

1、產生動態代理的類:

java.lang.refect.Proxy

2、裝備必須實現InvocationHandler介面實現invoke方法

3、反射

什麼是類的返射?

通過類說明可以得到類的父類、實現的介面、內部類、構造函數、方法、屬性

並可以根據構造器實例化一個對象,喚起一個方法,取屬性值,改屬性值。

如何得到一個類說明?

Class cls=類.class;

Class cls=對象.getClass();

Class.forName("類路徑");

如何得到一個方法並喚起它?

Class cls=類.class;

Constructor cons=cls.getConstructor(new Class[]{String.class});

Object obj=cons.newInstance(new Object[]{"aaa"});

Method method=cls.getMethod("方法名",new Class[]{String.class,Integer.class});

method.invoke(obj,new Object[]{"aa",new Integer(1)});

4、spring的三種注入方式是什麼?

setter

interface

constructor

5、spring的核心介面及核類配置文件是什麼?

FactoryBean:工廠bean主要實現ioc/di

ApplicationContext ac=new FileXmlApplicationContext("applicationContext.xml");

Object obj=ac.getBean("id值");

applicationContext.xml

❹ 在使用s2spring hibernate 的時候 我現在spring的配置文件配hibernate的二級緩存咋么配

hibernate的二級緩存是在hibernate.properties里配置的
################################################### Hibernate 緩存屬性 ###################################################

#是否使用二級緩存
# 默認值: true
hibernate.cache.use_second_level_cache = true

#CacheProvider 的實現類
#hibernate.cache.provider_class = org.hibernate.cache.NoCacheProvider
hibernate.cache.provider_class = net.sf.ehcache.hibernate.SingletonEhCacheProvider

#是否以頻繁的讀操作為代價,優化二級緩存來最小化寫操作. 該值對於群集緩存非常有用
# 默認值: false
#hibernate.cache.use_minimal_puts = true

#允許查詢緩存, 個別查詢仍然需要設置為可緩存的
# 默認值: false
hibernate.cache.use_query_cache = true

#QueryCacheFactory的實現類
hibernate.cache.query_cache_factory = org.hibernate.cache.StandardQueryCacheFactory

#二級緩存區域名前綴
#hibernate.cache.region_prefix = lbs

#強制Hibernate以更人性化的格式將數據存入二級緩存
#hibernate.cache.use_structured_entries = true

❺ 怎樣在spring初始化完成後執行一些操作

是applicationContext初始化完成後調用,並非某一個bean的初始化 問題補充:我詳細說下我的需求吧,我在項目中將公用數據放入了hibernate的二級緩存,比如地區等。在spring初始化完成後我想調用下service層的查詢方法,然後將結果緩存

❻ hibernate二級緩存 和 spring整合的緩存(就是用哪個Cacheable註解的)有什麼區別么

二級緩存配置(spring+hibernate)

說明:本人不建議使用查詢緩存,因為查詢緩存要求完全相同的查詢sql語句才會起作用,所說的查詢緩存是針對第二次查詢時 sql語句與第一次sql語句完全相同 那麼就可以從緩存中取數據而不去資料庫中取數據了,在不啟用查詢緩存的情況下 每次的查詢數據也會緩存到二級緩存的 只不過每次查詢都會去查詢資料庫(不包括根據ID查詢),啟用查詢緩存很麻煩 需要每次查詢時 調用Query.setCacheable(true)方法才可以,如:List<OrgiData> orgiDatas = (List<OrgiData>) s.createQuery("from OrgiData").setCacheable(true).list();

因此建議將查詢緩存設置為如下:
hibernate.cache.use_query_cache=false

還有就是最重要的一點:對於經常修改或重要的數據不宜進行緩存,因為多並發時會造成數據不同步的情況。

首先增加ehcache-1.4.1.jar和backport-util-concurrent-3.1.jar或oscache-2.1.jar

一、spring配置

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>com/handpay/core/merchant/bean/MerchGroupBuy.hbm.xml
</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.SQLServerDialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.hbm2ddl.auto=update
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=false
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider </value>
</property>
</bean>

<!---紅色字體是二級緩存相關的設置->

二、hbm.xml文件示例

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.handpay.core.merchant.bean">
<class name="MerchGroupBuy" table="merch_group_buy">
<cache usage="read-write" region="com.handpay.core.merchant.bean.MerchGroupBuy"/>
<id name="id">
<generator class="native" />
</id>
<property name="code" />
<property name="createTime"/>
<property name="minNum"/>
<property name="status">
</property>
<property name="title"/>
<property name="typeCode"/>
<property name="updateTime"/>
</class>
</hibernate-mapping>

三、註解示例

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@Table(name = "alcor_t_countries", catalog = "alcorweb")
public class AlcorTCountries implements java.io.Serializable{。。。。}

四、配置文件參數詳解

ehcache.xml是ehcache的配置文件,並且存放在應用的classpath中。下面是對該XML文件中的一些元素及其屬性的相關說明:

<diskStore>元素:指定一個文件目錄,當EHCache把數據寫到硬碟上時,將把數據寫到這個文件目錄下。 下面的參數這樣解釋:

user.home – 用戶主目錄

user.dir – 用戶當前工作目錄

java.io.tmpdir – 默認臨時文件路徑

<defaultCache>元素:設定緩存的默認數據過期策略。

<cache>元素:設定具體的命名緩存的數據過期策略。

<cache>元素的屬性

name:緩存名稱。通常為緩存對象的類名(非嚴格標准)。

maxElementsInMemory:設置基於內存的緩存可存放對象的最大數目。

maxElementsOnDisk:設置基於硬碟的緩存可存放對象的最大數目。

eternal:如果為true,表示對象永遠不會過期,此時會忽略timeToIdleSeconds和timeToLiveSeconds屬性,默認為false;

timeToIdleSeconds: 設定允許對象處於空閑狀態的最長時間,以秒為單位。當對象自從最近一次被訪問後,如果處於空閑狀態的時間超過了timeToIdleSeconds屬性值,這個對象就會過期。當對象過期,EHCache將把它從緩存中清空。只有當eternal屬性為false,該屬性才有效。如果該屬性值為0,則表示對象可以無限期地處於空閑狀態。

timeToLiveSeconds:設定對象允許存在於緩存中的最長時間,以秒為單位。當對象自從被存放到緩存中後,如果處於緩存中的時間超過了 timeToLiveSeconds屬性值,這個對象就會過期。當對象過期,EHCache將把它從緩存中清除。只有當eternal屬性為false,該屬性才有效。如果該屬性值為0,則表示對象可以無限期地存在於緩存中。timeToLiveSeconds必須大於timeToIdleSeconds屬性,才有意義。

overflowToDisk:如果為true,表示當基於內存的緩存中的對象數目達到了maxElementsInMemory界限後,會把益出的對象寫到基於硬碟的緩存中。注意:如果緩存的對象要寫入到硬碟中的話,則該對象必須實現了Serializable介面才行。

memoryStoreEvictionPolicy:緩存對象清除策略。有三種:

1 FIFO ,first in first out ,這個是大家最熟的,先進先出,不多講了

2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存。

2 LRU ,Least Recently Used ,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。

五 、查看 二級緩存數據

1、使用sessionFactory直接獲取
Map cacheEntries = sessionFactory().getStatistics()
.getSecondLevelCacheStatistics("cacheRegionName")
.getEntries();

其中 cacheRegionName 既是 ehcache.xml配置中的<cache 標簽的name屬性值

2、讓log4j列印緩存信息(生成環境下請注釋掉,以免影響性能)
log4j.logger.org.hibernate.cache=debug

❼ java spring 的jdbcTemplate 如何使用hibernate的二級緩存,只求高手來秒殺

查詢的時候設置
Query q = session.createQuery("from District d where d.city.citycode='" + cityId + "'");
q.setCacheable(true);
更新,刪除,保存都設置為false

❽ struts/spring/hibernate集成使用二級緩存的問題

你的speak.xml文件裡面看看是不是少hibernate映射文件少寫了什麼 或者就是你的某一個hbm.xml文件裡面的某個欄位寫錯或者主外鍵關系配錯了導致項目啟動不了

❾ springmvc3中使用hibernate的二級緩存失效

首先要說的是,spring中,需要手動配置緩存信息。不知道你配置了沒有
例如如下配置:
hibernate.cache.use_query_cache=true
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.EhCacheRegionFactory
最好貼出你的緩存配置信息才能判斷。

❿ spring的原理

Spring的哲學是在不影響Java對象的設計的情況下將Java對象加入到框架中。 我們下面來看看Spring的工作原理,看看Spring是如何做到不影響Java對象的。

EJB的框架採用了一種侵略性(Invasive)的方法來設計對象,它要求你在設計中加入符合EJB規范的代碼。一些輕量級的COP框架,例如Avalon,也要求對象設計時必須符合某種規范,例如Serviceable介面,這種做法是典型的Type 1做法。

這種設計思路要求Spring採用一種動態的、靈活的方式來設計框架。在Spring的工作原理中大量採用了反射。首先Spring要解決的一個問題就是如何管理bean。因為IOC的思想要求bean之間不能夠直接調用,而應該採用一種被動的方式進行協作。所以bean的管理是Spring工作原理中的核心部分。

反射和內省在代碼的層次上思考問題,有時候能夠帶來出人意料的靈活性。但它的使用有時候也是一個哲學問題,不論是在ORM設計還是在AOP設計上都出現了類似的問題-究竟是使用反射,還是使用代碼生成。

在Spring中,處理這個問題的核心是在org.springframework.beans包中。而其中最為核心的部分,則是BeanWrapper。BeanWrapper,顧名思義,就是bean的包裝器。所以,它的主要工作,就是對任何一個bean,進行屬性(包括內嵌屬性)的設置和方法的調用。在

BeanWrapper的默認實現類BeanWrapperImpl中,雖然代碼較長,但完成的工作卻是非常的集中的。

BeanWrapper的深入研究

我們看看這個BeanWrapper是如何發揮運作的,假設我們有兩個bean:

public class Company { private String name; private Employee managingDirector; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Employee getManagingDirector() { return this.managingDirector; } public void setManagingDirector(Employee managingDirector) { this.managingDirector = managingDirector; } } public class Employee { private float salary; public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } }

然後我們使用BeanWrapper來調用這兩個bean:

Company c = new Company(); BeanWrapper bwComp = BeanWrapperImpl(c); // setting the company name... bwComp.setPropertyValue("name", "Some Company Inc."); // ... can also be done like this: PropertyValue v = new PropertyValue("name", "Some Company Inc."); bwComp.setPropertyValue(v); // ok, lets create the director and tie it to the company: Employee jim = new Employee(); BeanWrapper bwJim = BeanWrapperImpl(jim); bwJim.setPropertyValue("name", "Jim Stravinsky"); bwComp.setPropertyValue("managingDirector", jim); // retrieving the salary of the managingDirector through the company Float salary = (Float)bwComp.getPropertyValue("managingDirector.salary");

看起來麻煩了許多,但是這樣Spring就可以使用統一的方式來管理bean的屬性了。

Bean的製造工廠

有了對單個Bean的包裝,還需要對多個的bean進行管理。在spring中,把bean納入到一個核心庫中進行管理。bean的生產有兩種方法:一種是一個bean產生多個實例,一種是一個bean只產生一個實例。如果對設計模式熟悉的話,我們就會想到,前者可以採用Prototype,後者可以採用Singleton。

注意到,反射技術的使用使得我們不再像原始的工廠方法模式那樣創建對象。反射可以非常靈活的根據類的名稱創建一個對象。所以spring只使用了Prototype和Singleton這兩個基本的模式。

Spring正是這樣處理的,但是我們希望用戶能夠維護統一的介面,而不需要關心當前的bean到底是Prototype產生的獨立的bean,還是Singleton產生的共享的bean。所以,在org.springframework.beans.factory包中的BeanFactory定義了統一的getBean方法。

JDBC再封裝JDBC優雅的封裝了底層的資料庫,但是JDBC仍然存在諸多的不變。你需要編寫大量的代碼來完成CRUD操作,而且,JDBC無論是遇到什麼樣的問題,都拋出一個SQLException,這種做法在異常使用上被稱為不完備的信息。因為問題可能是很復雜的,也許是資料庫連接的問題,也許是並發控制的問題,也許只是SQL語句出錯。沒有理由用一個簡單的SQLException就搞定全部的問題了,這種做法有些不負責任。針對這兩個問題,Spring Framework提出了兩種解決方法:首先,提供一個框架,把JDBC應用中的獲取連接、異常處理、釋放等比較通用的操作全部都集中起來,用戶只需要提供特定的實現就OK了。實現的具體細節採用的是模板方法。舉個例子,在org.springframework.jdbc.object包中,MappingSqlQuery類實現了將SQL查詢映射為具體的業務對象。JavaDoc中這樣寫到:Reusable query in which concrete subclasses must implement the abstract mapRow(ResultSet, int) method to convert each row of the JDBC ResultSet into an object. 用戶必須實現mapRow方法,這是典型模板方法的應用。我們拿一個具體的例子來看看:

class UserQuery extends MappingSqlQuery { public UserQuery(DataSource datasource) { super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?"); declareParameter(new SqlParameter(Types.NUMERIC)); compile(); } // Map a result set row to a Java object protected Object mapRow(ResultSet rs, int rownum) throws SQLException { User user = new User(); user.setId(rs.getLong("USER_ID")); user.setForename(rs.getString("FORENAME")); return user; } public User findUser(long id) { // Use superclass convenience method to provide strong typing return (User) findObject(id); } }

其次是第二個問題,最麻煩的地方應該說是需要截住JDBC的異常,然後判斷異常的類型,並重新拋出異常。錯誤的問題可以通過連接來獲取,所以麻煩的是如何截獲異常。Spring 框架採用的方法是回調,處理回調的類在Spring Framework中被稱為template 。

JdbcTemplate template = new JdbcTemplate(dataSource); final List names = new LinkedList(); template.query("SELECT USER.NAME FROM USER", new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { names.add(rs.getString(1)); } });

回調函數是一個匿名類,其中也使用了模板方法,異常的處理都在父類中完成了。

層間松耦合

在開放源碼界已經出現了大量的基於MVC的Web容器,但是這些容器都僅限於Web的范圍 ,不涉及Web層次後端的連接,Spring作為一個整體性的框架,定義了一種Web層和後端業務層的連接方式, 這個思路仍然疏運圖MVC的范疇,但耦合更鬆散,不依賴於具體的集成層次。

public class GoogleSearchController implements Controller { private IGoogleSearchPort google; private String googleKey; public void setGoogle(IGoogleSearchPort google) { this.google = google; } public void setGoogleKey(String googleKey) { this.googleKey = googleKey; } public ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String query = request.getParameter("query"); GoogleSearchResult result = // Google property definitions omitted... // Use google business object google.doGoogleSearch(this.googleKey, query,start, maxResults, filter, r estrict, safeSearch, lr, ie, oe); return new ModelAndView("googleResults", "result", result); } }

回調函數是一個匿名類,其中也使用了模板方法,異常的處理都在父類中完成了。
2.Spring是一個開源框架,它由Rod Johnson創建。它是為了解決企業應用開發的復雜性而創建的。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限於伺服器端的開發。從簡單性、可測試性和松耦合的角度而言,任何Java應用都可以從Spring中受益。 • 目的:解決企業應用開發的復雜性 • 功能:使用基本的JavaBean代替EJB,並提供了更多的企業應用功能 • 范圍:任何Java應用 簡單來說,Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器框架。 輕量——從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小隻有1MB多的JAR文件里發布。並且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。 控制反轉——Spring通過一種稱作控制反轉(IoC)的技術促進了松耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創建或者查找依賴對象。你可以認為IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。 面向切面——Spring提供了面向切面編程的豐富支持,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務()管理)進行內聚性的開發。應用對象只實現它們應該做的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。 容器——Spring包含並管理應用對象的配置和生命周期,在這個意義上它是一種容器,你可以配置你的每個bean如何被創建——基於一個可配置原型(prototype),你的bean可以創建一個單獨的實例或者每次需要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不應該被混同於傳統的重量級的EJB容器,它們經常是龐大與笨重的,難以使用。 框架——Spring可以將簡單的組件配置、組合成為復雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件里。Spring也提供了很多基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。 所有Spring的這些特徵使你能夠編寫更干凈、更可管理、並且更易於測試的代碼。它們也為Spring中的各種模塊提供了基礎支持。