java 中的动态代理

来源:互联网 发布:mac中word选项在哪里 编辑:程序博客网 时间:2024/06/05 14:38

一直都在纠结于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 ,到此结束


1 0
原创粉丝点击