动态代理模式

来源:互联网 发布:同步器软件 编辑:程序博客网 时间:2024/06/07 15:39

java动态代理(JDK和cglib)
JAVA的动态代理
代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
动态代理的原理是,首先创建一个代理对象这个代理对象包含了委托对象即被代理对象的所有方法,因为他们实现了同一接口。但是代理对象并没有采用直接将被代理对象作为其field的聚合方式,而是聚合了一个中间对象叫InvocationHandler,这个InvocationHandler的实现类再聚合委托类。这样做的好处就是代理对象的代理业务逻辑可以灵活地扩展了。下面时代码:

这里写图片描述

    package com.proxy;/** * @description 接口 * @author TianQiSen * @date 2017年4月9日 */public interface IAccountBiz {    public abstract void save() throws  Throwable;}----------package com.proxy;/** * @description 动态代理的应用,给业务逻辑加日志 * @author TianQiSen * @date 2017年4月9日 */public class AccountBiz implements IAccountBiz {    /* (non-Javadoc)     * @see com.proxy.IAccountBiz#save()     */    @Override    public void save(){        System.out.println("存钱$10000!");    }}----------package com.proxy;import org.junit.Test;public class AccountBizTest {    AccountBiz ab = new AccountBiz();    @Test    public void testSave() throws Throwable {        /*1:静态代理        final IAccountBiz proxy = new LogProxy(ab);        proxy.save();*/        /**         * 2:动态代理         */        final InvocationHandler log = new LogInvocationHandler(new AccountBiz());        final IAccountBiz proxy = (IAccountBiz) Proxy.newProxyInstance(IAccountBiz.class, log);        proxy.save();    }}----------package com.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(final Class infce, final InvocationHandler h) throws Exception {        /** 1.生成.java文件 */        final String rt = "\r\n";        String methodStr="";        final Method[] methods = infce.getMethods();        for(final Method m:methods){            methodStr +=                    "   @Override"+ rt +                    "   public void " + m.getName() +"() throws Throwable {"+ rt +                    "       Method md = "+infce.getName()+".class.getMethod(\""+m.getName()+"\");"+ rt +                    "       h.invoke(this, md, new Object[]{});"+ rt +                    "   }";        }        final String str =                "package com.proxy;"+ rt +                "import com.proxy.InvocationHandler;"+ rt +                "import java.lang.reflect.Method;"+ rt +                "public class $Proxy1 implements " + infce.getName() + "{"+ rt +                "   private final InvocationHandler h;"+ rt +                "   public $Proxy1(InvocationHandler h){"+ rt +                "       this.h=h;"+ rt +                "   }"+ rt +                methodStr+ rt +                "}";        final String fileName = "D:/src/com/proxy/$Proxy1.java";        final File file = new File(fileName);        final FileWriter fw = new FileWriter(file);        fw.write(str);        fw.flush();        fw.close();        /**         * 2.编译生成类文件         * 注意:这个JavaCompiler在jdk/jre里面,如果使用jre的话无法编译         */        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();        final StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);        final Iterable<?> units = fileMgr.getJavaFileObjects(fileName);        final CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, (Iterable<? extends JavaFileObject>) units);        t.call();        fileMgr.close();        /**         * 3.load到内存,并且生成相应对象         * 注意:普通的classloader只能load classpath 路径里的class即bin目录下的class文件         *      对于非bin目录下的class可以用URLClassLoader         */        final URL[] urls = new URL[]{new URL("file:/D:/src/")};        final URLClassLoader ucl = new URLClassLoader(urls);        final Class<?> c = ucl.loadClass("com.proxy.$Proxy1");        final Constructor<?> cons = c.getConstructor(InvocationHandler.class);        final Object o = cons.newInstance(h);        return o;    }}----------package com.proxy;import java.lang.reflect.Method;public interface InvocationHandler {    public abstract Object invoke(Object proxy, Method method,Object[] args)            throws Throwable;}----------package com.proxy; import java.lang.reflect.Method; import org.apache.log4j.Logger; /** * @description 日志处理逻辑 * @author TianQiSen * @date 2017年4月13日 */public class LogInvocationHandler implements InvocationHandler {    Object target;     public LogInvocationHandler(final Object target) {        this.target = target;    }     /* (non-Javadoc)     * @see com.proxy.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])     */    @Override    public Object invoke(final Object proxy, final Method method, final Object[] args)            throws Throwable {        final Logger log = Logger.getRootLogger();        log.warn("我要存10000美元!");        method.invoke(target, args);        log.warn("恭喜你存钱成功!");        return null;    } }----------                #1.配置根Logger#log4j.rootLogger = [ level ] , appenderName1, appenderName2, …log4j.rootLogger=warn, File                #2.配置日志信息输出目的地Appender#og4j提供的appender(类名是不能随意更改的)有以下几种:#org.apache.log4j.ConsoleAppender(控制台),  #org.apache.log4j.FileAppender(文件),  #org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),  #org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),  #org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)log4j.appender.File=org.apache.log4j.FileAppenderlog4j.appender.File.File=D:/log4j.txtlog4j.appender.File.Append=true                #3.配置日志信息的格式(布局)#Log4j提供的layout有以e几种:#org.apache.log4j.HTMLLayout(以HTML表格形式布局),  #org.apache.log4j.PatternLayout(可以灵活地指定布局模式),  #org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),  #org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)log4j.appender.File.layout=org.apache.log4j.PatternLayout#Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:#%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL  #%r 输出自应用启动到输出该log信息耗费的毫秒数  #%c 输出所属的类目,通常就是所在类的全名  #%t 输出产生该日志事件的线程名  #%m 输出代码中指定的消息#%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”  #%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:20021018221028921  #%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)log4j.appender.File.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n# console is set to be a FileAppender and console uses PatternLayout.log4j.appender.console=org.apache.log4j.ConsoleAppender  log4j.appender.console.threshold=INFO  log4j.appender.console.layout=org.apache.log4j.PatternLayout  log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n