1. 代理的分类:

  静态代理:每个代理类只能为一个接口服务

  动态代理:可以通过一个代理类完成全部的代理功能(由JVM生成实现一系列接口的代理类,即:生成实现接口的类的代理)

2. 动态代理:

在Java中要想实现动态代理机制,需要 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类的支持

java.lang.reflect.InvocationHandler 接口的定义如下:

public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

Object proxy:被代理的对象

Method method:要调用的方法

Object[] args:方法调用时传递的参数

注意:并不是对象的所有方法均通过代理来完成,对如继承自Object的方法,只将hashCode()、equals()、toString()转交给InvocationHandler来处理。

可以通过Proxy中的getProxyClass()或newProxyInstance()方法获取代理

其中newProxyInstance()方法定义如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

ClassLoader loader:类加载器

Class<?>[] interfaces:要实现的接口

InvocationHandler h:InvocationHandler子类的实例对象

下面来实现动态代理:

首先定义接口:

1 public interface Sourceable {
2 void function();
3 }

然后定义实现类:

1 public class Source implements Sourceable {
2
3 @Override
4 public void function() {
5 System.out.println("function");
6 }
7 }

定义工具类:

 1 import java.lang.reflect.Constructor;
2 import java.lang.reflect.InvocationHandler;
3 import java.lang.reflect.Method;
4 import java.lang.reflect.Proxy;
5
6 public class SourceableProxyUtil {
7 private Sourceable source;
8
9 public SourceableProxyUtil(Sourceable source) {
10 super();
11 this.source = source;
12 }
13
14 public Sourceable getProxy() throws Exception {
15 Class<Sourceable> clazzSource = (Class<Sourceable>) Proxy
16 .getProxyClass(source.getClass().getClassLoader(), source
17 .getClass().getInterfaces());
18 Constructor<Sourceable> constructor = clazzSource
19 .getConstructor(InvocationHandler.class);  // 没有无参构造函数
20
21 return constructor.newInstance(new InvocationHandler() {
22
23 @Override
24 public Object invoke(Object proxy, Method method, Object[] args)
25 throws Throwable {
26 System.out.println("before");
27 Object retVal = method.invoke(source, args);
28 System.out.println("after");
29 return retVal;
30 }
31 });
32 }
33 }

但是,使用getProxyClass()方式过于复杂,改用newProxyInstance():

 1 import java.lang.reflect.Constructor;
2 import java.lang.reflect.InvocationHandler;
3 import java.lang.reflect.Method;
4 import java.lang.reflect.Proxy;
5
6 public class SourceableProxyUtil {
7 private Sourceable source;
8
9 public SourceableProxyUtil(Sourceable source) {
10 super();
11 this.source = source;
12 }
13
14 public Sourceable getProxy() throws Exception {
15 return (Sourceable)Proxy.newProxyInstance(source.getClass().getClassLoader(),
16 source.getClass().getInterfaces(), new InvocationHandler() {
17
18 @Override
19 public Object invoke(Object proxy, Method method,
20 Object[] args) throws Throwable {
21 System.out.println("before");
22 Object retVal = method.invoke(source, args);
23 System.out.println("after");
24 return retVal;
25 }
26 });
27 }
28 }

测试代码:

1 public class Main {
2
3 public static void main(String[] args) throws Exception {
4 Sourceable source = new Source();
5 Sourceable proxy = new SourceableProxyUtil(source).getProxy();
6 proxy.function();
7 }
8 }

测试结果:

before
function
after

可以看出,代理类能够正常使用。

但是上述方式只能用于生成Sourceable接口的代理类,可以进一步将其抽象得到通用的代理类生成工具:

 1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public final class ProxyUtil {
6 public static Object getProxy(final Object target) throws Exception {
7 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
8 target.getClass().getInterfaces(), new InvocationHandler() {
9
10 @Override
11 public Object invoke(Object proxy, Method method,
12 Object[] args) throws Throwable {
13 System.out.println("before");
14 Object retVal = method.invoke(target, args);
15 System.out.println("after");
16 return retVal;
17 }
18 });
19 }
20 }

这样,无论任何类型的对象都可以通过此种方法获得相应的代理对象。此时,测试代码如下:

1 public class Main {
2
3 public static void main(String[] args) throws Exception {
4 // TODO Auto-generated method stub
5 Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source());
6 proxy.function();
7 }
8 }

3. 抽取切面:

将上述代码中的目标方法执行前后的操作进一步地抽象,将其抽取为切面。将切面代码进行封装得到通告。

定义通告接口:

1 public interface Advice {
2 void before();
3 void after();
4 }

改写工具类:

 1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public final class ProxyUtil {
6 public static Object getProxy(final Object target, final Advice advice) throws Exception {
7 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
8 target.getClass().getInterfaces(), new InvocationHandler() {
9
10 @Override
11 public Object invoke(Object proxy, Method method,
12 Object[] args) throws Throwable {
13 advice.before();
14 Object retVal = method.invoke(target, args);
15 advice.after();
16 return retVal;
17 }
18 });
19 }
20 }

此时的测试代码为:

 1 public class Main {
2
3 public static void main(String[] args) throws Exception {
4 Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source(), new Advice() {
5
6 @Override
7 public void before() {
8 // TODO Auto-generated method stub
9 System.out.println("before");
10 }
11
12 @Override
13 public void after() {
14 // TODO Auto-generated method stub
15 System.out.println("after");
16 }
17 });
18 proxy.function();
19 }
20 }

此种方式更具有普遍适用性,无论对于任意类型都可以产生相对应的代理对象,同时可以自定义所要进行的操作(通过通告)。

在整个过程中需要手动写的只有通告Advice

4. AOP编程:

