`
Rooock
  • 浏览: 36807 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

代理和AOP

阅读更多
一.起源
有时,我们在写一些功能方法的时候,需要加上特定的功能.比如说在方法调用的前后加上日志的操作,或者是事务的开启与关闭.对于一个方法来说,很简单,只要在需要的地方增加一些代码就OK.但是如果有很多方法都需要增加这种特定的操作呢?

没错,将这些特定的代码抽象出来,并且提供一个接口供调用者使用:
	public class RecordLog
	{
		public static void recordLog()
		{
			//	记录日志的操作
			System.out.println("记录日志...");
		}
	}

那么在其他的方法中,就可以使用RecordLog.recordLog()方法了.但你会发现,这仍不是个好的设计,因为在我们的代码里到处充塞着
RecordLog.recordLog()这样的语句:
 public class A
	{
		public void a()
		{
			//	1.记录日志
			RecordLog.recordLog();

			//	2.类A的方法a的操作
		}
	}
	public class B
	{
		public void b()
		{
			//	1.记录日志
			RecordLog.recordLog();

			//	2.类B的方法b的操作
		}
	}
	......

这样虽然会在一定程度减轻代码量,但你会发现,仍有大量的地方有重复的代码出现!这绝对不是优雅的写法!

为了避免这种吃力不讨好的现象发生,“代理”粉墨登场了.

二.传统的代理.静态的代理.面向接口编程
同样为了实现以上的功能,我们在设计的时候做了个小小的改动.

2.1 抽象出来的记录日志的类:
	public class RecordLog
	{
		public static void recordLog()
		{
			//	记录日志的操作
			System.out.println("记录日志...");
		}
	}

2.2 设计了一个接口:
public interface PeopleInfo
{
	public void getInfo();
}

该接口只提供了待实现的方法.

2.3 实现该接口的类:
	public class PeopleInfoImpl implements PeopleInfo
	{	
		private String name;

		private int age;

		//	构造函数
		public PeopleInfoImpl(String name, int age)
		{
			this.name = name;
			this.age  = age;
		}

		public void getInfo()
		{
			//	方法的具体实现
			System.out.println("我是" + name + ",今年" + age + "岁了.");
		}
	}

这个类仅仅是实现了PeopleInfo接口而已.平平实实.好了.关键的地方来了.就在下面!

2.4 创建一个代理类:
public class PeopleInfoProxy implements PeopleInfo
{
	//	接口的引用
	private PeopleInfo peopleInfo;

	//	构造函数	.针对接口编程,而非针对具体类
	public RecordLogProxy(PeopleInfo peopleInfo)
	{
		this.peopleInfo = peopleInfo;
	}

	//	实现接口中的方法
	public void record()
	{
		//	1.记录日志
		RecordLog.recordLog();

		//	2.方法的具体实现
		peopleInfo.getInfo();
	}
}

这个是类是一个代理类,它同样实现了PeopleInfo接口.比较特殊的地方在于这个类中有一个接口的引用private PeopleInfo peopleInfo;通过
这个引用,可以调用实现了该接口的类的实例的方法!
而不管是谁,只要实现了PeopleInfo这个接口,都可以被这个引用所引用.也就是说,这个代理类可以代理任何实现了接口的PeopleInfo的类.具体
如何实现,请看下面:

2.5 Main
	public class Main
	{
		public static void main(String[] args)
		{
			//	new了一个对象
			PeopleInfoImpl peopleInfoImpl = new PeopleInfoImpl("Rock",24);

			//	代理该对象
			PeopleInfoProxy peopleInfoProxy = new PeopleInfoProxy(PeopleInfoImpl);

			//	调用代理类的方法.输入的是目标类(即被代理类的方法的实现)
			peopleInfoProxy.getInfo();
		}
	}

这样,输出的结果将是:
记录日志...
我是Rock,今年24岁了.

由这个例子可见,这么做了之后不但省略了很多代码,而且不必要知道具体是由哪个类来执行方法.只需实现了特定的接口,代理类就可以打点一切
了.这就是面向接口的威力!HOHO...

三.动态代理.Java的动态机制.
面向接口的编程确实让我们省了不少心,只要实现一个特定的接口,就可以处理很多的相关的类了.
不过,这总是要实现一个“特定”的接口,如果有很多很多这样的接口需要被实现...也是件比较麻烦的事情.

好在,JDK1.3起,就有了动态代理机制,主要有以下两个类和一个接口:
java.lang.reflect.Proxy
java.lang.reflect.Method
java.lang.reflect.InvocationHandler

所谓动态代理,就是JVM在内存中动态的构造代理类.说的真是玄,还是看看代码吧.

3.1 抽象出来的记录日志的类:
	public class RecordLog
	{
		public static void recordLog()
		{
			//	记录日志的操作
			System.out.println("记录日志...");
		}
	}

3.2 设计了一个接口:
	public interface PeopleInfo
	{
		public void getInfo();
	}

该接口只提供了待实现的方法.

3.3 实现该接口的类:
	public class PeopleInfoImpl implements PeopleInfo
	{	
		private String name;

		private int age;

		//	构造函数
		public PeopleInfoImpl(String name, int age)
		{
			this.name = name;
			this.age  = age;
		}

		public void getInfo()
		{
			//	方法的具体实现
			System.out.println("我是" + name + ",今年" + age + "岁了.");
		}
	}

一直到这里,都和第二节没区别,好嘛,下面就是关键哟.

3.4 创建一个代理类,实现了接口InvocationHandler:
	public class PeopleInfoProxy implements InvocationHandler
	{
		//	定义需要被代理的目标对象
		private Object target;

		//	将目标对象与代理对象绑定
		public Object bind(Object targer)
		{
			this.target = target;

			//	调用Proxy的newProxyInstance方法产生代理类实例
			return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
		}

		//	实现接口InvocationHandler的invoke方法
		//	该方法将在目标类的被代理方法被调用之前,自动触发
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
		{
			Object result = null;
			
			//	1.目标类的被代理方法被调用之前,可以做的操作
			RecordLog.recordLog();

			//	2.方法的具体实现
			result = method.invoke(target, args);

			//	3.还可以在方法调用之后加上的操作
			//	自己补充

			return result;
		}
	}

关于Proxy, Method, InvocationHandler的具体说明,请参见JDK_API.

只对代码中关键部分做些解释说明:
3.4.1
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); 

表示生成目标类的代理类,传入的参数有目标类的ClassLoader, 目标类的接口列表, 和实现了接口InvocationHandler的代理类.
这样,bind方法就得到了目标类的代理类.

3.4.2
method.invoke(target, args);

目标类的被代理方法在被代用前,会自动调用InvocationHandler接口的invoke方法.
在该方法中,我们可以对目标类的被代理方法进行加强,比如说在其前后加上事务的开启和关闭等等.
这段代码才是真正调用目标类的被代理方法.

就这样,我们不用实现其他任何的接口,理论上就能代理所有类了.调用的方式如下:

3.5 Main:
	public class Main
	{
		public static void main(String[] args)
		{
			PeopleInfo peopleInfo = null;

			PeopleInfoProxy peopleInfoProxy = new PeopleInfoProxy();

			//	传入的参数是目标类实例,生成代理类实例,类型为Object
			Object obj = peopleInfoProxy.bind(new PeopleInfoImpl("Rock", 24));

			if(obj instanceof PeopleInfo)
			{
				peopleInfo = (PeopleInfo)obj;
			}
			peopleInfo.getInfo();
		}
	}

执行结果和上一节一样.
这就是使用Java动态代理机制的基本概述.而下一节,将要把Dynamic Proxy(动态代理)和AOP联系起来.

四.AOP概述.Spring的AOP.
AOP(Aspect Oriented Programming)面向切面编程.是一种比较新颖的设计思想.是对OOP(Object Orientd Programming)面向对象编程的一种有益的补充.

4.1 OOP和AOP
OOP对业务处理过程中的实体及其属性和行为进行了抽象封装,以获得更加清晰高效果的逻辑划分.研究的是一种“静态的”领域.
AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段.研究的是一种“动态的”领域.

举例说,某个网站(5016?)用户User类又可分为好几种,区长,管理员,斑竹和普通水友.我们把这些会员的特性进行提取进行封装,这是OOP.
而某一天,区长开会了,召集斑竹等级以上的会员参与,这样,普通水友就不能访问相关资源.

我们怎么做到让普通水友访问不了资源,而斑竹等级以上会员可以访问呢.
权限控制.对,权限.当水友们进行操作的时候,我们给他的身份进行权限的判断.
请注意,当且仅需水友门执行了操作的时候,我们才需要进行权限判断,也就是说,这是发生在一个业务处理的过程中的一个片面.
我们对这一个片面进行编程,就是AOP!

我这样,你应该能理解吧.

4.2 AOP的基本术语
4.2.1 切面Aspect
业务处理过程中的一个截面.就像权限检查.
通过切面,可以将不同层面的问题隔离开:浏览帖子和权限检查两者互不相干.
这样一来,也就降低了耦合性,我们可以把注意力集中到各自的领域中.
上两节的例子中,getInfo()和recordLog()就是两个领域的方法,应该处于切面的不同端.哎呀,不知不觉间,我们就用了AOP.呵呵...

4.2.2 连接点JoinPoint
程序运行中的某个阶段点.如某个方法的调用,或者异常的抛出等.
在前面,我们总是在getInfo()的前后加了recordLog()等操作,这个调用getInfo()就是连接点.

4.2.3 处理逻辑Advice
在某个连接点采取的逻辑.
这里的逻辑有三种:
I. Around 在连接点前后插入预处理和后处理过程.
II. Before 在连接点前插入预处理过程.
III.Throw 在连接点抛出异常的时候进行异常处理.

4.2.4 切点PointCut
一系列连接点的集合,它指明处理逻辑Advice将在何在被触发.

4.3 Spring中的AOP
Spring提供内置AOP支持.是基于动态AOP机制的实现.
所谓动态AOP,其实就是动态Proxy模式,在目标对象的方法前后插入相应的代码.(比如说在getInfo()前后插入的recordLog())
Spring AOP中的动态Proxy模式,是基于Java Dynamic Proxy(面向Interface)和CGLib(面向Class)的实现.

为什么要分面向接口和面向类呢.
还记得我们在生成代理类的代码吗:
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);

这里面的参数不许为空,也就是说:obj.getClass().getInterfaces()必有值,即目标类一定要实现某个接口.

有了这些,JVM在内存中就动态的构造出代理出来.

而没有实现任何接口的类,就必须使用CGLib来动态构造代理类.值得一提的是,CGLib构造的代理类是目标类的一个子类.

4.4 相关工程简解
Spring的相关知识不应该在这里讲,难度系数过大.这里只给个简单例子.供参考.
4.4.1 准备工作
打开Eclipse.新建Java工程,取名为AOP_Proxy.完成.
复制spring-2.0.jar.粘贴到AOP_Proxy下.
右击AOP_Proxy-->属性-->Java构建路径-->库-->添加JAR-->找spring-2.0.jar-->添加确定.
复制commons-logging.jar.粘贴到AOP_Proxy下.
右击AOP_Proxy-->属性-->Java构建路径-->库-->添加JAR-->找commons-logging.jar-->添加确定.

4.4.2 写代码
代码略.配置文件略.
         4.4.3 导入工程的步骤
新建工程AOP_Proxy-->完成-->右击AOP_Proxy-->导入-->常规-->文件系统-->找到项目文件,导入完成.

两个jar包和项目文件(项目文件需要先解压).
分享到:
评论
7 楼 hanjs 2008-12-03  
lz是写错了,没有实现接口的方法
6 楼 lirig 2008-09-12  
用spring的 事务,如果跟hibernat 结合的话,在一个事务里面控制了太多东西,是否会消耗太多内存?
5 楼 igogo007 2008-09-07  
Rooock 写道


2.4 创建一个代理类:
public class PeopleInfoProxy implements PeopleInfo
{
	//	接口的引用
	private PeopleInfo peopleInfo;

	//	构造函数	.针对接口编程,而非针对具体类
	public RecordLogProxy(PeopleInfo peopleInfo)
	{
		this.peopleInfo = peopleInfo;
	}

	//	实现接口中的方法
	public void record()
	{
		//	1.记录日志
		RecordLog.recordLog();

		//	2.方法的具体实现
		peopleInfo.getInfo();
	}
}

这个是类是一个代理类,它同样实现了PeopleInfo接口.比较特殊的地方在于这个类中有一个接口的引用private PeopleInfo peopleInfo;.


楼主这个方法严重写错了,楼上的也没有改正确。

1.构造方法写的是错误的。
2.没有实现接口中的方法。
4 楼 xidaboy 2008-09-07  
public class PeopleInfoProxy implements PeopleInfo
{
// 接口的引用
private PeopleInfo peopleInfo;

// 构造函数 .针对接口编程,而非针对具体类
public RecordLogProxy(PeopleInfo peopleInfo)
{
this.peopleInfo = peopleInfo;
}

// 实现接口中的方法
public void record()
{
// 1.记录日志
RecordLog.recordLog();

// 2.方法的具体实现
peopleInfo.getInfo();
}
}

==

public class PeopleInfoProxy implements PeopleInfo
{
// 接口的引用
private PeopleInfo peopleInfo;

// 构造函数 .针对接口编程,而非针对具体类
public RecordLogProxy(PeopleInfo peopleInfo)
{
this.peopleInfo = peopleInfo;
}

// 实现接口中的方法
public void getInfo()
{
// 1.记录日志
RecordLog.recordLog();

// 2.方法的具体实现
peopleInfo.getInfo();
}
}
3 楼 Joo 2008-09-07  
要讲AOP怎么能够少了CGLIB?
2 楼 davidgrubby 2008-09-06  
很不错!
目前手上恰好有一个这样类似的需求,请教一下。
如果我有30种event调用结束后需要调用RecordLog.recordLog(),并且log的方式有30种,请问这样的情况用AOP怎么更优雅的解决呢??

谢谢
1 楼 40020072 2008-09-06  
不错 讲解的很好~

相关推荐

    动态代理和AOP详解

    动态代理和AOP是Java和Spring框架中的重要概念,它们为开发者提供了强大的代码复用和模块化设计的能力。本文将深入解析这两个主题,并结合提供的源码进行详细讲解。 首先,让我们了解一下动态代理。在Java中,动态...

    Spring 动态代理和aop切面编程例子

    在Spring框架中,动态代理和AOP(面向切面编程)是两个重要的概念,它们极大地增强了代码的可维护性和灵活性。下面将详细讲解这两个概念及其实际应用。 动态代理,是Spring提供的一种机制,允许我们在不修改原对象...

    java动态代理实例aop

    总之,Java动态代理和AOP提供了强大的工具,使我们能够优雅地实现代码解耦,提高代码的复用性和可维护性。通过代理对象,我们可以在不修改原始类代码的情况下,添加额外的功能或改变原有行为,这对于系统的服务层和...

    java分页 动态代理 aop实现

    这些代码可能涉及到如何创建分页对象,如何与数据库交互获取分页数据,以及如何在AOP中使用动态代理进行拦截和处理。 总的来说,Java分页、动态代理和AOP是Java开发中的关键技术,它们能帮助我们构建出更加高效、可...

    代理机制及AOP原理实现

    在Java编程领域,代理机制和面向切面编程(AOP)是两个重要的概念,它们在Spring框架中得到了广泛的应用。本文将深入探讨这两个主题,并结合JDK的反射和CGLIB库来阐述其实现原理。 首先,我们来看动态代理。动态...

    从JDK动态代理到spring AOP

    在IT领域,动态代理和AOP(面向切面编程)是两种重要的编程概念,它们能够帮助我们实现代码的解耦和模块化。本篇将详细探讨JDK动态代理和Spring AOP,以及它们在实际应用中的作用。 首先,JDK动态代理是Java提供的...

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

    Spring AOP支持不同的代理策略,包括JDK动态代理和CGLIB代理。如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中...

    spring之AOP(动态代理)

    首先,我们需要引入Spring AOP和AspectJ的依赖,这通常在pom.xml或build.gradle文件中完成。然后,我们可以定义一个切面(Aspect),它包含通知(Advice)——即在特定连接点执行的代码。通知可以是前置通知(Before...

    用Java动态代理实现AOP

    Java动态代理实现AOP Java动态代理是实现Aspect Oriented Programming(AOP)的重要手段。在Java中,动态代理可以通过java.lang.reflect InvocationHandler接口和java.lang.reflect.Proxy类来实现。AOP的主要思想...

    AOP动态代理(反射机制)

    AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,旨在提供一种方法来组织和模块化系统中的交叉关注点,如日志、事务管理、权限控制等。它通过将这些关注点与核心业务逻辑分离,实现代码的解耦,...

    JDK动态代理 spring aop 的原理

    现在让我们深入探讨JDK动态代理和Spring AOP的原理。 首先,JDK动态代理基于Java的反射机制,通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现。Proxy类用于创建一个代理对象,...

    MethodInterceptProxy,MethodInterceptProxy for Android。AOP、测试、数据访问框架使用它生成动态代理对象和拦截字段访问。.zip

    MethodInterceptProxy是一个专门针对Android平台的动态代理库,它使得开发者能够方便地实现面向切面编程(AOP)以及进行单元测试和数据访问框架的优化。本文将深入探讨MethodInterceptProxy的原理、使用方法以及其在...

    基于Bytebuddy的Java Agent AOP框架.zip

    ByteBuddy是Java的一个库,用于在运行时创建和修改Java类和接口,它是创建Java代理或者实现AOP(面向切面编程)框架的理想工具。在本项目"基于Bytebuddy的Java Agent AOP框架.zip"中,我们将探讨如何使用ByteBuddy...

    动态代理实现AOP机制

    Spring提供了两种AOP代理方式:JDK动态代理和CGLIB。如果目标对象实现了至少一个接口,Spring会选择JDK动态代理;如果没有实现接口,Spring则会使用CGLIB代理。 AOP在Spring中的实现还涉及了切点(Pointcut)、通知...

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

    Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...

    解释Spring中的Aop代理

    Spring支持两种类型的AOP代理:JDK动态代理和CGLIB代理。 - JDK动态代理:基于Java反射API实现,适用于接口代理。如果目标对象实现了至少一个接口,Spring会使用JDK动态代理创建一个代理对象,该代理对象在调用...

    java反射与代理实现AOP

    ### Java反射与代理实现AOP #### 一、AOP概念及应用场景 AOP(Aspect-Oriented Programming,面向切面编程)是一种编程思想和技术,主要用于处理横切关注点问题,比如日志记录、性能统计、安全控制、事务处理、...

    JavaEE spring自动实现AOP代理

    Spring 提供了两种方式来实现AOP代理:JDK动态代理和CGLIB代理。 1. **JDK动态代理**: - JDK动态代理基于Java的接口实现,适用于目标对象实现了接口的情况。Spring会为这个接口创建一个代理类,代理类在调用真实...

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

    在`006_Dynamic_Proxy_Spring_AOP`这个压缩包中,可能包含了关于动态代理和Spring AOP的示例源代码。通过分析这些代码,你可以深入理解动态代理的实现细节,以及Spring如何在内部使用它来实现AOP功能。你可以查看...

Global site tag (gtag.js) - Google Analytics