tag:blogger.com,1999:blog-59465307047421309702024-03-06T16:20:07.273+08:00Jax 的工作紀錄除了在整理學習上的經驗,同時也能幫助其他需要的人Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.comBlogger43125tag:blogger.com,1999:blog-5946530704742130970.post-557572642807075762015-04-27T17:49:00.000+08:002015-04-27T17:49:13.294+08:00[轉載] Spring 使用 Cache、整合 Ehcache轉載自:<a href="http://haohaoxuexi.iteye.com/blog/2123030" target="_blank">Spring使用Cache、整合Ehcache</a><br />
<br />
<h2>Spring 使用 Cache</h2><br />
從 3.1 開始,Spring 引入了對 Cache 的支持。其使用方法和原理都類似於 Spring 對事務管理的支持。Spring Cache 是作用在方法上的,其核心思想是這樣的:當我們在調用一個緩存方法時會把該方法參數和返回結果作為一個鍵值對存放在緩存中,等到下次利用同樣的參數來調用該方法時將不再執行該方法,而是直接從緩存中獲取結果進行返回。所以在使用 Spring Cache 的時候我們要保證我們緩存的方法對於相同的方法參數要有相同的返回結果。<br />
<br />
使用 Spring Cache 需要我們做兩方面的事:<br />
<ul><li>聲明某些方法使用緩存</li>
<li>配置 Spring 對 Cache 的支持</li>
</ul><br />
和 Spring 對事務管理的支持一樣,Spring 對 Cache 的支持也有基於注解和基於 XML 配置兩種方式。下面我們先來看看基於注解的方式。<br />
<br />
<br />
<h2>1 基於注解的支持</h2><br />
Spring 為我們提供了幾個注解來支持 Spring Cache。其核心主要是 <code>@Cacheable</code> 和 <code>@CacheEvict</code>。使用 <code>@Cacheable</code> 標記的方法在執行後 Spring Cache 將緩存其返回結果,而使用 <code>@CacheEvict</code> 標記的方法會在方法執行前或者執行後移除 Spring Cache 中的某些元素。下面我們將來詳細介紹一下 Spring 基於注解對 Cache 的支持所提供的幾個注解。<br />
<br />
<br />
<h2>1.1 @Cacheable</h2><br />
<code>@Cacheable</code> 可以標記在一個方法上,也可以標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類所有的方法都是支持緩存的。對於一個支持緩存的方法,Spring 會在其被調用後將其返回值緩存起來,以保證下次利用同樣的參數來執行該方法時可以直接從緩存中獲取結果,而不需要再次執行該方法。Spring 在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果,至於鍵的話,Spring 又支持兩種策略,默認策略和自定義策略,這個稍後會進行說明。<strong style="color:red">需要注意的是當一個支持緩存的方法在對像內部被調用時是不會觸發緩存功能的。</strong><code>@Cacheable</code> 可以指定三個屬性,value、key 和 condition。<br />
<br />
<br />
<h2>1.1.1 value 屬性指定 Cache 名稱</h2><br />
value 屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪個 Cache 上的,對應 Cache 的名稱。其可以是一個 Cache 也可以是多個 Cache,當需要指定多個 Cache 時其是一個陣列。<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable("cache1") // Cache 是發生在 cache1 上的
public User find(Integer id) {
return null;
}
@Cacheable({"cache1", "cache2"}) // Cache 是發生在 cache1 和 cache2 上的
public User find(Integer id) {
return null;
}
</pre><br />
<br />
<h2>1.1.2 使用 key 屬性自定義 key</h2><br />
key 屬性是用來指定 Spring 緩存方法的返回結果時對應的 key 的。該屬性支持 SpringEL 表達式。當我們沒有指定該屬性時,Spring 將使用默認策略生成 key。我們這裡先來看看自定義策略,至於默認策略會在後文單獨介紹。<br />
<br />
自定義策略是指我們可以通過 Spring 的 EL 表達式來指定我們的 key。這裡的 EL 表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用 <code>"#參數名"</code> 或者 <code>"#p參數index"</code>。下面是幾個使用參數作為 key 的示例。<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
</pre><br />
除了上述使用方法參數作為 key 之外,Spring 還為我們提供了一個 root 對像可以用來生成 key。通過該 root 對像我們可以獲取到以下信息。<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1"><tr class="header"><th>屬性名稱</th><th>描述</th><th>示例</th></tr>
<tr><td>methodName</td><td>當前方法名</td><td>#root.methodName</td></tr>
<tr><td>method</td><td>當前方法</td><td>#root.method.name</td></tr>
<tr><td>target</td><td>當前被調用的對像</td><td>#root.target</td></tr>
<tr><td>targetClass</td><td>當前被調用的對像的 class</td><td>#root.targetClass</td></tr>
<tr><td>args</td><td>當前方法參數組成的陣列</td><td>#root.args[0]</td></tr>
<tr><td>caches</td><td>當前被調用的方法使用的 Cache</td><td>#root.caches[0].name</td></tr>
</table><br />
當我們要使用 root 對像的屬性作為 key 時我們也可以將 <code>"#root"</code> 省略,因為 Spring 默認使用的就是 root 對像的屬性。如:<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
</pre><br />
<br />
<h2>1.1.3 condition 屬性指定發生的條件</h2><br />
有的時候我們可能並不希望緩存一個方法所有的返回結果。通過 condition 屬性可以實現這一功能。condition 屬性默認為空,表示將緩存所有的調用情形。其值是通過 SpringEL 表達式來指定的,當為 <code>true</code> 時表示進行緩存處理;當為 <code>false</code> 時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次。如下示例表示只有當 user 的 id 為偶數時才會進行緩存。<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
</pre><br />
<br />
<h2>1.2 @CachePut</h2><br />
在支持 Spring Cache 的環境下,對於使用 <code>@Cacheable</code> 標注的方法,Spring 在每次執行前都會檢查 Cache 中是否存在相同 key 的緩存元素,如果存在就不再執行該方法,而是直接從緩存中獲取結果進行返回,否則才會執行並將返回結果存入指定的緩存中。<code>@CachePut</code> 也可以聲明一個方法支持緩存功能。與 <code>@Cacheable</code> 不同的是使用 <code>@CachePut</code> 標注的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。<br />
<br />
<code>@CachePut</code> 也可以標注在類上和方法上。使用 <code>@CachePut</code> 時我們可以指定的屬性跟 <code>@Cacheable</code> 是一樣的。<br />
<pre class="java:nogutter:nocontrols" name="code">@CachePut("users") // 每次都會執行方法,並將結果存入指定的緩存中
public User find(Integer id) {
return null;
}
</pre><br />
<br />
<h2>1.3 @CacheEvict</h2><br />
<code>@CacheEvict</code> 是用來標注在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發緩存的清除操作。<code>@CacheEvict</code> 可以指定的屬性有 value、key、condition、allEntries 和 beforeInvocation。其中 value、key 和 condition 的語義與 <code>@Cacheable</code> 對應的屬性類似。即 value 表示清除操作是發生在哪些 Cache 上的(對應 Cache 的名稱);key 表示需要清除的是哪個 key,如未指定則會使用默認策略生成的 key;condition 表示清除操作發生的條件。下面我們來介紹一下新出現的兩個屬性 allEntries和 beforeInvocation。<br />
<br />
<br />
<h2>1.3.1 allEntries 屬性</h2><br />
allEntries 是 boolean 類型,表示是否需要清除緩存中的所有元素。默認為 false,表示不需要。當指定了 allEntries 為 true 時,Spring Cache 將忽略指定的 key。有的時候我們需要 Cache 一下清除所有的元素,這比一個一個清除元素更有效率。<br />
<pre class="java:nogutter:nocontrols" name="code">@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
</pre><br />
<br />
<h2>1.3.2 beforeInvocation 屬性</h2><br />
清除操作默認是在對應方法成功執行之後觸發的,即方法如果因為拋出異常而未能成功返回時也不會觸發清除操作。使用 beforeInvocation 可以改變觸發清除操作的時間,當我們指定該屬性值為 true 時,Spring 會在調用該方法之前清除緩存中的指定元素。<br />
<pre class="java:nogutter:nocontrols" name="code">@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
</pre><br />
其實除了使用 <code>@CacheEvict</code> 清除緩存元素外,當我們使用 Ehcache 作為實現時,我們也可以配置 Ehcache 自身的驅除策略,其是通過 Ehcache 的配置文件來指定的。由於 Ehcache 不是本文描述的重點,這裡就不多贅述了,想了解更多關於 Ehcache 的信息,請查看我關於 <a href="http://haohaoxuexi.iteye.com/category/319453" target="_blank">Ehcache 的專欄</a>。<br />
<br />
<br />
<h2>1.4 @Caching</h2><br />
<code>@Caching</code> 注解可以讓我們在一個方法或者類上同時指定多個 Spring Cache 相關的注解。其擁有三個屬性:cacheable、put 和 evict,分別用於指定 <code>@Cacheable</code>、<code>@CachePut</code> 和 <code>@CacheEvict</code>。<br />
<pre class="java:nogutter:nocontrols" name="code">@Caching(
cacheable = @Cacheable("users"),
evict = {
@CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true)
}
)
public User find(Integer id) {
return null;
}
</pre><br />
<br />
<h2>1.5 使用自定義注解</h2><br />
Spring 允許我們在配置可緩存的方法時使用自定義的注解,前提是自定義的注解上必須使用對應的注解進行標注。如我們有如下這麼一個使用 <code>@Cacheable</code> 進行標注的自定義注解。<br />
<pre class="java:nogutter:nocontrols" name="code">@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}
</pre><br />
那麼在我們需要緩存的方法上使用 <code>@MyCacheable</code> 進行標注也可以達到同樣的效果。<br />
<pre class="java:nogutter:nocontrols" name="code">@MyCacheable
public User findById(Integer id) {
System.out.println("find user by id: " + id);
User user = new User();
user.setId(id);
user.setName("Name" + id);
return user;
}
</pre><br />
<br />
<br />
<h2>2 配置 Spring 對 Cache 的支持</h2><br />
<h2>2.1 聲明對Cache的支持</h2><br />
<h2>2.1.1 基於注解</h2><br />
配置 Spring 對基於注解的 Cache 的支持,首先我們需要在 Spring 的配置文件中引入 cache 命名空間,其次通過 <code><cache:annotation-driven/></code> 就可以啟用 Spring 對基於注解的 Cache 的支持。<br />
<pre class="xml:nogutter:nocontrols" name="code"><?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:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd"
>
<cache:annotation-driven/>
</beans>
</pre><br />
<code><cache:annotation-driven/></code> 有一個 cache-manager 屬性用來指定當前所使用的 CacheManager 對應的 bean 的名稱,默認是 cacheManager,所以當我們的 CacheManager 的 id 為 cacheManager 時我們可以不指定該參數,否則就需要我們指定了。<br />
<br />
<code><cache:annotation-driven/></code> 還可以指定一個 mode 屬性,可選值有 proxy 和 aspectj。默認是使用 proxy。<strong style="color:red">當 mode 為 proxy 時,只有緩存方法在外部被調用的時候 Spring Cache 才會發生作用</strong>,這也就意味著如果一個緩存方法在其聲明對像內部被調用時 Spring Cache 是不會發生作用的。而 mode 為 aspectj 時就不會有這種問題。另外<strong style="color:red">使用 proxy 時,只有 public 方法上的 <code>@Cacheable</code> 等標注才會起作用</strong>,如果需要非 public 方法上的方法也可以使用 Spring Cache 時把 mode 設置為 aspectj。<br />
<br />
此外,<code><cache:annotation-driven/></code> 還可以指定一個 proxy-target-class 屬性,表示是否要代理 class,默認為 false。我們前面提到的 <code>@Cacheable</code>、<code>@cacheEvict</code> 等也可以標注在接口上,這對於基於接口的代理來說是沒有什麼問題的,但是需要注意的是當我們設置 proxy-target-class 為 true 或者 mode 為 aspectj 時,是直接基於 class 進行操作的,定義在接口上的 <code>@Cacheable</code> 等 Cache 注解不會被識別到,那對應的 Spring Cache 也不會起作用了。<br />
<br />
需要注意的是 <code><cache:annotation-driven/></code> 只會去尋找定義在同一個 ApplicationContext 下的 <code>@Cacheable</code> 等緩存注解。<br />
<br />
<br />
<h2>2.1.2 基於 XML 配置</h2><br />
除了使用注解來聲明對 Cache 的支持外,Spring 還支持使用 XML 來聲明對 Cache 的支持。這主要是通過類似於 aop:advice 的 cache:advice 來進行的。在 cache 命名空間下定義了一個 cache:advice 元素用來定義一個對於 Cache 的 advice。其需要指定一個 cache-manager 屬性,默認為 cacheManager。 cache:advice 下面可以指定多個 cache:caching 元素,其有點類似於使用注解時的 <code>@Caching</code> 注解。cache:caching 元素下又可以指定 cache:cacheable、cache:cache-put 和 cache:cache-evict 元素,它們類似於使用注解時的 <code>@Cacheable</code>、<code>@CachePut</code> 和 <code>@CacheEvict</code>。下面來看一個示例:<br />
<pre class="xml:nogutter:nocontrols" name="code"><cache:advice id="cacheAdvice" cache-manager="cacheManager">
<cache:caching cache="users">
<cache:cacheable method="findById" key="#p0"/>
<cache:cacheable method="find" key="#user.id"/>
<cache:cache-evict method="deleteAll" all-entries="true"/>
</cache:caching>
</cache:advice>
</pre><br />
上面配置定義了一個名為 cacheAdvice 的 cache:advice,其中指定了將緩存 findById 方法和 find 方法到名為 users 的緩存中。這裡的方法還可以使用通配符 <code>"*"</code>,比如 <code>"find*"</code> 表示任何以 <code>"find"</code> 開始的方法。<br />
<br />
有了cache:advice 之後,我們還需要引入 aop 命名空間,然後通過 aop:config 指定定義好的 cacheAdvice 要應用在哪些 pointcut 上。如:<br />
<br />
<pre class="xml:nogutter:nocontrols" name="code"><aop:config proxy-target-class="false">
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.xxx.UserService.*(..))"/>
</aop:config>
</pre><br />
上面的配置表示在調用 com.xxx.UserService 中任意公共方法時將使用 cacheAdvice 對應的 cache:advice 來進行 Spring Cache 處理。更多關於 Spring AOP 的內容不在本文討論範疇內。<br />
<br />
<br />
<h2>2.2 配置 CacheManager</h2><br />
CacheManager 是 Spring 定義的一個用來管理 Cache 的接口。Spring 自身已經為我們提供了兩種 CacheManager 的實現,一種是基於 Java API 的 ConcurrentMap,另一種是基於第三方 Cache 實現—— Ehcache,如果我們需要使用其它類型的緩存時,我們可以自己來實現 Spring 的 CacheManager 接口或 AbstractCacheManager 抽像類。下面分別來看看 Spring 已經為我們實現好了的兩種 CacheManager 的配置示例。<br />
<br />
<br />
<h2>2.2.1 基於 ConcurrentMap 的配置</h2><br />
<pre class="xml:nogutter:nocontrols" name="code"><bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="xxx"/>
</set>
</property>
</bean>
</pre><br />
上面的配置使用的是一個 SimpleCacheManager,其中包含一個名為 <code>"xxx"</code> 的 ConcurrentMapCache。<br />
<br />
<br />
<h2>2.2.2 基於 Ehcache 的配置</h2><br />
<pre class="xml:nogutter:nocontrols" name="code"><!-- Ehcache 實現 -->
<bean id="ehCacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/ehcache.xml"/>
</bean>
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehCacheManagerFactory"/>
</bean>
</pre><br />
上面的配置使用了一個 Spring 提供的 EhCacheCacheManager 來生成一個 Spring 的 CacheManager,其接收一個 Ehcache 的 CacheManager,因為真正用來存入緩存數據的還是 Ehcache。Ehcache 的 CacheManager 是通過 Spring 提供的 EhCacheManagerFactoryBean 來生成的,其可以通過指定 ehcache 的配置文件位置來生成一個 Ehcache 的 CacheManager。若未指定則將按照 Ehcache 的默認規則取 classpath 根路徑下的 ehcache.xml 文件,若該文件也不存在,則獲取 Ehcache 對應 jar 包中的 ehcache-failsafe.xml 文件作為配置文件。更多關於 Ehcache 的內容這裡就不多說了,它不屬於本文討論的內容,欲了解更多關於 Ehcache 的內容可以參考我之前發布的 Ehcache 系列文章,也可以參考官方文檔等。<br />
<br />
<br />
<br />
<h2>3 鍵的生成策略</h2><br />
鍵的生成策略有兩種,一種是默認策略,一種是自定義策略。<br />
<br />
<br />
<h2>3.1 默認策略</h2><br />
默認的 key 生成策略是通過 KeyGenerator 生成的,其默認策略如下:<br />
<ul><li>如果方法沒有參數,則使用 0 作為 key</li>
<li>如果只有一個參數的話則使用該參數作為 key</li>
<li>如果參數多餘一個的話則使用所有參數的 hashCode 作為 key</li>
</ul><br />
如果我們需要指定自己的默認策略的話,那麼我們可以實現自己的 KeyGenerator,然後指定我們的 Spring Cache 使用的 KeyGenerator 為我們自己定義的 KeyGenerator。<br />
<br />
使用基於注解的配置時是通過 cache:annotation-driven 指定的.<br />
<pre class="xml:nogutter:nocontrols" name="code"><cache:annotation-driven key-generator="userKeyGenerator"/>
<bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>
</pre><br />
而使用基於 XML 配置時是通過 cache:advice 來指定的。<br />
<pre class="xml:nogutter:nocontrols" name="code"><cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="userKeyGenerator">
</cache:advice>
</pre><br />
需要注意的是此時我們所有的 Cache 使用的 Key 的默認生成策略都是同一個 KeyGenerator。<br />
<br />
<br />
<h2>3.1 自定義策略</h2><br />
自定義策略是指我們可以通過 Spring 的 EL 表達式來指定我們的 key。這裡的 EL 表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用 <code>"#參數名"</code> 或者 <code>"#p參數index"</code>。下面是幾個使用參數作為 key 的示例。<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
</pre><br />
除了上述使用方法參數作為 key 之外,Spring 還為我們提供了一個 root 對像可以用來生成 key。通過該 root 對像我們可以獲取到以下信息。<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1"><tr class="header"><th>屬性名稱</th><th>描述</th><th>示例</th></tr>
<tr><td>methodName</td><td>當前方法名</td><td>#root.methodName</td></tr>
<tr><td>method</td><td>當前方法</td><td>#root.method.name</td></tr>
<tr><td>target</td><td>當前被調用的對像</td><td>#root.target</td></tr>
<tr><td>targetClass</td><td>當前被調用的對像的 class</td><td>#root.targetClass</td></tr>
<tr><td>args</td><td>當前方法參數組成的陣列</td><td>#root.args[0]</td></tr>
<tr><td>caches</td><td>當前被調用的方法使用的 Cache</td><td>#root.caches[0].name</td></tr>
</table><br />
當我們要使用 root 對像的屬性作為 key 時我們也可以將 <code>"#root"</code> 省略,因為 Spring 默認使用的就是 root 對像的屬性。如:<br />
<pre class="java:nogutter:nocontrols" name="code">@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
</pre><br />
<br />
<br />
<h2>4 Spring 單獨使用 Ehcache</h2><br />
前面介紹的內容是 Spring 內置的對 Cache 的支持,其實我們也可以通過 Spring 自己單獨的使用 Ehcache 的 CacheManager 或 Ehcache 對像。通過在 Application Context 中配置 EhCacheManagerFactoryBean 和 EhCacheFactoryBean,我們就可以把對應的 EhCache 的 CacheManager 和 Ehcache 對像注入到其它的 Spring bean 對像中進行使用。<br />
<br />
<br />
<h2>4.1 EhCacheManagerFactoryBean</h2><br />
EhCacheManagerFactoryBean 是 Spring 內置的一個可以產生 Ehcache 的 CacheManager 對像的 FactoryBean。其可以通過屬性 configLocation 指定用於創建 CacheManager 的 Ehcache 配置文件的路徑,通常是 ehcache.xml文件的路徑。如果沒有指定 configLocation,則將使用默認位置的配置文件創建 CacheManager,這是屬於 Ehcache 自身的邏輯,即如果在 classpath 根路徑下存在 ehcache.xml 文件,則直接使用該文件作為 Ehcache 的配置文件,否則將使用 ehcache-xxx.jar 中的 ehcache-failsafe.xml 文件作為配置文件來創建 Ehcache 的 CacheManager。此外,如果不希望創建的 CacheManager 使用默認的名稱(在 ehcache.xml 文件中定義的,或者是由 CacheManager 內部定義的),則可以通過 cacheManagerName 屬性進行指定。下面是一個配置 EhCacheManagerFactoryBean 的示例。<br />
<pre class="xml:nogutter:nocontrols" name="code"><!-- 定義 CacheManager -->
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- 指定配置文件的位置 -->
<property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>
<!-- 指定新建的 CacheManager 的名稱 -->
<property name="cacheManagerName" value="cacheManagerName"/>
</bean>
</pre><br />
<br />
<h2>4.2 EhCacheFactoryBean</h2><br />
EhCacheFactoryBean 是用來產生 Ehcache 的 Ehcache 對像的 FactoryBean。定義 EhcacheFactoryBean 時有兩個很重要的屬性我們可以來指定。一個是 cacheManager 屬性,其可以指定將用來獲取或創建 Ehcache 的 CacheManager 對像,若未指定則將通過 CacheManager.create() 獲取或創建默認的 CacheManager。另一個重要屬性是 cacheName,其表示當前 EhCacheFactoryBean 對應的是 CacheManager 中的哪一個 Ehcache 對像,若未指定默認使用 beanName 作為 cacheName。若 CacheManager 中不存在對應 cacheName 的 Ehcache 對像,則將使用 CacheManager 創建一個名為 cacheName 的 Cache 對像。此外我們還可以通過 EhCacheFactoryBean 的 timeToIdle、timeToLive 等屬性指定要創建的 Cache 的對應屬性,注意這些屬性只對 CacheManager 中不存在對應 Cache 時新建的 Cache 才起作用,對已經存在的 Cache 將不起作用,更多屬性設置請參考 Spring 的 API 文檔。此外還有幾個屬性是對不管是已經存在還是新創建的 Cache 都起作用的屬性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking 和 cacheEventListeners,其中前四個默認都是 false,最後一個表示為當前 Cache 指定 CacheEventListener。下面是一個定義 EhCacheFactoryBean 的示例。<br />
<pre class="xml:nogutter:nocontrols" name="code"><!-- 定義 CacheManager -->
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- 指定配置文件的位置 -->
<property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>
<!-- 指定新建的 CacheManager 的名稱 -->
<property name="cacheManagerName" value="cacheManagerName"/>
</bean>
<!-- 定義一個 Ehcache -->
<bean id="userCache"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheName" value="user"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
</pre><br />
<br />
(注:本文是基於 Spring 3.1.0 所寫)<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-43909466944523068672015-03-13T13:12:00.002+08:002015-03-13T14:40:27.893+08:00[Java] Ant zip 解壓縮筆記<pre class="xml:nogutter:nocontrols" name="code"><unzip dest="./target_dir">
<!-- 來源的壓縮檔 -->
<fileset dir="lib">
<include name="tiles-jsp-*.jar"/>
</fileset>
<!-- 要解出的檔案 -->
<patternset>
<include name="**/*.tld"/>
</patternset>
<!-- 解出路徑的轉換 -->
<mapper type="flatten"/>
</unzip>
</pre><br />
參考文件:<br />
<a href="https://ant.apache.org/manual/Tasks/unzip.html" target="_blank">Apache Ant™ User Manual : Unzip Task</a><br />
<a href="https://ant.apache.org/manual/Types/mapper.html" target="_blank">Apache Ant™ User Manual : Mapper</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-48086193485153050832015-03-13T11:59:00.000+08:002015-03-13T14:40:27.899+08:00[Java] Reflection 筆記<pre class="java:nogutter:nocontrols" name="code">@SuppressWarnings("unused")
Object obj = new Object() {
String id = "123";
public String name = "Jax";
};
Class<?> cl = obj.getClass();
for (Field field : cl.getFields()) {
System.out.printf("%s = %s {%s}\n",
field.getName(), field.get(obj), field.getType());
}
System.out.println("=======================");
for (Field field : cl.getDeclaredFields()) {
System.out.printf("%s = %s {%s}\n",
field.getName(), field.get(obj), field.getType());
}
</pre><br />
Output:<br />
<pre class="none:nogutter:nocontrols" name="code">name = Jax {class java.lang.String}
=======================
id = 123 {class java.lang.String}
name = Jax {class java.lang.String}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-39181220147476341712015-03-10T15:18:00.000+08:002015-03-13T09:04:50.695+08:00[Java] 產生驗證碼圖片<pre class="java" name="code">import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Random;
import javax.imageio.ImageIO;
public class TestCaptchaImage {
public static void main(String[] args) throws Exception {
String captcha = "09875";
int width = 55;
int height = 20;
Color fontColor = new Color(36, 85, 92); /*文字顏色*/
Color lineColor = new Color(65, 161, 175); /*線條顏色*/
Color bgColor = new Color(208, 226, 229); /*底色*/
Random random = new Random();
random.setSeed(System.currentTimeMillis());
BufferedImage image = new BufferedImage(
width, height, BufferedImage.TYPE_INT_RGB
);
Graphics g = image.getGraphics();
/* 底色 */
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
/* 畫線 */
g.setColor(lineColor);
for (int i=0; i < 20; i++) {
g.drawLine(
random.nextInt(width), 0,
random.nextInt(width), height
);
}
/* 畫出文字 */
g.setFont(new Font("Atlantic Inline", Font.BOLD, 20));
g.setColor(fontColor);
g.drawString(captcha, 0, 18);
/* 畫線 */
g.setColor(lineColor);
for (int i=0; i < 4; i++) {
g.drawLine(
0, random.nextInt(height),
width, random.nextInt(height)
);
}
g.dispose();
ImageIO.write(image, "png", new File("captcha_image.png"));
}
}
</pre><br />
參考文件:<br />
<a href="http://nothing.tw/JDK_API_1_6/java/awt/Graphics.html" target="_blank">Graphics (Java 2 Platform SE 6)</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-18662793205287362292015-03-06T17:12:00.000+08:002015-03-13T14:40:27.874+08:00[Java] 製作縮圖筆記<pre class="java:nogutter:nocontrols" name="code">package test_image;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class TestImageResize {
private static int targetWidth = 120;
private static int targetHeight = 80;
private static double targetRate = (double) targetWidth / targetHeight;
public static void main(String[] args) throws Exception {
System.out.printf("Target w:%s, h:%s, r:%s\n",
targetWidth, targetHeight, targetRate);
BufferedImage image = ImageIO.read(new File("input.jpg"));
int type = image.getType();
if(type == 0) { type = BufferedImage.TYPE_INT_ARGB; }
int width = image.getWidth();
int height = image.getHeight();
double rate = (double) width / height;
System.out.printf("Source w:%s, h:%s, r:%s\n", width, height, rate);
/* 等比例縮小至指定大小內 */
int rWidth = targetWidth;
int rHeight = targetHeight;
if(width < targetWidth && height < targetHeight) {
rWidth = width;
rHeight = height;
} else if(rate > targetRate) {
rHeight = (int) (targetWidth / rate);
} else {
rWidth = (int) (targetHeight * rate);
}
System.out.printf("Resize w:%s, h:%s\n", rWidth, rHeight);
BufferedImage resize1 = new BufferedImage(rWidth, rHeight, type);
Graphics g1 = resize1.getGraphics();
g1.drawImage(image, 0, 0, rWidth, rHeight, null);
g1.dispose();
ImageIO.write(resize1, "jpg", new File("output_1.jpg"));
/* 等比例縮小填滿指定大小 */
BufferedImage resize2 = new BufferedImage(targetWidth,targetHeight,type);
Graphics g2 = resize2.getGraphics();
int startX = 0;
int startY = 0;
int size = 0;
if(rate > targetRate) {
startX = (int) (width - height * targetRate) / 2;
size = height;
} else {
startY = (int) (height - width / targetRate) / 2;
size = width;
}
System.out.printf("x:%s, y:%s, size:%s\n", startX, startY, size);
g2.drawImage(
image,
0, 0, targetWidth, targetHeight,
startX, startY, (size + startX), (size + startY),
null
);
g2.dispose();
ImageIO.write(resize2, "jpg", new File("output_2.jpg"));
}
}
</pre><br />
參考文件:<br />
<a href="http://nothing.tw/JDK_API_1_6/java/awt/Graphics.html" target="_blank">Graphics (Java 2 Platform SE 6)</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-16494669802506525802015-03-06T15:33:00.001+08:002015-03-13T14:40:27.869+08:00[Java] Jsoup 筆記<pre class="java" name="code">import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class TestJsoup {
public static void main(String[] args) throws Exception {
String url = "http://epg.dishstar.net/calendar.php?s=DISC&d=1";
// Document doc = Jsoup.parse(new URL(url), 5000);
Document doc = Jsoup.connect(url)
.userAgent("Mozilla/5.0")
.timeout(5000).get();
Elements trs = doc.select("table tr");
for (Element tr : trs) {
String time = tr.select("td:eq(0)").text();
String title = tr.select("td:eq(1)").text();
System.out.println(time + " <=> " + title);
}
}
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-59790078868821735492015-03-06T11:57:00.001+08:002015-03-13T14:40:27.907+08:00Spring JavaMail 筆記<strong>Gmail via SSL</strong><br />
<pre class="xml" name="code"><bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="465" />
<property name="username" value="smtp-user" />
<property name="password" value="smtp-passwd" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
<prop key="mail.smtp.auth">true</prop>
</props>
</property>
</bean>
</pre><br />
<br />
<strong>Gmail via TLS</strong><br />
<pre class="xml" name="code"><bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="smtp-user" />
<property name="password" value="smtp-passwd" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.auth">true</prop>
</props>
</property>
</bean>
</pre><br />
<br />
<strong>Sample Code</strong><br />
<pre class="java:nogutter:nocontrols" name="code">package test_mail;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
public class TestSpringMail {
public static void main( String[] args ) throws Exception {
AbstractApplicationContext context
= new ClassPathXmlApplicationContext("test_mail/spring-mail.xml");
JavaMailSender mailSender
= (JavaMailSender) context.getBean("mailSender");
sample1(mailSender);
sample2(mailSender);
sample3(mailSender);
context.close();
}
public static void sample1(JavaMailSender mailSender) throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
mimeMessage.setFrom("from@no-spam.com");
mimeMessage.setRecipients(
Message.RecipientType.TO, "to@no-spam.com"
);
mimeMessage.setSubject("Testing Subject");
mimeMessage.setContent(
"<b>Testing Content.</b>",
"text/html; charset=utf-8"
);
mailSender.send(mimeMessage);
}
public static void sample2(JavaMailSender mailSender) throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "utf-8");
message.setFrom("from@no-spam.com");
message.setTo("to@no-spam.com");
message.setSubject("Testing Subject");
message.setText("<b>Testing Content.</b>", true);
mailSender.send(mimeMessage);
}
public static void sample3(JavaMailSender mailSender) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("from@no-spam.com");
message.setTo("to@no-spam.com");
message.setSubject("Testing Subject");
message.setText("Testing Content.");
mailSender.send(message);
}
}
</pre><br />
<br />
參考自:<br />
<a href="http://www.mkyong.com/java/javamail-api-sending-email-via-gmail-smtp-example/" target="_blank">JavaMail API – Sending email via Gmail SMTP example : Mkyong</a><br />
<a href="http://www.mkyong.com/spring/spring-sending-e-mail-with-attachment/" target="_blank">Spring – Sending e-mail with attachment : Mkyong</a><br />
<a href="http://www.mkyong.com/spring/spring-define-an-e-mail-template-in-bean-configuration-file/" target="_blank">Spring – Define an E-mail template in bean configuration file : Mkyong</a><br />
<a href="http://www.mkyong.com/spring/spring-sending-e-mail-via-gmail-smtp-server-with-mailsender/" target="_blank">Spring – Sending E-mail via Gmail SMTP server with MailSender : Mkyong</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-68762282214393586532015-03-05T10:24:00.000+08:002015-03-05T10:24:19.486+08:00用 Spring 取得 JSP 執行後的字串內容為了取得執行後的字串內容,我們需要建立 <code>Response</code> 並且替換 <code>PrintWriter</code>,這樣才有辦法取得執行後的內容。<br />
<br />
接著可以從 Spring 的 <code>ViewResolver</code> 去取得 <code>View</code>,再透過 <code>View</code> 去處理 render 前的包裝,最後才由 dispatcher 真正去處理 render 的動作。 <br />
<br />
想到要建立 Request 跟 Response 就感覺讓人頭痛,還好 Spring 有提供 Mock 的類別可以簡單地去建立 Request 跟 Response。<br />
<br />
<br />
<h2>JspRenderer</h2><pre class="java" name="code">package com.orion.webmvc.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
public class JspRenderer {
@Autowired
private ServletContext servletContext;
private ViewResolver viewResolver;
public void setViewResolver(ViewResolver viewResolver) {
this.viewResolver = viewResolver;
}
public String render(String viewName, Model model)
throws IOException
{
return render(viewName, model.asMap());
}
public String render(String viewName, Map<String,Object> modelMap)
throws IOException
{
RendererRequest request = new RendererRequest(servletContext);
RendererResponse response = new RendererResponse();
try {
/* 透過 ViewResolver 取得 View 進行 render 的動作 */
View view = viewResolver.resolveViewName(
viewName, Locale.getDefault()
);
view.render(modelMap, request, response);
return response.getContentAsString();
}
catch(Exception e) {
throw new IOException(e);
}
}
}
class RendererRequest extends MockHttpServletRequest {
private ServletContext servletContext;
public RendererRequest(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
/* 需要透過真實的 RequestDispatcher 進行 Render */
return servletContext.getRequestDispatcher(path);
}
}
class RendererResponse extends MockHttpServletResponse {
private StringWriter writer = new StringWriter();
@Override
public PrintWriter getWriter() throws UnsupportedEncodingException {
/* 用 StringWriter 作為輸出的容器 */
return new PrintWriter(writer);
}
@Override
public boolean isCommitted() {
/* true 是為了讓 View 可以採用 include 方式 Render 到 Response */
return true;
}
@Override
public String getContentAsString() throws UnsupportedEncodingException {
/* 取得 Render 後的內容 */
return writer.getBuffer().toString();
}
}
</pre><br />
<br />
<h2>配置 spring.xml</h2><pre class="xml" name="code"><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="jspRenderer" class="com.orion.webmvc.util.JspRenderer">
<property name="viewResolver" ref="viewResolver"/>
</bean>
</pre><br />
<br />
<h2>使用範例</h2><pre class="java" name="code">//@Autowired
//private JspRenderer jspRenderer;
Map<String,Object> jspMap = new HashMap<String,Object>();
jspMap.put("jspMessage", "中文訊息測試");
jspMap.put("costMessage", 4567.89);
String jspOutput = jspRenderer.render("/mailer/test", jspMap);
System.out.println(jspOutput);
</pre><br />
<br />
參考自:<a href="http://technologicaloddity.com/2011/10/04/render-and-capture-the-output-of-a-jsp-as-a-string/3/" target="_blank">Render and capture the output of a JSP as a String | Technological Oddity</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-34341042884506955832015-03-04T11:33:00.002+08:002015-03-13T14:40:27.887+08:00[Java] FileFilter 筆記<pre class="java:nogutter:nocontrols" name="code">File dir = new File("D:/log");
File[] list = dir.listFiles(new FileFilter(){
public boolean accept(File file) {
return file.getName().endsWith(".txt");
}
});
for(File file : list){
System.out.println(file.getAbsoluteFile());
}
// 004.txt
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-44160072254927015452015-03-04T11:27:00.003+08:002015-03-04T11:27:43.734+08:00[Java] 取得本地端 IP 與 MAC<pre class="java:nogutter:nocontrols" name="code">import java.net.InetAddress;
import java.net.NetworkInterface;
public class TestInetAddress {
public static void main(String[] args) throws Exception {
InetAddress ip = InetAddress.getLocalHost();
System.out.println("Current IP address : " + ip.getHostAddress());
// Current IP address : 192.168.0.109
System.out.println(ip.getCanonicalHostName());
// 192.168.0.109
System.out.println(ip.getHostName());
// jaxhu-PC
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%s%02X", (i > 0 ? "-" : ""), mac[i]));
}
System.out.println("Current MAC address : " + sb.toString());
// Current MAC address : 38-2C-4A-B4-C3-24
System.out.println(network.getDisplayName());
// Realtek PCIe GBE Family Controller
System.out.println(network.getName());
// eth3
}
}
</pre><br />
參考自:<a href="http://www.mkyong.com/java/how-to-get-mac-address-in-java/" target="_blank">How to get MAC address in Java : Mkyong</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-91231883166810473952015-03-04T11:23:00.000+08:002015-03-13T14:40:27.925+08:00[Java] Object Serializable 筆記<pre class="java:nogutter:nocontrols" name="code">import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Address implements Serializable {
private static final long serialVersionUID = 1L;
String street;
String country;
public Address() {}
public Address(String s, String c) {
street = s; country = c;
}
public void setStreet(String street){ this.street = street; }
public String getStreet(){ return this.street; }
public void setCountry(String country){ this.country = country; }
public String getCountry(){ return this.country; }
@Override
public String toString() {
return String.format("Street : %s Country : %s", street, country);
}
}
public class TestSerializable {
public static void main(String[] args) throws Exception {
Address addr = new Address("wall street", "united state");
FileOutputStream fout = new FileOutputStream("address.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(addr);
oos.close();
FileInputStream fin = new FileInputStream("address.ser");
ObjectInputStream ois = new ObjectInputStream(fin);
Address addr2 = (Address) ois.readObject();
ois.close();
System.out.println(addr2);
// Street : wall street Country : united state
}
}
</pre><br />
參考自:<br />
<a href="http://www.mkyong.com/java/how-to-read-an-object-from-file-in-java/" target="_blank">How to read an Object from file in Java : Mkyong</a><br />
<a href="http://www.mkyong.com/java/how-to-write-an-object-to-file-in-java/" target="_blank">How to write an Object to file in Java : Mkyong</a><br />
<a href="http://www.mkyong.com/java-best-practices/understand-the-serialversionuid/" target="_blank">Understand the serialVersionUID : Mkyong</a>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-43492161064296939902015-03-04T11:20:00.000+08:002015-03-04T11:20:06.120+08:00[Java] 執行 shell command<pre class="java:nogutter:nocontrols" name="code">Process p = Runtime.getRuntime().exec("ping -n 3 google.com");
p.waitFor();
Scanner sc = new Scanner(p.getInputStream(), "MS950");
String output = sc.useDelimiter("\\Z").next();
sc.close();
System.out.println("exitValue: " + p.exitValue());
System.out.println(output);
</pre><br />
Output:<br />
<pre class="none:nogutter:nocontrols" name="code">exitValue: 0
Ping google.com [173.194.72.138] (使用 32 位元組的資料):
回覆自 173.194.72.138: 位元組=32 時間=21ms TTL=48
回覆自 173.194.72.138: 位元組=32 時間=20ms TTL=48
回覆自 173.194.72.138: 位元組=32 時間=18ms TTL=48
173.194.72.138 的 Ping 統計資料:
封包: 已傳送 = 3,已收到 = 3, 已遺失 = 0 (0% 遺失),
大約的來回時間 (毫秒):
最小值 = 18ms,最大值 = 21ms,平均 = 19ms
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-56012573972848588582015-03-03T16:40:00.000+08:002015-03-03T21:24:16.574+08:00[Java] 自定字符集 Charset網路上找到有關自定 Charset 的資料很少,大部分的人都是不建議自行定義一個 Charset,的確沒有什麼特別的理由,實在不需要去做這樣的事,但實際遇到的是工作上用的 DB 實在太舊,還在用 Big5 編碼,以及一堆的自造字,偏偏大部分都還是人名,讓別人的名子變成奇怪的字,實在很不禮貌,基於這個理由我延伸了 Big5 Charset。 <br />
<br />
要製作一個 Charset 一定要瞭解 Java 的字串核心,如果之前是寫 C 語言的朋友,首先要認知 char 跟 btye 是不一樣的,在 Java 的 char 是固定 Unicode 編碼,所有輸入都會轉成 Unicode,輸出時在轉成指定編碼,如下圖: <br />
<a href="http://3.bp.blogspot.com/-_1eAcNRBgno/VPVw3PUFKOI/AAAAAAAANrE/rjiMnJCqO8Y/s1600/java_char_io.png"><img src="http://3.bp.blogspot.com/-_1eAcNRBgno/VPVw3PUFKOI/AAAAAAAANrE/rjiMnJCqO8Y/s500/java_char_io.png" /></a><br />
<br />
<br />
那麼要實做哪些東西呢??<br />
<dl><dt>class Charset</dt>
<dd>定義 Charset 的名稱跟提供 Encoder 跟 Decoder,跟據 IANA 的規範自定的 Charset 名稱必須以 <code>X-</code> 或 <code>x-</code> 開頭。</dd>
<dt>class CharsetDecoder</dt>
<dd>字符解碼器負責將 <code>byte[]</code> 轉換成 <code>char[]</code>。</dd>
<dt>class CharsetEncoder</dt>
<dd>字符編碼器負責將 <code>char[]</code> 轉換成 <code>byte[]</code>。</dd>
<dt>class CharsetProvider</dt>
<dd>Charset 提供者,以名稱提供 Charset,在 <code>Charset.forName("xxx")</code> 調用時會尋訪所有的 Provider 來取得 Charset。</dd>
<dt>META-INF/services/java.nio.charset.spi.CharsetProvider</dt>
<dd>告知 JVM 將 <code>CharsetProvider</code> 註冊到延伸清單中,JVM 在啟動的時候會掃描所有這個路徑的檔案。</dd> <dd></dd> </dl><br />
最後再將 <code>{Charset}.jar</code> 檔複製到 <code>{jre}/lib/ext</code> 目錄下就部署完畢<br />
<br />
<br />
<h2>Big5_Extend</h2><pre class="java:nogutter:nocontrols" name="code">public class Big5_Extend extends Charset {
private static final String BASE_CHARSET = "Big5";
private static final String NAME = "X-Big5-Extend";
private static final String[] ALIASES = { "X-Big5_Extend" };
private Charset baseCharset;
public Big5_Extend() {
this(NAME, ALIASES);
}
public Big5_Extend(String canonical, String[] aliases) {
super(canonical, aliases);
baseCharset = Charset.forName(BASE_CHARSET);
}
public boolean contains(Charset cs) {
return this.getClass().isInstance(cs) ||
baseCharset.getClass().isInstance(cs);
}
public CharsetDecoder newDecoder() {
return new Decoder(this, baseCharset.newDecoder());
}
public CharsetEncoder newEncoder() {
return new Encoder(this, baseCharset.newEncoder());
}
// ...
}
</pre>繼承自 Charset,我們只要實作名稱定義跟 Encoder / Decoder 兩件事,當然如果有 char mapping array 也是在這裡初始化,讓所有 Encoder 跟 Decoder 可以共用同一個記憶體空間。 <br />
<br />
<br />
<h2>解碼處理</h2><pre class="java:nogutter:nocontrols" name="code">protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
base.reset(); /* 重置狀態 */
/* 先用原生的 Big5 進行解碼 */
CoderResult result = base.decode(in, out, true);
if(!result.isUnmappable() || in.remaining() < 2){ return result; }
/* 無法轉換,進一步使用自訂的解碼 */
int pos = in.position();
char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1));
char outChar;
switch (big5Char) {
case '\uFA40': outChar = '\u5803'; break; /* 堃 */
case '\uFA41': outChar = '\u83D3'; break; /* 菓 */
case '\uFA42': outChar = '\u854B'; break; /* 蕋 */
case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */
default: return result; /* 不在清單內直接回傳 */
}
out.put(outChar);
in.position(pos + 2);
return decodeLoop(in, out); /* 遞迴處理*/
}
</pre>解碼的部分就是先呼叫 big5 原本的 decode,當發生無法解碼的四種狀況就會停止解碼,回傳解碼狀態,我們只要針對 isUnmappable 的狀態接著處裡,base.reset() 是為了清除 Decoder 內部的狀態紀錄,不然會被前一次的解碼結果所影響。<br />
<br />
<br />
<h2>編碼處理</h2><pre class="java:nogutter:nocontrols" name="code">protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
base.reset(); /* 重置狀態 */
/* 先用原生的 Big5 進行編碼 */
CoderResult result = base.encode(in, out, true);
if(!result.isUnmappable() || out.remaining() < 2){ return result; }
/* 無法轉換,進一步使用自訂的編碼 */
int pos = in.position();
char uniChar = in.get(pos);
char outChar;
switch (uniChar) {
case '\u5803': outChar = '\uFA40'; break; /* 堃 */
case '\u83D3': outChar = '\uFA41'; break; /* 菓 */
case '\u854B': outChar = '\uFA42'; break; /* 蕋 */
case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */
default: return result; /* 不在清單內直接回傳 */
}
out.put((byte)(outChar >> 8));
out.put((byte)outChar);
in.position(pos + 1);
return encodeLoop(in, out); /* 遞迴處理*/
}
</pre>編碼的部分跟解碼採用相同的方式,一樣是先呼叫 big5 原本的 encode。<br />
<br />
<br />
<h2>CoderResult 四種狀態</h2><ul><li><b>UNDERFLOW</b> 欠位</li>
<li><b>OVERFLOW</b> 溢位</li>
<li><b>MALFORMED</b> 有缺陷的輸入</li>
<li><b>UNMAPPABLE</b> 無映射字符</li>
</ul><br />
<br />
<h2>完整的 Big5_Extend</h2><pre class="java:nogutter:nocontrols" name="code">package com.custom.nio.charset;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
public class Big5_Extend extends Charset {
private static final String BASE_CHARSET = "Big5";
private static final String NAME = "X-Big5-Extend";
private static final String[] ALIASES = { "X-Big5_Extend" };
private Charset baseCharset;
public Big5_Extend() {
this(NAME, ALIASES);
}
public Big5_Extend(String canonical, String[] aliases) {
super(canonical, aliases);
baseCharset = Charset.forName(BASE_CHARSET);
}
public boolean contains(Charset cs) {
return this.getClass().isInstance(cs) ||
baseCharset.getClass().isInstance(cs);
}
public CharsetDecoder newDecoder() {
return new Decoder(this, baseCharset.newDecoder());
}
public CharsetEncoder newEncoder() {
return new Encoder(this, baseCharset.newEncoder());
}
private class Decoder extends CharsetDecoder {
/* Java 原生的 Big5 解碼器 */
private final CharsetDecoder base;
Decoder(Charset cs, CharsetDecoder base) {
super(cs, base.averageCharsPerByte(), base.maxCharsPerByte());
this.base = base;
}
@Override
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
base.reset(); /* 重置狀態 */
/* 先用原生的 Big5 進行解碼 */
CoderResult result = base.decode(in, out, true);
if(!result.isUnmappable() || in.remaining() < 2){ return result; }
/* 無法轉換,進一步使用自訂的解碼 */
int pos = in.position();
char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1));
char outChar;
switch (big5Char) {
case '\uFA40': outChar = '\u5803'; break; /* 堃 */
case '\uFA41': outChar = '\u83D3'; break; /* 菓 */
case '\uFA42': outChar = '\u854B'; break; /* 蕋 */
case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */
default: return result; /* 不在清單內直接回傳 */
}
out.put(outChar);
in.position(pos + 2);
return decodeLoop(in, out);
}
}
private class Encoder extends CharsetEncoder {
/* Java 原生的 Big5 編碼器 */
private final CharsetEncoder base;
Encoder(Charset cs, CharsetEncoder base) {
super(cs, base.averageBytesPerChar(), base.maxBytesPerChar());
this.base = base;
}
@Override
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
base.reset(); /* 重置狀態 */
/* 先用原生的 Big5 進行編碼 */
CoderResult result = base.encode(in, out, true);
if(!result.isUnmappable() || out.remaining() < 2){ return result; }
/* 無法轉換,進一步使用自訂的編碼 */
int pos = in.position();
char uniChar = in.get(pos);
char outChar;
switch (uniChar) {
case '\u5803': outChar = '\uFA40'; break; /* 堃 */
case '\u83D3': outChar = '\uFA41'; break; /* 菓 */
case '\u854B': outChar = '\uFA42'; break; /* 蕋 */
case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */
default: return result; /* 不在清單內直接回傳 */
}
out.put((byte)(outChar >> 8));
out.put((byte)outChar);
in.position(pos + 1);
return encodeLoop(in, out);
}
}
}
</pre><br />
<br />
<h2>CharsetProvider</h2><pre class="java:nogutter:nocontrols" name="code">package com.custom.nio.charset;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
/** 字元編碼器連結器,用來向 JVM 提交自訂的編碼器
*/
public class CharsetProvider extends java.nio.charset.spi.CharsetProvider {
static Map<String, Charset> name2charset;
static Collection<Charset> charsets;
public Charset charsetForName(String charsetName) {
if (charsets == null){ init(); }
return name2charset.get(charsetName.toLowerCase());
}
public Iterator<Charset> charsets() {
if (charsets == null){ init(); }
return charsets.iterator();
}
void init() {
name2charset = new HashMap<String, Charset>();
charsets = new HashSet<Charset>();
charsets.add(new Big5_Extend());
for (Charset charset : charsets) {
name2charset.put(charset.name().toLowerCase(), charset);
for (String aliase: charset.aliases()) {
name2charset.put(aliase.toLowerCase(), charset);
}
}
}
}
</pre><br />
<br />
<h2>java.nio.charset.spi.CharsetProvider</h2><pre class="none:nogutter:nocontrols" name="code">com.custom.nio.charset.CharsetProvider
</pre>內容就一行類別定義<br />
<br />
<br />
<h2>測試</h2><pre class="java:nogutter:nocontrols" name="code">public class Test {
public static void main(String[] args) throws Throwable {
String charset = "X-Big5-Extend";
String source = "堃菓蕋侊";
byte[] bytes = source.getBytes(charset);
for (byte b : bytes) {
System.out.printf("%x ", b);
}
System.out.println("\n");
// fa 40 fa 41 fa 42 fa 43
String result = new String(bytes, charset);
System.out.println(result);
// 堃菓蕋侊
}
}
</pre><br />
參考自:<a href="http://www.jb51.net/article/36413.htm" target="_blank">Java字符编码解码的实现详解_java_脚本之家</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-81978445335236548722015-03-03T13:34:00.000+08:002015-03-03T13:34:49.453+08:00[轉載] Spring Collections (List, Set, Map, and Properties)轉載自:<a href="http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/" target="_blank">Spring Collections (List, Set, Map, and Properties) example</a><br />
<br />
Spring examples to show you how to inject values into collections type (List, Set, Map, and Properties). 4 major collection types are supported :<br />
<ul><li>List – <list/></li>
<li>Set – <set/></li>
<li>Map – <map/></li>
<li>Properties – <props/></li>
</ul><br />
<br />
<h2>Spring beans</h2>A Customer object, with four collection properties.<br />
<pre class="java:nogutter:nocontrols" name="code">package com.mkyong.common;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Customer
{
private List<Object> lists;
private Set<Object> sets;
private Map<Object, Object> maps;
private Properties pros;
//...
}
</pre>See different code snippets to declare collection in bean configuration file.<br />
<br />
<br />
<h2>1. List example</h2><pre class="xml:nogutter:nocontrols" name="code"><property name="lists">
<list>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongList" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</list>
</property>
</pre><br />
<br />
<h2>2. Set example</h2><pre class="xml:nogutter:nocontrols" name="code"><property name="sets">
<set>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongSet" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</set>
</property>
</pre><br />
<br />
<h2>3. Map example</h2><pre class="xml:nogutter:nocontrols" name="code"><property name="maps">
<map>
<entry key="Key 1" value="1" />
<entry key="Key 2" value-ref="PersonBean" />
<entry key="Key 3">
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongMap" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</entry>
</map>
</property>
</pre><br />
<br />
<h2>4. Properties example</h2><pre class="xml:nogutter:nocontrols" name="code"><property name="pros">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</pre><br />
<br />
<h2>Full Spring’s bean configuration file.</h2><pre class="xml:nogutter:nocontrols" name="code"><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="CustomerBean" class="com.mkyong.common.Customer">
<!-- java.util.List -->
<property name="lists">
<list>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongList" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</list>
</property>
<!-- java.util.Set -->
<property name="sets">
<set>
<value>1</value>
<ref bean="PersonBean" />
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongSet" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</set>
</property>
<!-- java.util.Map -->
<property name="maps">
<map>
<entry key="Key 1" value="1" />
<entry key="Key 2" value-ref="PersonBean" />
<entry key="Key 3">
<bean class="com.mkyong.common.Person">
<property name="name" value="mkyongMap" />
<property name="address" value="address" />
<property name="age" value="28" />
</bean>
</entry>
</map>
</property>
<!-- java.util.Properties -->
<property name="pros">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</bean>
<bean id="PersonBean" class="com.mkyong.common.Person">
<property name="name" value="mkyong1" />
<property name="address" value="address 1" />
<property name="age" value="28" />
</bean>
</beans>
</pre><br />
Run it…<br />
<pre class="java:nogutter:nocontrols" name="code">package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main( String[] args ) {
ApplicationContext context
= new ClassPathXmlApplicationContext("SpringBeans.xml");
Customer cust = (Customer)context.getBean("CustomerBean");
System.out.println(cust);
}
}
</pre><br />
Output<br />
<pre class="none:nogutter:nocontrols" name="code">Customer [
lists=[
1,
Person [address=address 1, age=28, name=mkyong1],
Person [address=address, age=28, name=mkyongList]
],
maps={
key 1=1,
key 2=Person [address=address 1, age=28, name=mkyong1],
key 3=Person [address=address, age=28, name=mkyongMap]
},
pros={
admin=admin@nospam.com,
support=support@nospam.com
},
sets=[
1,
Person [address=address 1, age=28, name=mkyong1],
Person [address=address, age=28, name=mkyongSet]
]
]
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-83209673505296905892015-03-01T20:14:00.000+08:002015-03-13T14:40:27.920+08:00[Java] Jackson Json Parser 筆記 <strong>Object Encode / Decode</strong><br />
<pre class="java:nocontrols" name="code">import java.util.Arrays;
import java.util.Date;
import com.fasterxml.jackson.databind.ObjectMapper;
class Album {
private int id;
private String title;
private Date date;
private String[] list;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date; }
public String[] getList() { return list; }
public void setList(String[] list) { this.list = list; }
@Override
public String toString() {
return String.format("id: %s, title: %s, date: %s, list: %s",
id, title, date, Arrays.toString(list)
);
}
}
public class TestJackson {
public static void main(String[] args) throws Exception {
Album album = new Album();
album.setId(1);
album.setTitle("Go Go Go!");;
album.setDate(new Date());
album.setList(new String[]{"Love", "Despair"});
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(album);
System.out.println(json);
// {"id":1,"title":"Go Go Go!","date":1425211903948,"list":["Love","Despair"]}
Album album2 = jsonMapper.readValue(json, Album.class);
System.out.println(album2);
// id: 1, title: Go Go Go!, date: Sun Mar 01 20:11:43 CST 2015, list: [Love, Despair]
}
}
</pre><br />
<br />
<strong>Parser to Map</strong><br />
<pre class="java:nogutter:nocontrols" name="code">ObjectMapper jsonMapper = new ObjectMapper();
Map<String,String> map;
map = jsonMapper.readValue(
"{\"name\":\"jax\", \"age\":\"31\"}",
new TypeReference<HashMap<String,String>>(){}
);
System.out.println(map);
// {age=31, name=jax}
jsonMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
jsonMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
map = jsonMapper.readValue(
"{name:'jax', age:'31'}",
new TypeReference<HashMap<String,String>>(){}
);
System.out.println(map);
// {age=31, name=jax}
</pre><br />
<br />
<strong>Encode Date</strong> <br />
<pre class="java:nogutter:nocontrols" name="code">Date date = new Date();
String json;
ObjectMapper jsonMapper = new ObjectMapper();
json = jsonMapper.writeValueAsString(date);
System.out.println(json);
// 1425211840183
jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
json = jsonMapper.writeValueAsString(date);
System.out.println(json);
// "2015-03-01T12:10:40.183+0000"
</pre><br />
參考自:<a href="https://github.com/FasterXML/jackson-databind/" target="_blank">FasterXML/jackson-databind · GitHub</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com3tag:blogger.com,1999:blog-5946530704742130970.post-83153883473562477762015-03-01T15:36:00.001+08:002015-03-01T15:36:18.139+08:00Spring Security 存取控制表示式<h2>常用内建表示式</h2>ps: 定義在 SecurityExpressionRoot<br />
<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1"><tr class="header"><th>表示式</th><th>說明</th></tr>
<tr><td><b>hasRole('role')</b></td><td>當前的 User 擁有指定的 Role 就回傳 true</td></tr>
<tr><td><b>hasAnyRole('role1', 'role2')</b></td><td>當前的 User 擁有任一個 Role 就回傳 true</td></tr>
<tr><td><b>principal</b></td><td>當前的 User 的 Principal 物件</td></tr>
<tr><td><b>authentication</b></td><td>當前的 User 的 Authentication 物件</td></tr>
<tr><td><b>permitAll</b></td><td>總是為 true</td></tr>
<tr><td><b>denyAll</b></td><td>總是為 false</td></tr>
<tr><td><b>isAnonymous()</b></td><td>當前的 User 是匿名登入就回傳 true</td></tr>
<tr><td><b>isRememberMe()</b></td><td>當前的 User 是透過 remember-me 登入就回傳 true</td></tr>
<tr><td><b>isAuthenticated()</b></td><td>當前的 User 不是匿名登入就回傳 true</td></tr>
<tr><td><b>isFullyAuthenticated()</b></td><td>當前的 User 不是匿名登入或 remember-me 登入就回傳 true</td></tr>
</table><br />
<br />
<br />
<h2>在方法執行前的驗證</h2><br />
驗證 User 角色<br />
<pre class="java:nogutter:nocontrols" name="code">@PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);
</pre><br />
驗證參數值是否等於 User 名稱<br />
<pre class="java:nogutter:nocontrols" name="code">@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
</pre><br />
驗證 User 角色以及來源 IP 區間<br />
<pre class="java:nogutter:nocontrols" name="code">@PreAuthorize("hasRole('admin') and hasIpAddress('192.168.1.0/24')")
public void doSomething(Contact contact);
</pre><br />
<br />
<br />
<h2>在方法內的驗證</h2><br />
取得角色驗證<br />
<pre class="java:nogutter:nocontrols" name="code">@RequestMapping("/index")
public void index(HttpServletRequest request) {
System.out.println(request.isUserInRole("ROLE_USER"));
if (request.isUserInRole("admin")) {
System.out.println("is admin");
}
}
</pre><br />
<br />
<br />
<h2>在 JSP 的驗證</h2><br />
取得 User 名稱<br />
<pre class="xml:nogutter:nocontrols" name="code"><sec:authentication property="name"/>
<sec:authentication property="principal.username"/>
</pre><br />
取得 User IP<br />
<pre class="xml:nogutter:nocontrols" name="code"><sec:authentication property="details.remoteAddress"/>
</pre><br />
取得 User SessionId<br />
<pre class="xml:nogutter:nocontrols" name="code"><sec:authentication property="details.sessionId"/>
</pre><br />
驗證角色為 admin 才顯示<br />
<pre class="xml:nogutter:nocontrols" name="code"><sec:authorize access="hasRole('admin')">
<div>is admin</div>
</sec:authorize>
</pre><br />
驗證角色為 admin 存入變數 isAdmin<br />
<pre class="xml:nogutter:nocontrols" name="code"><sec:authorize var="isAdmin" access="hasRole('admin')" />
<c:if test="isAdmin">
<div>is admin</div>
</c:if>
</pre><br />
<br />
參考自:<a href="http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html" target="_blank">15. Expression-Based Access Control</a><br />
<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-51245005324441978602015-02-26T16:15:00.001+08:002015-02-26T16:35:29.948+08:00[轉載] Java 內部類別轉載自:<a href="http://openhome.cc/Gossip/Java/InnerClass.html" target="_blank">Java Gossip: 內部類別</a><br />
<br />
可以在類別中再定義類別,稱之為內部類別(Inner class),初學者暫時不會使用到內部類別,在這邊先簡單介紹語法,雖然會無聊一些,不過之後章節就會看到相關應用。<br />
<br />
內部類別可以定義在類別區塊之中。例如以下程式片段建立了非靜態的內部類別:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">class Some {
class Other {
}
}
</pre><br />
雖然實務上很少看到接下來的寫法,不過要使用 <code>Some</code> 中的 <code>Other</code> 類別,必須先建立實例 <code>Some</code> 實例。例如:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">Some s = new Some();
Some.Other o = s.new Other();
</pre><br />
內部類別也可以使用 <b>public</b>、<b>protected</b> 或 <b>private</b> 宣告。例如:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">class Some {
private class Other {
}
}
</pre><br />
內部類別本身可以存取外部類別的成員,通常非靜態內部類別會宣告為 <b>private</b>,這類內部類別是輔助類別中某些操作而設計,外部不用知道內部類別的存在。<br />
<br />
內部類別也可以宣告為 <b>static</b>。例如:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">class Some {
static class Other {
}
}
</pre><br />
一個被宣告為 static 的內部類別,通常是將外部類別當作名稱空間。你可以如下建立類別實例:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">Some.Other o = new Some.Other();
</pre><br />
被宣告為 <b>static</b> 的內部類別,雖然將外部類別當作名稱空間,但算是個獨立類別,它可以存取外部類別 <b>static</b> 成員,但不可存取外部類別非 <b>static</b> 成員。例如:<br />
<img src="http://2.bp.blogspot.com/-R99-VO01ZWc/VO7aUCENdCI/AAAAAAAANpo/vHAvwetaCzk/s550/InnerClass-1.PNG" /><br />
<br />
方法中也可以宣告類別,這通常是輔助方法中演算之用,方法外無法使用。例如:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">class Some {
public void doSome() {
class Other {
}
}
}
</pre><br />
實務上比較少看到在方法中定義具名的內部類別,倒很常看到方法中定義匿名內部類別(Anonymous inner class)並直接實例化,這跟類別繼承或介面實作有關,以下先看一下語法,細節留到談到繼承與介面時再作討論:<br />
<br />
<pre class="java:nogutter:nocontrols" name="code">Object o = new Object() {
public String toString() {
return "無聊的語法示範而已";
}
};
</pre><br />
如果要稍微解釋一下,這個語法定義了一個沒有名稱的類別,它繼承 <code>Object</code> 類別,並重新定義(Override)了 <code>toString()</code> 方法,<b>new</b> 表示實例化這個沒有名稱的類別。匿名類別語法本身,在某些場合有時有些囉嗦,JDK 8 提出了 Lambda,有一部份目的是用來解決匿名類別語法囉嗦的問題,之後會再討論。<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-43582761593150662342015-02-26T16:07:00.001+08:002015-02-26T16:07:43.331+08:00[轉載] Java 動態代理轉載自:<a href="http://openhome.cc/Gossip/JavaEssence/Proxy.html" target="_blank">Java Essence: 動態代理</a><br />
<br />
來看一個最簡單的例子,當您需要在執行某些方法時留下日誌訊息,直覺的,您可能會如下撰寫:<br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
import java.util.logging.*;
public class HelloSpeaker {
private Logger logger =
Logger.getLogger(this.getClass().getName());
public void hello(String name) {
// 方法執行開始時留下日誌
logger.log(Level.INFO, "hello method starts....");
// 程式主要功能
System.out.println("Hello, " + name);
// 方法執行完畢前留下日誌
logger.log(Level.INFO, "hello method ends....");
}
}
</pre><br />
<br />
在 <code>HelloSpeaker</code> 類別中,當執行 <code>hello()</code> 方法時,你希望該方法執行開始與執行完畢時都能留下日誌,最簡單的作法就是如以上的程式設計,在 方法執行的前後加上日誌動作,然而記錄的這幾行程式碼橫切入(Cross-cutting)<code>HelloSpeaker</code> 類別中,對於 <code>HelloSpeaker</code> 來說,日誌的這幾個動作並不屬於 <code>HelloSpeaker</code> 商務邏輯(顯示"Hello"等文字),這使得 <code>HelloSpeaker</code> 增加了額外的職責。<br />
<br />
想想如果程式中這種日誌的動作到處都有需求,以上的寫法勢必造成你必須到處撰寫這些日誌動作的程式碼,這將使得維護日誌程式碼的困難度加大。如果需要的服 務(Service)不只有日誌動作,有一些非物件本身職責的相關動作也混入了物件之中(例如權限檢查、交易管理等等),會使得物件的負擔更形加重,甚至 混淆了物件本身該負有的職責,物件本身的職責所佔的程式碼,或許還小於這些與物件職責不相關的動作或服務的程式碼。<br />
<br />
另一方面,使用以上的寫法,若你有一日不再需要日誌(或權限檢查、交易管理等)的服務,那麼你將需要修改所有留下日誌動作的程式碼,你無法簡單的就將這些相關服務從即有的程式中移去。<br />
<br />
可以使用代理(Proxy)機制來解決這個問題,在這邊討論兩種代理方式:<br />
<ul><li><b>靜態代理(Static proxy)</b></li>
<li><b>動態代理(Dynamic proxy)</b></li>
</ul><br />
在靜態代理的實現中,代理物件與被代理的物件都必須實現同一個介面,在代理物件中可以實現記錄等相關服務,並在需要的時候再呼叫被代理的物件,如此被代理物件當中就可以僅保留商務相關職責。<br />
<br />
舉個實際的例子來說,首先定義一個 <code>IHello</code> 介面:<br />
<em>IHello.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
public interface IHello {
public void hello(String name);
}
</pre><br />
<br />
然後讓實現商務邏輯的 <code>HelloSpeaker</code> 類別要實現 <code>IHello</code> 介面,例如:<br />
<em>HelloSpeaker.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello, " + name);
}
}
</pre><br />
<br />
可以看到,在 <code>HelloSpeaker</code> 類別中現在沒有任何日誌的程式碼插入其中,日誌服務的實現將被放至代理物件之中,代理物件同樣也要實現 <code>IHello</code> 介面,例如:<br />
<em>HelloProxy.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
import java.util.logging.*;
public class HelloProxy implements IHello {
private Logger logger =
Logger.getLogger(this.getClass().getName());
private IHello helloObject;
public HelloProxy(IHello helloObject) {
this.helloObject = helloObject;
}
public void hello(String name) {
// 日誌服務
log("hello method starts....");
// 執行商務邏輯
helloObject.hello(name);
// 日誌服務
log("hello method ends....");
}
private void log(String msg) {
logger.log(Level.INFO, msg);
}
}
</pre><br />
<br />
在 <code>HelloProxy</code> 類別的 <code>hello()</code> 方法中,真正實現商務邏輯前後可以安排記錄服務,可以實際撰寫一個測試程式來看看如何使用代理物件。<br />
<em>ProxyDemo.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
public class ProxyDemo {
public static void main(String[] args) {
IHello proxy =
new HelloProxy(new HelloSpeaker());
proxy.hello("Justin");
}
}
</pre><br />
<br />
程式中呼叫執行的是代理物件,建構代理物件時必須給它一個被代理物件,記得在操作取回的代理物件時,必須轉換操作介面為 IHello 介面。<br />
<br />
代理物件 <code>HelloProxy</code> 將代理真正的 <code>HelloSpeaker</code> 來執行 <code>hello()</code>,並在其前後加上日誌的動作,這使得我們的 <code>HelloSpeaker</code> 在撰寫時不必介入日誌動作,<code>HelloSpeaker</code> 可以專心於它的職責。<br />
<br />
在 JDK 1.3 之後加入了可協助開發動態代理功能的 API 等相關類別,您不必為特定物件與方法撰寫特定的代理物件,使用動態代理,可以使得一個處理者 (Handler)服務於各個物件,首先,一個處理者的類別設計必須實作 <code>java.lang.reflect.InvocationHandler</code> 介面, 以實例來進行說明,例如設計一個 <code>LogHandler</code> 類別:<br />
<em>LogHandler.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
import java.util.logging.*;
import java.lang.reflect.*;
public class LogHandler implements InvocationHandler {
private Logger logger =
Logger.getLogger(this.getClass().getName());
private Object delegate;
public Object bind(Object delegate) {
this.delegate = delegate;
return Proxy.newProxyInstance(
delegate.getClass().getClassLoader(),
delegate.getClass().getInterfaces(),
this
);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
Object result = null;
try {
log("method starts..." + method);
result = method.invoke(delegate, args);
logger.log(Level.INFO, "method ends..." + method);
} catch (Exception e){
log(e.toString());
}
return result;
}
private void log(String message) {
logger.log(Level.INFO, message);
}
}
</pre><br />
<br />
主要的概念是使用 <code>Proxy.newProxyInstance()</code> 靜態方法建立一個代理物件(底層會使用 Native 的方式生成代理物件的 Class 實例),建立代理物件時必須告知所要代理的介面,之後您可以操作所 建立的代理物件,在每次操作時會呼叫 <code>InvocationHandler</code> 的 <code>invoke()</code> 方法,<code>invoke()</code> 方法會傳入被代理物件的方法名稱與執行 參數,實際上要執行的方法交由 <code>method.invoke()</code>,您在 <code>method.invoke()</code> 前後加上記錄動作,<code>method.invoke()</code> 傳 回的物件是實際方法執行過後的回傳結果。<br />
<br />
接下來撰寫一個測試的程式,您要使用 <code>LogHandler</code> 的 <code>bind()</code> 方法來綁定被代理物件,如下所示:<br />
<em>ProxyDemo.java</em><br />
<pre class="java:nogutter:nocontrols" name="code">package cc.openhome;
public class ProxyDemo {
public static void main(String[] args) {
LogHandler logHandler = new LogHandler();
IHello helloProxy =
(IHello) logHandler.bind(new HelloSpeaker());
helloProxy.hello("Justin");
}
}
</pre><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-87873507994584505492015-02-26T15:44:00.000+08:002015-02-26T15:44:09.024+08:00[轉載] Java 型態通配字元轉載自:<a href="http://openhome.cc/Gossip/JavaEssence/WildCard.html" target="_blank">Java Essence: 我只收這種東西</a><br />
<br />
如果你定義了以下的類別:<br />
<pre class="java:nogutter:nocontrols" name="code">class Node<T> {
T value;
Node<T> next;
Node(T value, Node<T> next) {
this.value = value;
this.next = next;
}
}
</pre><br />
<br />
如果在以下的例子中:<br />
<pre class="java:nogutter:nocontrols" name="code">class Fruit {}
class Apple extends Fruit {
@Override
public String toString() {
return "Apple";
}
}
class Banana extends Fruit {
@Override
public String toString() {
return "Banana";
}
}
public class Main {
public static void main(String[] args) {
Node<Apple> apple = new Node<Apple>(new Apple(), null);
Node<Fruit> fruit = apple; // 編譯錯誤,incompatible types
}
}
</pre><br />
<br />
<br />
在範例中,apple 的型態是 <code>Node<Apple></code>,而 fruit 的型態為 <code>Node<Fruit></code>,你將 apple 所參考的物件 給 fruit 參考,那麼 <code>Node<Apple></code> 該是一種 <code>Node<Fruit></code> 呢?在上例中編譯器給你的答案為「不是」!<br />
<br />
如 果 B 是 A 的子型態,而 <code>Node<B></code> 被視為一種 Node<A> 型態,則稱 Node 具有<b>共變性(Covariance)</b>或有<b>彈性的(flexible)</b>。如 果 <code>Node<A></code> 被視為一種 <code>Node<B></code> 型態,則稱 Node 具有<b>逆變性(Contravariance)</b>。如果不具共變性或逆變性,則 Node 是<b>不可變 的(nonvariant)</b>或<b>嚴謹的(rigid)</b>。<br />
<br />
Java 的泛型不支援<b>共變性</b>,不過可以使用型態通配字元 <b>?</b> 與 <b>extends</b> 來宣告變數,使其達到類似共變性,例如:<br />
<pre class="java:nogutter:nocontrols" name="code">public class Main {
public static void main(String[] args) {
Node<Apple> apple = new Node<Apple>(new Apple(), null);
Node<? extends Fruit> fruit = apple; // 類似共變性效果
}
}
</pre><br />
<br />
一個實際應用的例子是:<br />
<pre class="java:nogutter:nocontrols" name="code">public class Main {
public static void main(String[] args) {
Node<Apple> apple1 = new Node<Apple>(new Apple(), null);
Node<Apple> apple2 = new Node<Apple>(new Apple(), apple1);
Node<Apple> apple3 = new Node<Apple>(new Apple(), apple2);
Node<Banana> banana1 = new Node<Banana>(new Banana(), null);
Node<Banana> banana2 = new Node<Banana>(new Banana(), banana1);
show(apple3);
show(banana2);
}
static void show(Node<? extends Fruit> n) {
Node<? extends Fruit> node = n;
do {
System.out.println(node.value);
node = node.next;
} while(node != null);
}
}
</pre><br />
<br />
<br />
你的目的是可以顯示所有的水果節點,由於 <code>show()</code> 方法使用型態通配字元宣告參數,使得 n 具備類似共變性的效果,因此 <code>show()</code> 方法就可以顯示 <code>Node<Apple></code> 也可以顯示 <code>Node<Banana></code>。<br />
<br />
Java 的泛型亦不支援<b>逆變性</b>,不過可以使用型態通配字元 <b>?</b> 與 <b>super</b> 來宣告變數,使其達到類似逆變性,例如:<br />
<pre class="java:nogutter:nocontrols" name="code">public class Main {
public static void main(String[] args) {
Node<Fruit> fruit = new Node<Fruit>(new Fruit(), null);
Node<? super Apple> apple = fruit;
Node<? super Banana> banana = fruit;
}
}
</pre><br />
<br />
一個實際應用的例子如下:<br />
<pre class="java:nogutter:nocontrols" name="code">class Fruit {
int price;
int weight;
Fruit(int price, int weight) {
this.price = price;
this.weight = weight;
}
}
class Apple extends Fruit {
Apple(int price, int weight) {
super(price, weight);
}
}
class Banana extends Fruit {
Banana(int price, int weight) {
super(price, weight);
}
}
interface Comparator<T> {
int compare(T t1, T t2);
}
class Basket<T> {
private T[] things;
Basket(T... things) {
this.things = things;
}
void sort(Comparator<? super T> comparator) {
// 作一些排序
}
}
</pre><br />
<br />
籃子(Basket)中可以置放各種物品,並可以傳入一個比較器(Comparator)進行排序。假設你分別在兩個籃子中放置了蘋果(Apple)與香蕉(Banana):<br />
<pre class="java:nogutter:nocontrols" name="code">public class Main {
public static void main(String[] args) {
Comparator<Fruit> comparator = new Comparator<Fruit>() {
public int compare(Fruit f1, Fruit f2) {
return f1.price - f2.price;
}
};
Basket<Apple> b1 = new Basket<Apple>(
new Apple(20, 100), new Apple(25, 150)
);
Basket<Banana> b2 = new Basket<Banana>(
new Banana(30, 200), new Banana(25, 250)
);
b1.sort(comparator);
b2.sort(comparator);
}
}
</pre><br />
<br />
現在 b1 的型態為 <code>Basket<Apple></code>,而 b2 的型態為 <code>Basket<Banana></code>,你可以如上實作一個水果(Fruit)比較器,比較水果的價格進行排序,這可以同時適用於 <code>Basket<Apple></code> 與 <code>Basket<Banana></code>。<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-61888603785399785712015-02-26T14:40:00.001+08:002023-02-25T21:30:47.901+08:00[PHP][Java][C#] 用 XSD 驗證 XML<strong>menu_config.xml</strong> 要驗證的 XML<br />
<pre class="xml:nocontrols" name="code"><?xml version="1.0" encoding="utf-8"?>
<menu_config>
<menu title="文章管理" url="~/Article" target="" allow="">
<submenu title="列表" url="~/Article/list" target="" allow=""/>
<submenu/>
<submenu title="新增" url="~/Article/add" target="" allow=""/>
</menu>
<menu/>
<menu title="帳號管理" url="~/Admin"/>
</menu_config>
</pre><br />
<br />
<strong>menu_config.xsd</strong> 結構定義<br />
<pre class="xml:nocontrols" name="code"><?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="menu_config">
<xs:complexType>
<xs:sequence>
<xs:element name="menu" type="menuType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="menuType">
<xs:sequence>
<xs:element name="submenu" type="menuType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="title" type="xs:string"/>
<xs:attribute name="url" type="xs:string"/>
<xs:attribute name="target" type="xs:string"/>
<xs:attribute name="allow" type="xs:string"/>
</xs:complexType>
</xs:schema>
</pre><dl><dt>element minOccurs</dt>
<dd>最少的出現次數,不設置為至少出現 1 次,設置 0 為可有可無。</dd>
<dt>element maxOccurs</dt>
<dd>最大的出現次數,設置 unbounded 為無上限。</dd>
<dt>attribute type</dt>
<dd>型別必須為 QName,常用的有 xs:string, xs:date, xs:int, xs:integer, xs:decimal, xs:boolean, xs:double, xs:float。</dd> </dl><br />
<br />
<strong>PHP</strong><br />
<pre class="php:nocontrols" name="code">$xmlFile = 'menu_config.xml';
$xsdFile = 'menu_config.xsd';
/* 啟用自行錯誤處裡 */
libxml_use_internal_errors(true);
$xml = new DOMDocument();
$xml->load($xmlFile);
if (!$xml->schemaValidate($xsdFile)) {
/* 顯示錯誤訊息 */
print_r(libxml_get_errors());
libxml_clear_errors();
}
</pre><br />
<br />
<strong>C#</strong><br />
<pre class="cs:nocontrols" name="code">var menuConfig = XDocument.Load("menu_config.xml");
schemas.Add("", XmlReader.Create("menu_config.xsd"));
menuConfig.Validate(schemas, (o, e) => {
e.Message.Dump();
});
</pre><br />
<br />
<strong>Java</strong><br />
<pre class="java:nocontrols" name="code">import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.xml.sax.SAXException;
public class TestXsd {
public static void main(String[] args) {
File xsd = new File("menu_config.xsd");
File xml = new File("menu_config.xml");
try {
SchemaFactory factory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI
);
Schema schema = factory.newSchema(xsd);
Validator validator = schema.newValidator();
validator.validate(new StreamSource(xml));
System.out.println("xml is valid");
}
catch (SAXException|IOException e) {
System.out.println("Reason: " + e.getLocalizedMessage());
}
}
}
</pre><br />
<br />
<br />
參考資料:<br />
<a href="https://msdn.microsoft.com/zh-tw/library/ms256142(v=vs.110).aspx" target="_blank">XML 結構描述項目 - MSDN</a><br />
<a href="http://www.w3schools.com/schema/default.asp" target="_blank">XML Schema Tutorial W3Schools</a>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-84562807106555137832015-02-26T13:16:00.001+08:002015-02-26T13:16:22.052+08:00[Java] 列出 HttpServletRequest header<pre class="java:nocontrols" name="code">Enumeration<String> names = request.getHeaderNames();
for (String name : Collections.list(names)) {
System.out.println(name + " : " + request.getHeader(name));
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-56657232459362873182015-02-26T13:09:00.000+08:002015-02-26T13:10:35.724+08:00[Java] ipToLong 與 longToIp<pre class="java:nocontrols" name="code">public class IpToLong {
public static long ipToLong(String ip) {
long result = 0;
String[] numbers = (ip + ".0.0.0").split("\\.");
for (int i = 0; i < 4; i++) {
result = result << 8;
result |= Long.parseLong(numbers[i]) & 0xFF;
}
return result;
}
public static String longToIp(long ip) {
return (ip >> 24 & 0xFF) + "." +
(ip >> 16 & 0xFF) + "." +
(ip >> 8 & 0xFF) + "." +
(ip & 0xFF);
}
public static void main(String[] args) {
String[] testList = {
"0.0.0.0",
"255.255.255.255",
"192.168.1.2"
};
for (String ip : testList) {
long ipNum = ipToLong(ip);
System.out.printf(
"%s | %s | %s\n", ip, longToIp(ipNum), ipNum
);
}
}
}
</pre><br />
Output:<br />
<pre class="none:nocontrols" name="code">0.0.0.0 | 0.0.0.0 | 0
255.255.255.255 | 255.255.255.255 | 4294967295
192.168.1.2 | 192.168.1.2 | 3232235778
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-41059270666300159762015-02-26T12:48:00.000+08:002015-02-26T12:48:02.408+08:00[Java] 取得 Client IP<pre class="java:nocontrols" name="code">public String getClientIp(HttpServletRequest request){
String ip = request.getHeader("X-FORWARDED-FOR");
if (ip == null) {
ip = request.getRemoteAddr();
}
return ip;
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-8483276154308296222015-02-26T09:47:00.000+08:002015-02-26T09:47:45.363+08:00[Java] array, List, Set, Map 排序 <pre class="java" name="code">import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
public class TestSort {
public static void main(String[] args) {
/* sort array */
String[] array1 = new String[] {
"Pineapple", "Apple", "Orange", "Banana"
};
Arrays.sort(array1);
for (String value : array1) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* reverse sort array */
String[] array2 = new String[] {
"Pineapple", "Apple", "Orange", "Banana"
};
Arrays.sort(array2, Collections.reverseOrder());
for (String value : array2) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* sort list */
List<String> list1 = Arrays.asList(
"Pineapple","Apple", "Orange", "Banana"
);
Collections.sort(list1);
for (String value : list1) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* reverse sort list */
List<String> list2 = Arrays.asList(
"Pineapple","Apple", "Orange", "Banana"
);
Collections.sort(list2, Collections.reverseOrder());
for (String value : list2) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* sort set */
Set<String> set = new HashSet<>();
set.add("Pineapple");
set.add("Apple");
set.add("Orange");
set.add("Banana");
Set<String> treeSet1 = new TreeSet<>(set);
for (String value : treeSet1) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* reverse sort set */
Set<String> treeSet2 = new TreeSet<>(Collections.reverseOrder());
treeSet2.addAll(set);
for (String value : list2) {
System.out.printf("%s, ", value);
}
System.out.println("\n");
/* sort map */
Map<String, String> map = new HashMap<>();
map.put("Pineapple","z");
map.put("Apple", "g");
map.put("Orange", "a");
map.put("Banana", "f");
Map<String, String> treeMap1 = new TreeMap<>(map);
for (Map.Entry<String, String> entry : treeMap1.entrySet()) {
System.out.printf(
"Key : %s Value : %s \n",
entry.getKey(), entry.getValue()
);
}
System.out.println();
/* reverse sort map */
Map<String, String> treeMap2 =
new TreeMap<>(Collections.reverseOrder());
treeMap2.putAll(map);
for (Map.Entry<String, String> entry : treeMap2.entrySet()) {
System.out.printf("Key : %s Value : %s \n",
entry.getKey(), entry.getValue()
);
}
System.out.println();
}
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-70363097865536340622015-02-24T13:50:00.000+08:002015-02-26T09:44:23.294+08:00[Java] 取得檔案編碼格式使用 <a href="https://code.google.com/p/juniversalchardet/" target="_blank">Mozilla juniversalchardet</a> 套件<br />
<br />
<pre class="java" name="code">import java.io.FileInputStream;
import org.mozilla.universalchardet.UniversalDetector;
public class ReadFileCharset {
public static void main(String[] args) throws Exception {
/* 讀取檔案 */
FileInputStream fis = new FileInputStream("input.txt");
/* 建立分析器 */
UniversalDetector detector = new UniversalDetector(null);
int nread;
byte[] buf = new byte[4096];
while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
/* 分析資料 */
detector.handleData(buf, 0, nread);
}
fis.close();
detector.dataEnd();
/* 取得編碼格式 */
String encode = detector.getDetectedCharset();
System.out.println(encode);
}
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0