`
royzhou1985
  • 浏览: 250042 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

代理模式:反射实现 AOP 动态代理模式

    博客分类:
  • Java
阅读更多
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到三个角色:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

代理,可以通过下面代码实现
需要一个接口、实现类、以及一个代理类

抽象角色
IHello.java

public interface IHello {
	/**
	 * 假设这是一个业务方法
	 * @param name
	 */
	public void sayHello(String name) ;
}


真实角色
Hello.java

public class Hello implements IHello {
	public void sayHello(String name) {
		System.out.println("hello," + name + "!!!");
	}
}


代理角色:
现在我们要为这个业务方法加上日志记录的业务,我们在不改变原代码的情况下,我们会去怎么做呢?也许,你会去写一个类去实现IHello接口,并依赖Hello这个类.代码如下:'
HelloProxy.java

public class HelloProxy implements IHello {
	
	private IHello hello;
	
	public HelloProxy(IHello hello) {
		this.hello = hello;
	}
	
	public void sayHello(String name) {
		Logger.logging(Level.INFO,"方法开始");
		hello.sayHello(name);
		Logger.logging(Level.INFO,"方法结束");
	}
}


其中.Logger类和Level枚举代码如下:
Logger.java

import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger {
	
	/**
	 * 根据等级记录信息
	 * @param level
	 * @param context
	 */
	public static void logging(Level level, String context) {
		if (level.equals(Level.INFO)) {
			System.out.println("start:  "
					+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
							.format(new Date()) + " " + context);
		}
		if (level.equals(Level.DEBUGE)) {
			System.err.println("end:  "
					+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
							.format(new Date()) + " " + context);
		}
	}

}


Level.java

public enum Level {
  INFO,DEBUGE;
}


下面我们写一个测试类Base.java
public class Base {
	public static void main(String[] args) {
		IHello hello = new HelloProxy(new Hello());//生成代理类
                //sayHello方法已经由代理类代理
		hello.sayHello("java");
	}
}

结果输出为:
start:  2009-03-08 14:39:48 方法开始
hello,java!!!
start:  2009-03-08 14:39:48 方法结束

从上面的代码我们可以看出,hello对象是被HelloProxy这个所谓的代理态所创建的,但是存在这样一个问题,如果方法一多的话,我们必须对每个方法都这样重写一次,使得程序不易维护


JDK为我们提供了一个API   java.lang.reflect.InvocationHandler的类. 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事.
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:

(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。


(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:

Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。

Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

下面例子中仍然使用上面代码中的抽象角色和真实角色
代理角色修改为一个实现IncocationHandler接口的类DynaProxyHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHandler implements InvocationHandler {
	/**
	 * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象,如例子中的Hello)
	 */
	private Object obj;
	
	/**
	 * 动态生成方法被处理过后的对象 (写法固定)
	 * @param obj
	 * @return
	 */
	public Object bind(Object obj) {
		this.obj = obj;
		return Proxy.newProxyInstance(this.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
	}
	
	/**
	 * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
     * 此方法是动态的,不是手动调用的
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result = null;
		Logger.logging(Level.INFO,"动态代理类方法" + method.getName() + "开始");
		//JVM通过这条语句执行原来的方法(反射机制)
		result = method.invoke(this.obj,args);
		Logger.logging(Level.INFO,"动态代理类方法" + method.getName() + "结束");
		return result;
	}

}


测试类修改为
public class Base {
	public static void main(String[] args) {
		IHello h = (IHello)new DynaProxyHandler().bind(new Hello());
		h.sayHello("java");
	}
}

结果输出为:
start:  2009-03-08 14:43:42 动态代理类方法sayHello开始
hello,java!!!
start:  2009-03-08 14:43:42 动态代理类方法sayHello结束

从上面的例子我们看出.只要你是采用面向接口编程,那么,你的任何对象的方法执行之前要加上记录日志的操作都是可以的.他(DynaPoxyHandler)自动去代理执行被代理对象(Hello)中的每一个方法,一个java.lang.reflect.InvocationHandler接口就把我们的代理对象和被代理对象解藕了.但是,我们又发现还有一个问题,这个DynaPoxyHandler对象只能跟我们去在方法前后加上日志记录的操作.我们能不能把DynaPoxyHandler对象和日志操作对象(Logger)解藕呢?
结果是肯定的.让我们来分析一下我们的需求.
我们要在被代理对象的方法前面或者后面去加上日志操作代码(或者是其它操作的代码),
那么,我们可以抽象出一个接口,这个接口里就只有两个方法,一个是在被代理对象要执行方法之前执行的方法,我们取名为start,第二个方法就是在被代理对象执行方法之后执行的方法,我们取名为end .接口定义如下 :
IOperation.java

package com.royzhou;

import java.lang.reflect.Method;

public interface IOperation {
	
	/**
	 * 方法开始前操作
	 * @param method
	 */
	public void start(Method method);
	
	/**
	 * 方法结束后操作
	 * @param method
	 */
	public void end(Method method);
}


我们去写一个实现上面接口的类.我们把作他真正的操作者,如下面是日志操作者的一个类:
LoggerOperation.java

package com.royzhou;

import java.lang.reflect.Method;

public class LoggerOperation implements IOperation {

	public void end(Method method) {
		Logger.logging(Level.INFO, method.getName() + " Method end ...");
	}

	public void start(Method method) {
		Logger.logging(Level.INFO, method.getName() + " Method start ...");
	}
}


然后我们要改一下代理对象DynaProxyHello中的代码.如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHandler implements InvocationHandler {
	
	/**
	 * 操作对象
	 */
	private Object proxy;
	
	/**
	 * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象,如例子中的Hello)
	 */
	private Object obj;
	
	/**
	 * 动态生成方法被处理过后的对象 (写法固定)
	 * @param obj
	 * @return
	 */
	public Object bind(Object obj, Object proxy) {
		this.obj = obj;
		this.proxy = proxy;
		return Proxy.newProxyInstance(this.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
	}
	
	/**
	 * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
     * 此方法是动态的,不是手动调用的
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result = null;
		//反射得到操作者的实例
		Class clazz = this.proxy.getClass();
		//反射得到操作者的Start方法
		Method start = clazz.getDeclaredMethod("start", new Class[]{Method.class});
		//反射执行start方法
		start.invoke(this.proxy, new Object[]{method});
		//Logger.logging(Level.INFO,"动态代理类方法" + method.getName() + "开始");
		//JVM通过这条语句执行原来的方法(反射机制)
		result = method.invoke(this.obj,args);
		//反射得到操作者的end方法
		Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class});
		//反射执行end方法
		end.invoke(this.proxy, new Object[]{method});
		//Logger.logging(Level.INFO,"动态代理类方法" + method.getName() + "结束");
		return result;
	}

}


修改一下测试类
public class Base {
	public static void main(String[] args) {
		IHello h = (IHello)new DynaProxyHandler().bind(new Hello(),new LoggerOperation());
		h.sayHello("java");
	}
}

结果还是一样的吧.

通过这样的修改,如果你想在每个方法之前加上日志记录,而不在方法后加上日志记录.你就把LoggerOperation类中start的实现注释掉,很轻松就解决了这个问题。
运行一下.你就会发现,每个方法之后没有记录日志了. 这样,我们就把代理者和操作者解藕了!

还有这样一个问题,如果只有部分方法需要记录日志,可以这样实现
在代理对象的public Object invoke(Object proxy, Method method, Object[] args)方法里面加上个if(),对传进来的method的名字进行判断,判断的条件存在XML里面.这样我们就可以配置文件时行解藕了.可以把操作者,被代理者,都通过配置文件进行配置 ,那么就可以写一个简单的SpringAOP框架了.
分享到:
评论

相关推荐

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    AOP的意思就是面向切面编程。本文主要是通过梳理JDK中自带的反射机制,实现 AOP动态代理模式,这也是Spring AOP 的实现原理

    反射实现aop 动态代理

    反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)

    反射实现 AOP 动态代理模式(Spring AOP 的实现 原理) - Java 例子 -

    NULL 博文链接:https://arne3166.iteye.com/blog/1046340

    使用动态代理演示Spring的AOP编程原理

    为了说明Spring的AOP原理,本人使用代理模式中的动态代理完成演示AOP编程的原理的演示。相信,如果你耐心看完整个程序(几乎一行注释一行代码),那么你对Spring这个东西就不是觉得有什么神秘了! 阅读对象:凡是喜爱...

    spring 代理模式

    反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)

    利用Java的反射与代理实现IOC模式

    利用Java的反射与代理实现IOC模式 在Java中,其反射和动态代理机制极其强大,我们可以通过其反 射机制在运行时获取信息。而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插入到真 实对象中的...

    复习反射利用反射机制和AOP代理模式

    reflection是一系列的API,用于表示或者处理当前JVM中的类,接口和对象. java.lang.reflect/java.lang.Class 在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

    Java反射和动态代理实例

    几个Java反射和动态代理的小例子。可以学习如何通过Java的反射机制实例化对象、调用对象的方法、操作对象的私有成员变量、改变...可以学习Java的动态代理模式、学习Java工厂模式以及如何将工厂模式与属性文件相结合。

    通用动态代理链-为你的应用程序添加AOP

    这篇文章并不打算对AOP作深入的讨论,而准备把重点放在动态代理的一般链化上,从而使开发者可以用以框架驱动的方法去实现一些AOP概念。如果一个项目早已使用用某些现存的AOP框架,那么开发者不用再担心实现一个定制...

    Java动态代理简单应用

     代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。  Java动态代理比代理...

    JAVA Reflection(反射机制)

     代理模式  Java动态代理  简单的Aop实现  “程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。  尽管在这样...

    13Spring知识点1

    反射:动态代理设计模式 1.jdk 依赖接口 newProxyInstance方法 2.cglb 实现MethodInterceptor Aop 四种通知之前通

    asp.net知识库

    ASP.NET运行模式:PageHandlerFactory 利用搜索引擎引用来高亮页面关键字 网站首页的自动语言切换 应用系统的多语言支持 (一) 应用系统的多语言支持 (二) 自动返回上次请求页面(小技巧) ASP.NET 2.0 控件 ASP...

    JAVA高并发高性能高可用高扩展架构视频教程

    揭开springAOP神秘面纱(动态代理) Mysql性能优化之索引优化 写实现Tomcat服务器 移动后台端框架设计 公司级框架原理解析 解密公司内部框架开发(打造属于自己的专属框架) 手写Tomca之深度解析动态资源请求原理 深度...

    大厂面试专栏,冲击大厂必备

    IOC、AOP、生命周期、动态代理、设计模式 第十一篇:Spring Boot !starter组件、JPA、定时任务、全局异常 第十二篇:Spring Cloud !Gateway、注册发现、Hystrix、Ribbon 第十三篇:MQ 消息队列 !消息丢失、重复...

    SpringBoot的定时调用的加强工具,实现定时任务动态管理,后续加入可视化管理、调度日志、集群任务统一管理.zip

    springboot Spring框架是Java平台上的一种开源应用框架,提供具有控制反转...Spring框架具有面向切面编程(AOP)框架,SpringAOP框架基于代理模式,同时运行时可配置;AOP框架主要针对模块之间的交叉关注点进行模块化。

    spring:Spring框架

    代理模式 是一种设计模式,主要解决在直接访问对象时代理的问题。通过代理对象访问目标对象,可以在目标对象实现的基础上,增加额外的功能,即扩展目标对象的功能。 分类: 静态代理:由程序员创建或工具生成代理类...

    Java 基础核心总结 +经典算法大全.rar

    代理模式 静态代理与动态代理常见的动态代理实现JDK Proxy CGLIB JDK Proxy 和 CGLIB 的对比动态代理的实际应用 Spring AOP 变量 变量汇总实例变量 实例变量的特点全局变量 静态变量 静态变量的特点类变量 局部变量

Global site tag (gtag.js) - Google Analytics