2013年4月5日 星期五

Java API 實作動態單一實例工廠+AOP LOG使用自訂ANNOTATION


當習慣用Spring之後
突然遇到不使用Spring的系統真的很不方便…
要寫一堆Factory...
更別提到寫一堆log真的會煩死人…
於是興起了這個念頭…
何不自已寫一個Singleton Bean Factory與AOP做LOG
最好能夠用自定義的ANNOTATION就能作動…
ok… 搞了一個鐘頭… 還好不太難
主要在中間層service的部份做log就夠了

先來看成果:
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:

代理類別:sun.proxy.$Proxy4
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
執行類別:idv.tsaipifong.MyService
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
執行方法:doSome01
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
傳入值:{"傳入值類別 > java.lang.String":"00001"}
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
傳回值:{"id":"00001","name":"Pifong Tsai"}
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:

代理類別:sun.proxy.$Proxy4
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
執行類別:idv.tsaipifong.MyService
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
執行方法:doException
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
傳入值:{"傳入值類別 > java.lang.String":"這是例外訊息"}
2013/4/6 上午 02:34:54 idv.tsaipifong.MyLogger invoke
資訊:
發生例外,訊息如后:
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
idv.tsaipifong.MyLogger.invoke(MyLogger.java:41)
sun.proxy.$Proxy4.doException(Unknown Source)
idv.tsaipifong.Test.main(Test.java:10)
處理例外


相當不錯
記錄了執行類別、方法、傳入值、傳回值、例外資訊

動態代理的AOP主程式如下:(仔細看invoke()就覺得跟Spring AOP的實作有點像)

public class MyLogger implements InvocationHandler {

private Logger logger = Logger.getLogger(this.getClass().getName());

private Object target;

public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
logger.info("\n\n代理類別:" + proxy.getClass().getName());
logger.info("\n執行類別:" + target.getClass().getName());
logger.info("\n執行方法:" + method.getName());

if(args==null || args.length<=0){ logger.info("\n傳入值:無傳入值"); }else{ JSONObject json = new JSONObject(); for(Object obj : args){ json.put("傳入值類別 > " + obj.getClass().getName(), obj);
}
logger.info("\n傳入值:" + json.toString());
}

result = method.invoke(target, args);

if(result==null){
logger.info("\n傳回值:無傳回值");
}else{
logger.info("\n傳回值:" + JSONObject.fromObject(result));
}

} catch (Exception e){
StackTraceElement[] ste = e.getStackTrace();
StringBuffer sb = new StringBuffer();
for(StackTraceElement s : ste){
sb.append("\n" + s);
}
logger.info("\n發生例外,訊息如后:" +sb.toString());
throw e; //處理log之後,拋出例外
}
return result;
}
}


動態單一實例工廠主程式如下:

public class ServiceFactory {

private static Map map = null;

private ServiceFactory(){

}
public static Object getInstance(String classname)throws Exception {
if(classname==null){
throw new Exception("Classname is null...");
}
if(map==null){
synchronized(HashMap.class){
map = new HashMap();
}
}
Object obj = null;
synchronized(map){
if(map.get(classname)==null){
Class cls = Class.forName(classname);
//如果有@DoLogger註解,則套上aop proxy
if(cls.isAnnotationPresent(idv.tsaipifong.DoLogger.class)){
MyLogger mylogger = new MyLogger();
map.put(classname, mylogger.getProxy(cls.newInstance()));
}else{
map.put(classname, cls.newInstance());
}
}
obj = map.get(classname);
}
return obj;
}
}


自定義的ANNOTATION如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DoLogger {

}


以上就完成了… 那要怎麼使用呢…!?

一、首先要定義service的介面

public interface IMyService {
public Map doSome01(String str);
public void doException(String msg)throws Exception;
}


二、然後實作介面的類別
如果這個service要做log
就在類別宣告加上自訂的annotation,@DoLogger
沒加就不做log

@DoLogger
public class MyService implements IMyService {

@Override
public Map doSome01(String str) {
Map map = new HashMap();
map.put("id", str);
map.put("name", "Pifong Tsai");
return map;
}

@Override
public void doException(String msg) throws Exception {
throw new Exception(msg);
}
}


三、最後當然要執行一下看看囉~

IMyService myservice = null;
try {
myservice = (IMyService)ServiceFactory.getInstance("idv.tsaipifong.MyService");
myservice.doSome01("00001");
myservice.doException("這是例外訊息");
} catch (Exception e) {
System.out.println("處理例外");
}


運用aop搭配annotation,程式碼就簡潔多了
資料格式驗證、交易等等,都可以用類似的方式去實作
不過Spring已經是很成熟優秀的框架了…
能用就用囉… 不必這麼累還需要自已開發…
小弟實在是千萬個不得已啊…

2013年1月10日 星期四

Tomcat 設定 SSL

在Tomcat中要設定SSL事實上十分簡單,甚至無需認證主機即可進行,步驟如下:

1.C:\>%JAVA_HOME%\bin\keytool -genkey -keyalg RSA -alias test -keystore .keystore

輸入 keystore 密碼:abcde1234
您的名字與姓氏為何?
[Unknown]: AAA
您的編制單位名稱為何?
[Unknown]: AAA
您的組織名稱為何?
[Unknown]: AAA
您所在的城市或地區名稱為何?
[Unknown]: Taipei
您所在的州及省份名稱為何?
[Unknown]: Taipei
該單位的二字國碼為何
[Unknown]: tw
CN=AAA, OU=AAA, O=AAA, L=Taipei, ST=Taipei, C=tw 正確嗎?
[否]: y
輸入 的主密碼
(RETURN 如果和 keystore 密碼相同):

2.將前一步驟所產生出來的.keystore放置到tomcat目錄中

3.修改%TOMCAT_HOME%\conf\server.xml,找到被註解掉的SSL設定行
< Connector port="8443" maxthreads="150" minsparethreads="25" maxsparethreads="75" enablelookups="false" disableuploadtimeout="true" acceptcount="100" debug="0" scheme="https" secure="true" clientauth="false" sslprotocol="TLS" keystoreFile=".keystore" keystorePass="abcde1234"/>
並加入粗斜體字的兩行指定keystore檔案與密碼。

4.重新啟動tomcat即大功告成

SSL預設port為443,是故可以將tomcat的預設port從8443修改為443,如此依來以https進入WebApp時,便無須輸入port(就如同http的預設port為80)

詳細的設定方式,會因應tomcat的版本而些微調整,可以參考官方網站的設定方式。

2012年5月27日 星期日

JavaScript - 字串截取的區別 substr 與 substring



1. substr ( index, length ):
從 index 起,截取長度為 length 的字串
2. substring ( index1, index2 ):
截取從 index1 起,至 index2 -1 為止的字串

程式碼範例:

var str = "abcdefg";
//顯示bcd
alert ( str.substr ( 1, 3 ) );
//顯示bc
alert ( str.substring ( 1, 3 ) );