java 中的動態代理

一直都在糾結於java 中的動態代理,說實話自己也是不太明白,前幾天專門研究了這個,網上有一篇不支持轉載的文章,關於java 的動態代理寫的不錯,特別進行記錄一番

還是由場景引入吧,例如某個第三方的jar包中的某個類中的某個方法的前後加上自己的邏輯,比如日誌輸出,你該怎麼辦,因爲此時提供 的是第三方的class文件,你不可能去修改別人的代碼

    冥思苦想,要麼是繼承,要麼是聚合,這兩種方法  假設第三方提供一個Run接口,裏面有一個run方法以及它的實現類Person

   public Interface Run(){

   public  void run();


public  class Person implements Run(){

@Override

  public void run(){

system.out.println("Person running........");

}


}

第一種,利用繼承實現


public class  xiaoming extends Person(){

@Override

public void run(){

LOG.info("xiaoming running before run");

super.run();

LOG.INFO("xiaoming running after run");


}



}


第二種  利用聚合實現

public class xiaohei implements Run(){

private Run person;

public xiaohei(Run person){

this.person=person;

}

@Override

public void run(){

LOG.info("xiaohei  running before run");

super.run();

LOG.INFO("xiaohei  running after run");

}

}


綜合比較兩種實現方式,那種更好呢?

顯然聚合比繼承更好,更靈活,沒有那麼多的複雜的父子關係,對於後期的維護都是很方便

其實xiaoming 和xiaohei 都是person的代理類,對person 中的方法進行加強,只不過是靜態代理,很受限制,只能代理Run類型的類,其他類型則沒辦法

java中爲此引入了動態代理

動態代理的字面意思:就是一個類的代理類是動態生成的,這個類不是提前寫好的,是程序運行時生出來的,並且能夠代理實現了某個接口的任何類型的類

具體關係圖如下所示:|




根據上圖,我們大概知道動態代理有這樣一個過程:

1 根據輸入的接口,通過反射機制,肯定可以獲取該接口對應的方法

2 根據輸入的被代理類,通過反射機制,可以獲取其實現的方法

可是,在哪個地方加入某個方法前後自己加入的邏輯呢,這個在哪裏實現呢?

於是對該圖進行改造




我們將被代理類不是直接給了黑箱子,而是通過一個Handler來實現的,至此我們來一一對應一下java中的動態代理:

黑箱子:java中的Proxy類

handler:就是java中的invocationHandler的子類

利用Proxy的newProxyInstance的靜態方法,就可以動態生成一個代理類
* 這個方法有三個參數
* 1 類加載器,將動態生成的代理類的字節碼文件加載到jvm虛擬機中
* 2 被代理類實現的接口的class類
* 3 invocationHandler的子類,在這個類中的invoke方法中前後加入加強的邏輯


我自己寫了一個實例:

該實例模擬spring的AOP機制,即我們只要在配置文件裏打開了事務機制,
 * 那麼調用方法就會開啓事務,同樣我們在配置文件裏關閉了事務機制,那麼調用方法就不會開啓事務


AOP java 

package proxy;


public interface AOP {


public void show(String str);
public String say(String str);






}

AOP java 實現

package proxy;


public class AOPImpl implements AOP{


@Override
public void show(String str) {
System.out.println("show"+str);

}


@Override
public String say(String str) {

return "say"+str;
}


}


AOP handler:


package proxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;


public class AOPHandler implements InvocationHandler {

private Object object;
private boolean flag;


public AOPHandler(Object object){

this.object=object;

}
public void setFlag(Map<String, String> config){
if(config==null){
flag=false;
}else{
if(config.containsKey("transaction")&& "true".equalsIgnoreCase(config.get("transaction"))){

flag=true;

}else{

flag=false;
}





}




}





@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

if(flag){
doBefore();

}

Object result=method.invoke(object, args);

if(flag){

doAfter();

}

return result;
}



public void doBefore(){

System.out.println("Transcation start...");
}

public void doAfter(){

System.out.println("Transcation commit....");

}


}


Client   java 


package proxy;


import java.lang.reflect.Proxy;


/*
 * 該實例模擬spring的AOP機制,即我們只要在配置文件裏打開了事務機制,
 * 那麼調用方法就會開啓事務,同樣我們在配置文件裏關閉了事務機制,那麼調用方法就不會開啓事務
 */


public class Client {

public static void main(String[] args) throws Exception{

AOPImpl impl=new AOPImpl();
AOPHandler handler=new AOPHandler(impl);
handler.setFlag(JVMCache.getConfig());
System.out.println(JVMCache.getConfig()+"提示");

/*
* 利用Proxy的newProxyInstance的靜態方法,就可以動態生成一個代理類
* 這個方法有三個參數
* 1 類加載器,將動態生成的代理類的字節碼文件加載到jvm虛擬機中
* 2 被代理類實現的接口的class類
* 3 invocationHandler的子類,在這個類中的invoke方法中前後加入加強的邏輯

*/

AOP aop=(AOP)Proxy.newProxyInstance(AOPImpl.class.getClassLoader(),
new Class<?>[] {AOP.class}, handler);
aop.show("我擦擦");
String result=aop.say("杭州G20峯會");
System.out.println(result);



}






}


JVM Cache java 


package proxy;


import java.util.Map;


public class JVMCache {

private static Map<String, String> config;
public synchronized static Map<String, String> getConfig() throws Exception{

if(null==config){
 config=XMLUtil.parseXml();
}

return config;
}


}

XMLUtil java

package proxy;


import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;




import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class XMLUtil {

public static Map<String, String> parseXml ()throws Exception{

Map<String, String> result=new HashMap<String, String>();
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
DocumentBuilder db=dbf.newDocumentBuilder();


InputStream in=XMLUtil.class.getClassLoader().getResourceAsStream("config.xml");

Document document=db.parse(in);
Element root=document.getDocumentElement();
NodeList xmlNodes=root.getChildNodes();
for(int i=0;i<=xmlNodes.getLength();i++){

Node  config=xmlNodes.item(i);
if(null!=config &&config.getNodeType()==Node.ELEMENT_NODE){
String nodeName=config.getNodeName();
System.out.println(nodeName+"輸出節點名稱爲:");
if("transaction".equals(nodeName)){
String textContent=config.getTextContent();
result.put("transaction", textContent);
System.out.println("你好啊");

}

}

}

return result;

}






}


config.xml   java 

<?xml version="1.0" encoding="UTF-8"?>
<config>
<transaction>true</transaction>
</config>


運行時結果爲:


transaction輸出節點名稱爲:
你好啊
{transaction=true}提示
Transcation start...
show我擦擦
Transcation commit....
Transcation start...
Transcation commit....
say杭州G20峯會


OK  ,將配置文件裏面的值改爲false

運行


事務自動關閉,ok ,到此結束


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章