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

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); //[email protected]
        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文件

打开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); //[email protected]
        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
===============
提交事务
释放资源

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

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

于切入点匹配的类才会由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 --- 设计模式 --- 动态代理

时间: 2024-11-04 09:44:39

java --- 设计模式 --- 动态代理的相关文章

JAVA设计模式之代理模式

学编程吧JAVA设计模式之代理模式发布了,欢迎通过xuebiancheng8.com来访问 一.概述 给某一个对象提供一个代理,并由代理对象来完成对原对象的访问.代理模式是一种对象结构型模式. 二.适用场景 当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口. 三.UML类图 四.参与者 1.接口类:Subject 它声明了真实访问者和代理访问者的共同接口,客户端通常需要针对接口角色进行编程. 2.代理类

代理模式 &amp; Java原生动态代理技术 &amp; CGLib动态代理技术

第一部分.代理模式  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务.(其实就是在代理类中关联一个委托类的实例,然后在代理类中进行包装). UML图如下: 第二部分.在Java中实现代理模式  按照代理的创建时期,代理类可以分

设计模式--动态代理(CGLIB)

上一篇文章<设计模式--动态代理(JDK)>已经写了JDK中动态代理的应用,这篇文章将介绍动态代理在CGLIB中应用.,从上篇文章中我们知道使用JDK动态代理是有一定限制.就是被代理类必须至少实现一个接口,因为JDK为我们生成的动态代理也是实现这个接口的.所以如果没有接口,JDK基本就歇菜了.CGLIB正好弥补了JDK的这个缺陷,他的被代理类是无需实现接口,因为它的实现机制是继承被代理类,从而创建一个代理对象(继承被代理对象)的. CGLIB(Code Generation Library)是

java的动态代理

最近在研究这个java的ssh三大框架,当看到这个spring的aop(aspect-orinted-programming)的时候,其中提到了这个java的动态代理机制,这个动态代理,我以前似乎看过,但是那是设计模式的事情.所以有一次搜索到这个动态代理,对这个动态代理进行一个研究,记录自己的进步. spring的aop编程是一个面向切面的编程思想,和这个面向对象的编程是一个补充的关系,不是一个对立的关系.面向对象强调和使用的从上到下的层次关系,但是aop编程使用的是从左到右的关系.一个是纵的关

JAVA的动态代理机制

前文讲解了代理的基本概念和静态代理机制:       设计模式之代理模式 现在来谈谈JAVA的动态代理机制 在java的动态代理机制中有一个重要的接口invocationhandler和一个重要的类Proxy,让我们查看一下官方文档: InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated 

[转载] java的动态代理机制详解

转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理

有关java的动态代理和代理模式

有关java的动态代理和代理模式 一, 有关设计模式==设计模式可分为以下三类==创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式.结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式.行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式.今天说构造型的代理模式,动态代理可以说是代理模式+反射了 二,代理模式当我们要去给一段代码最增

java设计模式6——代理模式

java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代理 动态代理 1.3.代理模式关系图(以租房子为例) 2.静态代理 2.1.角色分析: 抽象角色:一般会使用接口或者抽象类来解决 真实角色:被代理的角色 代理客户:代理真实角色.代理真实角色后,我们一般会做一些附属的操作 客户:访问代理对象的人 2.2.例1(租房子演示) 2.2.1.抽象角色实现(

java的动态代理机制详解

在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于 Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是 java的动态代理机制,所以本篇随笔就是对java的动态机制进行一个回顾. 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Cla