码迷,mamicode.com
首页 > 编程语言 > 详细

java --- 设计模式 --- 动态代理

时间:2014-08-11 08:23:21      阅读:361      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   java   使用   os   

Java设计模式——动态代理

 

java提供了动态代理的对象,本文主要探究它的实现,

动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式,

动态代理使代码的重复更少,更便与维护

本文参考了满一行老师和马士兵老师的视频,在此表示Thanks。

 

假设小张通过QQ和一个网名为如花的妹纸聊天,

而在QQ的另一端,这个网名为如花的妹纸的name是小丽

但是,正在聊天的时候小丽生病了不想打字,小丽就找她的男朋友帮忙回应

小丽的男朋友在如花的QQ账号上与小张聊天,小张并不知道和他聊天的如花是谁

小张发来消息了,"HI, 你好,",小丽的男朋友说,“小张发来消息了‘HI, 你好’,我该怎么回?”

小丽说,那就回一个“你也好, 哈哈”吧, 小张的男朋友按照小丽说的做了。

这就是代理模式的雏形,小丽实现了如花这个接口,小张也实现了如花这个接口

小张调用的是如花这个接口,但是他并不知道,和他聊天的是小丽,还是小丽的男朋友

小丽的男朋友因为在信息的必经道上,所以可以对信息进行扭曲以及篡改,对不良的信息过滤

哈哈,这就是面向切面编程的一个例子了。

 

故事就讲到这里,下面来解释一下静态代理

Tank类实现一个Movable接口,怎样知道它的运行时间呢方法move()的运行时间呢

我们可以在方法前面和后面都记录时间, long start = System.currentTimeMillis();

 

 /Proxy/src/yuki/design/proxy/package1/Movable.java

package yuki.design.proxy.package1;

public interface Movable {

    void move();
    
}

 

 /Proxy/src/yuki/design/proxy/package1/Tank.java

package yuki.design.proxy.package1;

import java.util.Random;

public class Tank implements Movable {

    @Override
    public void move() {
//      long start = System.currentTimeMillis();
        
        System.out.println("Tank is moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
//      long end = System.currentTimeMillis();
//      System.out.println("time:" + (start-end));
    }

}

 

 

通过注释掉的内容可以计算出夹在中间代码的运行时间

这种方法需要修改源码,但是很多方法都是不知道源码的class文件

这个方式拿到的也不是方法本身运行的时间,因为方法已经被篡改了

还有一种方法是要在main方法中把方法执行的前后加上时间的记录

这是不行的,因为jdk为方法的运行准备也需要时间,这显然是不精确的

 

哦,我们可以继承Tank这个类,重写它的方法就可以了

用继承把原来的方法前后加一些逻辑,原来的方法就用super调用

/Proxy/src/yuki/design/proxy/package1/Tank2.java

package yuki.design.proxy.package1;

public class Tank2 extends Tank {

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        
        super.move();
        
        long end = System.currentTimeMillis();
        System.out.println("time:" + (start-end));
    }

}

 

 

一个类里面有另一个类的对象,这种对象间的关系叫做聚合,

它也可以实现上面的继承所提到的效果,只是把父类改成代理类的一个成员变量而已

Tank3是用聚合的方式实现的

/Proxy/src/yuki/design/proxy/package1/Tank3.java

package yuki.design.proxy.package1;

public class Tank3 implements Movable {

    public Tank3(Tank t) {
        this.t = t;
    }
    
    Tank t;

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        
        t.move();
        
        long end = System.currentTimeMillis();
        System.out.println("time:" + (start-end));
    }
    
}

 

 

继承明显是不灵活的,也不符合日常的逻辑

一般来说,如果可能这个类有多个代理类呢,比如说,添加一个记录日志的类

功能上的叠加,先记录日志,后记录时间;或者是相反的顺序

如果用继承的方式,会导致类越来越多

如果用聚合的方式,可以代理Movable接口,这里的关键是实现同一接口

