`
royzhou1985
  • 浏览: 253388 次
  • 性别: 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方法,可以很好地将业务逻辑代码与额外的横切关注点分离,使得代码更加清晰,易于维护。如果将来需要去除日志记录功能,只需要替换掉代理类的实例,而不需要修改任何业务逻辑代码。此外,...

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

    本文将深入探讨Spring AOP的实现原理,以及如何使用反射来实现动态代理模式。 首先,我们需要了解AOP的基本概念。AOP的核心思想是切面,它包含两个主要部分:切点(Pointcut)和通知(Advice)。切点定义了在程序...

    AOP动态代理(反射机制)

    6. **代理模式的其他实现**:除了Java的动态代理,还有其他实现方式,比如CGLIB库,它通过字节码生成技术创建代理对象,即使目标类没有实现接口也能进行代理。 学习AOP动态代理有助于我们理解面向切面编程的核心...

    代理模式(含动态代理讲解)【Spring AOP实质】

    在Java中,代理模式主要有两种实现方式:静态代理和动态代理。 1. 静态代理: 在静态代理中,我们需要手动创建一个代理类,这个代理类实现了与目标类相同的接口。代理类中会持有目标对象的引用,并在调用目标对象...

    AOP之JDK动态代理和CGLib动态代理

    JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring会创建一个代理对象,这个代理对象实现了与目标对象相同的接口,并在调用接口方法时插入额外的切面逻辑。代理对象在运行...

    利用C#实现AOP常见的几种方法详解

    例如,`Castle.DynamicProxy`库就是基于动态代理实现AOP的。动态代理在运行时创建代理类,可以针对接口或者抽象类,而不需要重新编译源代码。这使得代码更加灵活,但性能相比静态织入略低。 3. **特性驱动编程**: ...

    动态代理设计模式 日志和源码

    在Java中,动态代理有两种主要实现方式:一是使用Java的反射API,即`java.lang.reflect.Proxy`类;二是使用JDK动态代理。另一种常见的方式是使用CGLIB库,它是在字节码级别创建代理对象,适用于无法实现接口的目标类...

    springAOP之代理模式.docx

    代理模式是实现AOP的关键。代理模式的核心思想是创建一个代理类,它充当原对象的代理,控制对原对象的访问。在Spring AOP中,代理模式使得我们可以在不修改原始业务逻辑的情况下,插入额外的逻辑,比如事务管理、...

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 .md

    为了更好地理解代理模式的工作原理,我们通过下面的例子来实现一个简单的代理模式。 **定义接口**: ```java public interface SellPerfume { void sellPerfume(double price); } ``` **定义真实对象**: ```java...

    springAop默认代理方式.zip

    4. **代理模式的创建**:Spring AOP 使用`org.springframework.aop.framework.ProxyFactoryBean`或`@EnableAspectJAutoProxy`注解来配置代理。`ProxyFactoryBean`是XML配置方式,而`@EnableAspectJAutoProxy`是基于...

    利用Java的反射与代理实现AOP.docx

    Spring框架广泛使用AOP来实现这些功能,而Java的反射和动态代理机制是实现AOP的基础。 首先,AOP的核心思想是将横切关注点(如日志、事务管理)从核心业务逻辑中分离出来,形成独立的模块,以提高代码的可读性和可...

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

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

    AOP的实现机制.

    6. 目标对象(Target Object):被AOP代理的对象。 7. 代理(Proxy):AOP框架创建的对象,用于在调用目标对象时执行通知。 二、AOP实现方式 1. 静态代理:通过Java反射API或者自定义接口实现,缺点是需要为每个...

    AOP技术及其在J2EE中动态代理实现

    在J2EE(Java 2 Platform, Enterprise Edition)环境中,AOP的动态代理实现机制是一种常用的技术手段,它利用了Java的反射机制。Java的动态代理可以分为接口型和类型两种。接口型动态代理要求目标对象实现一个接口,...

    基于Java动态代理的AOP实现.zip

    实现方式通过手动编写代理类,实现代理模式。 特点在编译期就生成了代理类,代理类持有委托类的实例,代为执行具体类实例方法。 应用场景适用于简单的代理需求,代码直观易懂。 2. JDK动态代理 实现方式利用...

    Java 动态代理详解(代理模式+静态代理+JDK动态代理+CGLIB动态代理)

    Java 动态代理详解(代理模式+静态代理+JDK动态代理+CGLIB动态代理) Java 动态代理是 Java 编程语言中的一种强大工具,广泛应用于 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、Java 注解...

    JAVA的反射机制与动态代理

    在实际应用中,Spring AOP(面向切面编程)就是利用动态代理技术实现的。它可以让我们编写关注点分离的代码,比如日志、事务等通用功能,而无需侵入业务逻辑。此外,动态代理也被广泛应用于RPC框架,如Dubbo、gRPC,...

    java 动态代理模式 适配器模式

    在实际应用中,动态代理模式常用于AOP(面向切面编程)框架,例如Spring AOP,它可以方便地实现日志、事务控制等功能。而适配器模式则广泛应用于各种集成场景,比如将第三方库的API转换为项目统一的接口,或者在不同...

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

    当一个Bean需要依赖其他Bean时,Spring会利用反射找到依赖的类并创建实例,然后通过代理机制,如AOP代理,来增强Bean的功能。 总结起来,Java的反射和代理机制在实现IOC模式中起到关键作用。反射用于在运行时解析和...

Global site tag (gtag.js) - Google Analytics