一直都在糾結於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 ,到此結束