/Proxy/src/yuki/design/proxy/package1/TankTimeProxy.java

package yuki.design.proxy.package1;

public class TankTimeProxy implements Movable {

    public TankTimeProxy(/*Tank*/Movable t) {
        this.t = t;
    }
    
    /*Tank*/Movable t;

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        
        t.move();
        
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

}

 

/Proxy/src/yuki/design/proxy/package1/TankLogProxy.java

package yuki.design.proxy.package1;

public class TankLogProxy implements Movable{

    public TankLogProxy(/*Tank*/ Movable t) {
        this.t = t;
    }
    
    /*Tank*/Movable t;
    
    @Override
    public void move() {
        System.out.println("Tank start......");
        
        t.move();
        
        System.out.println("Tank stop......");
    }

}

 

 

注意,这里代理类的对象并不是真实的对象,而是接口,通过这个接口

这两个代理类和被代理的对象就可以自由组合

严格的说,在被代理对象和最终的代理对象之间可以随机插入代理类

每一个代理类就可以看作是一个切片

/Proxy/src/yuki/design/proxy/package1/Client.java

 

package yuki.design.proxy.package1;

public class Client {

    public static void main(String[] args) {
        Tank t = new Tank();
        
        TankTimeProxy timeProxy = new TankTimeProxy(t);
        TankLogProxy logProxy = new TankLogProxy(timeProxy);
        Movable m = logProxy;
        m.move();
        /*
        Movable logProxy = new TankLogProxy(t);
        Movable timeLogProxy = new TankTimeProxy(logProxy);
        timeLogProxy.move();
        */
    }
    
}

 

 

 

 

spring这个轻量级容器,提供了继承和聚合实现代理的方式,

但是,强烈建议我们使用聚合的哪一种

可以说,AOP是动态代理的一个应用

 

还要说明的是,当一个接口有多个方法时,相同的代码必须重复

就是封装成对象也至少要插入一条插入调用方法的语句

/Proxy/src/yuki/design/proxy/package2/Movable.java

 

package yuki.design.proxy.package2;

public interface Movable {

    void move();
    void stop();
    
}

 

 

 

/Proxy/src/yuki/design/proxy/package2/Tank.java

 

package yuki.design.proxy.package2;

import java.util.Random;

public class Tank implements Movable {

    @Override
    public void move() {
        System.out.println("Tank is moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stop() {
        System.out.println("Tank is stopping...");
    }

}

 

 

/Proxy/src/yuki/design/proxy/package2/TankTimeProxy.java

package yuki.design.proxy.package2;

public class TankTimeProxy implements Movable {

    public TankTimeProxy(Movable t) {
        this.t = t;
    }
    
    Movable t;

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        
        t.move();
        
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

    @Override
    public void stop() {
        long start = System.currentTimeMillis();
        
        t.stop();
        
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }
    
    

}

 

如果Movable接口里还有其它的方法,计算时间的代码在代理类中还得重写
可以把相同的代码放在方法中,before,after
怎样让计算时间的代理变得更加通用,
不只是计算坦克的时间,还可以计算汽车的
怎样写一个通用的时间代理

 

 

我们应该怎样生成一个代理呢

动态代理的意思是,不再看到代理类的名字,需要的是代理对象直接产生

加入能把源码编译完产生新的类,再把这个类放入内存,产生新的对象
怎样对这段代码动态的编译,动态编译完成就能产生动态代理了
现在看不到这个动态代理类的名字
这里可以自定义proxy的实现,而具体的类就不用写了

/Proxy/src/yuki/design/proxy/package3/Movable.java

/Proxy/src/yuki/design/proxy/package3/Tank.java

 

package yuki.design.proxy.package3;

import java.util.Random;

public class Tank implements Movable {

    @Override
    public void move() {
//        long start = System.currentTimeMillis();
        
        System.out.println("Tank is moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
//        long end = System.currentTimeMillis();
//        System.out.println("time:" + (start-end));
    }

}

 

 

 

package yuki.design.proxy.package3;

import java.util.Random;

public class Tank implements Movable {