AOP(Aspect-Oriented Programming)可以通过代理方式完成诸如:安全控制、事务管理、性能统计、日志记录、错误处理...等功能。

在实际过程中,更多是通过getter/setter方式传递target与advice的:

 1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public class ProxyFactoryBean {
6
7 private Object target;
8 private Advice advice;
9
10
11 public Object getTarget() {
12 return target;
13 }
14
15
16 public void setTarget(Object target) {
17 this.target = target;
18 }
19
20
21 public Advice getAdvice() {
22 return advice;
23 }
24
25
26 public void setAdvice(Advice advice) {
27 this.advice = advice;
28 }
29
30
31 public Object getProxy() {
32
33 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
34 target.getClass().getInterfaces(), new InvocationHandler() {
35
36 @Override
37 public Object invoke(Object proxy, Method method,
38 Object[] args) throws Throwable {
39 advice.before();
40 Object retVal = method.invoke(target, args);
41 advice.after();
42 return retVal;
43 }
44 });
45 };
46 }

通过AOP的方式可以在运行时动态地将代码切入到类的指定方法、指定位置上。

Spring框架的核心即是IoC和AOP。

动态代理与AOP的更多相关文章

  1. 动态代理到基于动态代理的AOP

    动态代理,是java支持的一种程序设计方法. 动态代理实现中有两个重要的接口和类,分别是InvocationHandler(interface),Proxy(class). 要实现动态代理,必须要定义 ...

  2. 动态代理实现AOP【转】

    http://blog.csdn.net/beijiguangyong/article/details/8624016 根据前面介绍的Proxy和InvocationHandler,实在很难看出这种动 ...

  3. .Net 动态代理,AOP

    .Net 动态代理,AOP 直接上代码了. /***************************************** * author:jinshuai * * E-mail:redfox ...

  4. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  5. Java动态代理-->Spring AOP

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Jav ...

  6. .Net 框架实现AOP(动态代理实现AOP,本文为翻译)

    在上一节,我们将静态实现AOP,但是对于一个大型项目,要想为每个类,每个方法都去实现AOP ,进行日志记录和权限验证似乎是不可能的. 即使可能对于成百上千个类维护,也是很难维护.所以今天的主题就是如标 ...

  7. Java使用动态代理实现AOP

    参考资料: http://www.importnew.com/15420.htmlhttp://www.cnblogs.com/techyc/p/3455950.html Spring是借助了动态代理 ...

  8. DispatchProxy实现动态代理及AOP

    DispatchProxy类是DotnetCore下的动态代理的类,源码地址:Github,官方文档:MSDN.主要是Activator以及AssemblyBuilder来实现的(请看源码分析),园子 ...

  9. Java 动态代理与AOP

    动态代理与AOP 代理模式 代理模式给某一个目标对象(target)提供代理对象(proxy),并由代理对象控制对target对象的引用. 模式图: 代理模式中的角色有: 抽象对象角色(Abstrac ...

  10. Java 动态代理及AOP实现机制

    AOP实现机制http://www.iteye.com/topic/1116696 AOP: (Aspect Oriented Programming) 面向切面编程AOP包括切面(aspect).通 ...

随机推荐

  1. ASP.NET MVC轻教程 Step By Step 12——客户端验证

    前面两节使用的两种数据验证方法都是在服务器端进行的,也就是提交了表单,数据回传给服务器才能验证.这样会带来两个问题,一是用户体验不好,用户提交了表单之后才知道存在问题:二是会给服务器带来额外的压力.我 ...

  2. PS证件照换背景

    综述 博主原创内容. 在PS里,对于抠图,比较有技术含量的便是抠头发丝了,下面为大家带来一个比较详细的抠头发丝的教程. 素材准备 在这里我们用这张图片作为抠图素材,下面让我们一步步来演示抠图的过程,并 ...

  3. GetSystemMetrics() 函数的用法

    可以用GetSystemMetrics函数可以获取系统分辨率,但这只是其功能之一,GetSystemMetrics函数只有一个参数,称之为「索引」,这个索引有75个标识符,通过设置不同的标识符就可以获 ...

  4. MFC窗口分割以及各窗口间的通讯

    一个偶然的机会又重新接触了MFC窗口的分割,自己结合资料重新写了一个窗口分割的程序,现将具体流程跟大家分享一下: 1.我们先创建一个MFC单文档类的程序,具体分割方式先将单文档整个客户区分成两行一列, ...

  5. Codeforces 161D

    树形DP: 要求找出树上距离为k的点的对数: 对于每个节点,经过这个节点的符合条件的的点有两种: 第一种:距离他为i的儿子和他爸爸中距离他爸爸为k-i-1:(不是符合的点对中的一个) 第二种:他儿子中 ...

  6. lc面试准备:Regular Expression Matching

    1 题目 Implement regular expression matching with support for '.' and '*'. '.' Matches any single char ...

  7. python中文乱码例子

    #coding=utf-8 #---中文乱码--- #直接打印中文 print '千里之外取人首级,瞬息之间爆人菊花.' #中文前面加u,变成Unicode编码 print u'千里之外取人首级' # ...

  8. Linux学习笔记27——共享内存

    一 共享内存 共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中.其他进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址.如果某个进程向 ...

  9. Hash(4) hashtable,hashmap

    首先,我们要知道set是利使用map是实现的,因为只要利用map中的key唯一性就行了. 1.hashmap 和hashtable的区别是什么? 我们可以背出:  hashtable线程安全.hash ...

  10. [Java] JavaMail 发送带图片的 html 格式的邮件

    JavaMail 发送的邮件正文和附件是相互独立的,但是内置图片需要定位图片在正文中的位置,所以内置图片和邮件正文是互相依赖的. 发送带附件的邮件可参考JavaMail 发送 html 格式.带附件的 ...