    @Override
    public void move() {
//        long start = System.currentTimeMillis();
        
        System.out.println("Tank is moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
//        long end = System.currentTimeMillis();
//        System.out.println("time:" + (start-end));
    }

}

 

 

在JDK6里,提供了编译java代码的API
获取项目的根路径System.getProperty("user.dir");
通过FileWriter的write方法把字符串写到文件中

 

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler是空值,把/Java/Installed JREs由jre改为jdk
这里的jre还是从jdk下的jre下拿的
/JavaBuildPath/Libraries选择Workspace default JRE(jdk1.8.0)

 

在Eclipse中,必须先编译好,有了文件后才能加载类
使用反射加载对象,运行方法

 

 

/Proxy/src/yuki/design/proxy/Proxy.java

package yuki.design.proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {

    public static Object newProxyInstance(Class<?> interfaces, InvocationHandler h) throws Exception {
        
        String rt = "\r\n";
        String methodStr = "";
        
        Method[] methods = interfaces.getMethods();
        /*for(Method m : methods){
            methodStr += 
                    "    @Override" + rt +
                    "    public void "+ m.getName() +"(){" + rt +
                    "        long start = System.currentTimeMillis();"+rt+
                    "        System.out.println(\"starttime:\" + start);"+rt+
                    "        t."+ m.getName() +"();"+rt+
                    "        long end = System.currentTimeMillis();"+rt+
                    "        System.out.println(\"time:\" + (end-start));"+rt+
                    "    }";
        }*/
        for(Method m : methods){
            methodStr += 
                    "    @Override" + rt +
                    "    public void "+ m.getName() +"() {" + rt +
                    "        try{" + rt +
                    "            Method md = "+ interfaces.getName() +".class.getMethod(\""+ m.getName() +"\");" + rt +
                    "            h.invoke(this, md);"+rt+
                    "        }catch(Exception e){ e.printStackTrace(); }" + rt +
                    "    }";
        }
        
        
        /*String src = 
                "package yuki.design.proxy.package3;"+rt+
                "public class TankTimeProxy implements "+ interfaces.getName() +" {"+rt+
                "    public TankTimeProxy("+ interfaces.getName() +" t) {"+rt+
                "        this.t = t;"+rt+
                "    }"+rt+
                "    "+ interfaces.getName() +" t;"+rt+
                    methodStr +rt+
                "}";*/
        String src = 
                "package yuki.design.proxy.package3;"+rt+
                "import yuki.design.proxy.InvocationHandler;"+rt+
                "import java.lang.reflect.Method;"+rt+
                "public class TankTimeProxy implements "+ interfaces.getName() +" {"+rt+
                "    public TankTimeProxy(InvocationHandler h) {"+rt+
                "        this.h = h;"+rt+
                "    }"+rt+
                "    InvocationHandler h;"+rt+
                methodStr +rt+
                "}";
        
        String fileName = System.getProperty("user.dir") + 
                                        "/temp/yuki/design/proxy/package3/TankTimeProxy.java";
        File f = new File(fileName);
        if(!f.exists()){ f.mkdirs(); f.delete();}
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        
        /*
         * compile
         */
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // System.out.println(compiler); //com.sun.tools.javac.api.JavacTool@a570f
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> units =  fileManager.getJavaFileObjects(fileName);
        CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
        task.call();
        fileManager.close();
        
        /*
         * load into memory and create an instance
         */
        URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/temp/")};
        //System.out.println(urls[0]); //file:/D:/Workspaces/Eclipse/Proxy/bin
        URLClassLoader ul = new URLClassLoader(urls);
        Class<?> c = ul.loadClass("yuki.design.proxy.package3.TankTimeProxy");
        //System.out.println(c); //class yuki.design.proxy.package3.TankTimeProxy
        ul.close();
        
        Constructor<?> constructor = c.getConstructor(InvocationHandler.class);
        Object m = constructor.newInstance(h);
        
        return m;
    }
    
}

 

 

现在的代理只能实现Movable接口,现在要求可以实现任意接口的
传入Class<?>类型的参数就可以了
站在ClassLoader的角度Method也是一系列的对象
假设有很多方法就遍历所有的方法
往里传入任意接口,就可以产生实现了这个接口的任意对象

 

更换为其它的目录后,一次运行就可以成功
原先在src目录下,会有两份class文件,
一份是eclipse编译的,一份是ClassLoader加载的

/Proxy/src/yuki/design/proxy/InvocationHandler.java

package yuki.design.proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {

    void invoke(Object o, Method m);
    
}

 

 

这里,实现了InvocationHandler的invoke(Obejct, Method)方法

/Proxy/src/yuki/design/proxy/TimeHandler.java

package yuki.design.proxy;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {

    private Object target;
    
    public TimeHandler(Object target) {
        this.target = target;
    }


    @Override
    public void invoke(Object o, Method m) {
        long start = System.currentTimeMillis();
        System.out.println("starttime:" + start);
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

}

 

 

我们来看一看它是如何被调用的吧

/Proxy/src/yuki/design/proxy/Client.java

package yuki.design.proxy;

import yuki.design.proxy.package3.Movable;
import yuki.design.proxy.package3.Tank;

public class Client {

    public static void main(String[] args) throws Exception {
        Tank t = new Tank();
        InvocationHandler h = new TimeHandler(t);
        Movable m = (Movable) Proxy.newProxyInstance(Movable.class, h);
//        Movable m = (Movable) Proxy.newProxyInstance(Comparable.class);
        m.move();
        
    }
}

 

 

看一看生成的代理java文件和class文件

bubuko.com,布布扣

打开TankTimeProxy.java

package yuki.design.proxy.package3;
import yuki.design.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class TankTimeProxy implements yuki.design.proxy.package3.Movable {
    public TankTimeProxy(InvocationHandler h) {
        this.h = h;
    }
    InvocationHandler h;
    @Override
    public void move() {
        try{
            Method md = yuki.design.proxy.package3.Movable.class.getMethod("move");
            h.invoke(this, md);
        }catch(Exception e){ e.printStackTrace(); }
    }
}

 

它被翻译成了class文件,加载进内存,生成对象

 

if(!f.exists()){ f.mkdirs(); f.delete();}

 

这一句的意思是,如果文件不存在就建立这些文件,但是在文件的树梢

f.mkdirs()之后,有一个TimeTankProxy.java的文件夹,

导致在该目录下不能新建TimeTankProxy.java的文件,所以要f.delete();

 

运行看一下结果吧,控制台打印输出如下语句

starttime:1407701374031
Tank is moving...
time:5934

 

补充一些小细节,

 

/Proxy/src/yuki/design/proxy/complier/Test1.java

 

package yuki.design.proxy.complier;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import yuki.design.proxy.package3.Movable;
import yuki.design.proxy.package3.Tank;

public class Test1 {

    public static void main(String[] args) throws Exception {
        String rt = "\r\n";
        String src = 
                "package yuki.design.proxy.package3;"+rt+
                "public class TankTimeProxy implements Movable {"+rt+
                "    public TankTimeProxy(Movable t) {"+rt+
                "        this.t = t;"+rt+
                "    }"+rt+
                "    Movable t;"+rt+
                "    @Override"+rt+
                "    public void move() {"+rt+
                "        long start = System.currentTimeMillis();"+rt+
                "        System.out.println(\"starttime:\" + start);"+rt+
                "        t.move();"+rt+
                "        long end = System.currentTimeMillis();"+rt+
                "        System.out.println(\"time:\" + (end-start));"+rt+
                "    }"+rt+
                "}";
        
        String fileName = System.getProperty("user.dir") + 
                                        "/src/yuki/design/proxy/package3/TankTimeProxy.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        
        /*
         * compile
         */
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // System.out.println(compiler); //com.sun.tools.javac.api.JavacTool@a570f
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> units =  fileManager.getJavaFileObjects(fileName);
        CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
        task.call();
        fileManager.close();
        
        /*
         * load into memory and create an instance
         */
        URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/src")};
        //System.out.println(urls[0]); //file:/D:/Workspaces/Eclipse/Proxy/bin
        URLClassLoader ul = new URLClassLoader(urls);
        Class<?> c = ul.loadClass("yuki.design.proxy.package3.TankTimeProxy");
        //System.out.println(c); //class yuki.design.proxy.package3.TankTimeProxy
        ul.close();
        
        Constructor<?> constructor = c.getConstructor(Movable.class);
        Movable m = (Movable) constructor.newInstance(new Tank());
        m.move();
        
    }

}

 

 

 

/Proxy/src/yuki/design/proxy/complier/Test2.java

package yuki.design.proxy.complier;

import java.lang.reflect.Method;

import yuki.design.proxy.package3.Movable;

public class Test2 {

    public static void main(String[] args) {
        Method[] methods = Movable.class.getMethods();
        for(Method m : methods){
            System.out.println(m.getName());
        }
    }
    
}

 

 

现在,我们一起来连贯一下代理的主要细节:

一个代理类TimeHandler实现一个接口InvocationHandler
接口的方法是invoke(Object, Method)
这个类需要传入一个被代理对象Tank,
并提供一个它的每个方法需要添加的代码,在invoke中调用Tank的方法
它会被Proxy的生成器吸收,遍历这个类的所有方法
最后会在一个临时的目录生成代码并编译,在生成的类中,
它吸收了InvocationHandler,
并通过invoke的方法参数调用被代理类的每个方法
生成Class文件,加载进内存,获得了这个代理类的对象
这时,调用这个代理类的每个方法都会执行在TimeHandler中被填入的内容

 

使用代理类实现不修改原来的代码,在代码前后添加逻辑
面向切面编程,这个逻辑是可插拔的,可以把逻辑卸载配置文件中
配置是可以叠加的,

 

既然动态代理的类已写好,我们让它来实现一些其它的代理逻辑

/Proxy/src/yuki/design/proxy/test/UserMgr.java

package yuki.design.proxy.test;

public interface UserMgr {

    void addUser();
    
}

 

/Proxy/src/yuki/design/proxy/test/UserMgrImpl.java

package yuki.design.proxy.test;

public class UserMgrImpl implements UserMgr {

    @Override
    public void addUser() {
        System.out.println("1.插入记录到user表");
        System.out.println("2.记录日志到日志表");
    }
    
}

 

/Proxy/src/yuki/design/proxy/test/TransactionHandler.java

package yuki.design.proxy.test;

import java.lang.reflect.Method;

import yuki.design.proxy.InvocationHandler;

public class TransactionHandler implements InvocationHandler {

    private Object target;
    public TransactionHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public void invoke(Object o, Method m) {
        System.out.println("Transaction start");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Transaction commit");
    }

}

 

/Proxy/src/yuki/design/proxy/test/Client.java

package yuki.design.proxy.test;

import java.lang.reflect.Method;

import yuki.design.proxy.InvocationHandler;

public class TransactionHandler implements InvocationHandler {

    private Object target;
    public TransactionHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public void invoke(Object o, Method m) {
        System.out.println("Transaction start");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Transaction commit");
    }

}

 

运行一下,就会看到结果,编译的文件以及控制台输出的结果

package yuki.design.proxy.package3;
import yuki.design.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class TankTimeProxy implements yuki.design.proxy.test.UserMgr {
    public TankTimeProxy(InvocationHandler h) {
        this.h = h;
    }
    InvocationHandler h;
    @Override
    public void addUser() {
        try{
            Method md = yuki.design.proxy.test.UserMgr.class.getMethod("addUser");
            h.invoke(this, md);
        }catch(Exception e){ e.printStackTrace(); }
    }
}

 

starttime:1407702024556
Transaction start
1.插入记录到user表
2.记录日志到日志表
Transaction commit
time:0

 

 

在最后用到了两个代理类的嵌套,它也可以用配置文件变成灵活的,即配置是可叠加的

jdk的动态代理有Proxy类的
newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
接口InvocationHandler有方法
invoke(Object, Method, Object[])

 

简单的小思考:

什么叫动态代理?
动态代理是怎么产生的?
动态代理有什么用?

 

 

怎样使用JavaAPI生成动态代理呢

/Proxy/src/yuki/design/proxy/java/EmpService.java

 

package yuki.design.proxy.java;

public interface EmpService {
    
    void save();
    
    void update();
    
    void delete(int id);
}

 

 

 

/Proxy/src/yuki/design/proxy/java/EmpServiceImpl.java

 

package yuki.design.proxy.java;

public class EmpServiceImpl implements EmpService {

    public void save() {
        System.out.println("EmpService save");
    }

    @Override
    public void update() {
        System.out.println("EmpService update");
    }

    @Override
    public void delete(int id) {
        System.out.println("EmpService delete, id="+ id);
    }

}

 

 

 

/Proxy/src/yuki/design/proxy/java/EmpServiceProxy.java

 

package yuki.design.proxy.java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class EmpServiceProxy {
    
    public static void main(String[] args) {
        //被代理的目标对象
        final EmpService target = new EmpServiceImpl();
        
        /**
         * 用来产生代理的类
         * 第一个参数:classLoader, 用来加载*.class文件,类加载器
         *                                 需要拿到当前线程的类加载器
         * 第二个参数:代理类应当实现的接口,可以实现多个接口
         * 第三个参数:一个接口
         */
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] interfaces = new Class[]{EmpService.class};
        InvocationHandler h = new InvocationHandler() {
            /**
             * Object proxy 代理对象自己
             * Method method 代理对象正在被调用的那个方法,是在接口里定义的
             * Object[] args 方法的参数数组
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println("方法名:" + method.getName());
                try {
                    System.out.println("开始事物");
                    // 调用真正业务逻辑
                    System.out.println("===============");
                    method.invoke(target, args);
                    System.out.println("===============");
                    System.out.println("提交事务");
                } catch (Exception e) {
                    System.out.println("回滚事务");
                } finally {
                    System.out.println("释放资源");
                }
                return null;
            }
        };
        EmpService service = (EmpService) Proxy.newProxyInstance(loader, interfaces, h);
//        service.save();
//        service.update();
        service.delete(111);
    }
}

 

 

 

运行,会在控制台打印出如下语句

 

方法名:delete
开始事物
===============
EmpService delete, id=111
===============
提交事务
释放资源

 

整个演示项目的包路径如下:

bubuko.com,布布扣

 

 

对于面向切面变成来说,有切入点和切面两个术语

于切入点匹配的类才会由spring生成动态代理,在spring中,我们需要写切入点表达式

切面是对应的附加操作,可以把这部分操作写在通知类中

<bean class="通知类"></bean>

<aop:config>

  <aop:pointcut id="切入点id" expression="切入点表达式"></aop:pointcut>

  <aop:aspect ref="通知类id">

    <aop:通知类型 method="通知类中的方法" pointcut-ref="切入点id"></<aop:通知类型>

  </aop:aspect>

</aop:config>

Object 代理对象 = context.getBean("目标对象id");

将主业务逻辑的附加操作抽取出去,根据功能不同,抽取为不同通知类的思想,称为面向切面编程

 

 

更多好文请查看:http://www.cnblogs.com/kodoyang/

 

孔东阳

2014/8/11

 

java --- 设计模式 --- 动态代理,布布扣,bubuko.com

java --- 设计模式 --- 动态代理

标签:des   style   blog   http   color   java   使用   os   

原文地址:http://www.cnblogs.com/kodoyang/p/DesignPattern_DynamicProxy.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!