代理和AOP

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

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

public class RecordLog
{
public static void recordLog()
{
// 记录日志的操作
System.out.println("记录日志...");
}
}

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

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

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

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 抽象出来的记录日志的类:
Java代码
public class RecordLog
{
public static void recordLog()
{
// 记录日志的操作
System.out.println("记录日志...");
}
}

public class RecordLog
{
public static void recordLog()
{
// 记录日志的操作
System.out.println("记录日志...");
}
}

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

public interface PeopleInfo
{
public void getInfo();
}

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

2.3 实现该接口的类:
Java代码
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 + "岁了.");
}
}

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 创建一个代理类:
Java代码
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 record()
{
// 1.记录日志
RecordLog.recordLog();

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

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

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

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

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

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代码
java.lang.reflect.Proxy
java.lang.reflect.Method
java.lang.reflect.InvocationHandler

java.lang.reflect.Proxy
java.lang.reflect.Method
java.lang.reflect.InvocationHandler

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

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

public class RecordLog
{
public static void recordLog()
{
// 记录日志的操作
System.out.println("记录日志...");
}
}

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

public interface PeopleInfo
{
public void getInfo();
}

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

3.3 实现该接口的类:
Java代码
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 + "岁了.");
}
}

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:
Java代码
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;
}
}

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 Java代码
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
表示生成目标类的代理类,传入的参数有目标类的ClassLoader, 目标类的接口列表, 和实现了接口InvocationHandler的代理类.
这样,bind方法就得到了目标类的代理类.

3.4.2 Java代码
method.invoke(target, args);

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

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

3.5 Main:
Java代码
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();
}
}

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)的实现.

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

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包和项目文件(项目文件需要先解压).
<think>好的,用户想了解Java静态代理AOP的关系及实现方式,并且需要代码示例。首先,我需要回顾一下静态代理AOP的基本概念。 根据引用[2],静态代理需要为每个被代理的类编写一个代理类,代理代理类实现相同的接口。这样可以在调用方法前后添加额外的逻辑,比如日志或事务管理,这正是AOP的核心思想。AOP通过代理模式实现横切关注点的分离,静态代理作为其中一种实现方式,虽然代码量大,但结构清晰。 接下来,用户需要代码示例。我需要构造一个简单的例子,包含接口、实现类、代理类,并在代理类中添加日志逻辑。根据引用[3]中的静态代理描述,代理类需要实现相同的接口,并持有被代理对象的引用。在方法调用前后插入日志代码。 同时,要注意用户可能混淆静态代理动态代理的区别,所以在回答中可以简要对比两者,但重点放在静态代理上。引用[1]提到动态代理使用Proxy类,但这里不需要深入,只需说明静态代理的手动编写特性。 然后,确保代码正确性。比如,接口定义doSomething方法,实现类具体实现,代理类包装并添加日志。主类中直接调用代理对象的方法。这部分需要检查语法,确保没有错误。 用户可能还想知道静态代理AOP中的优缺点,比如代码冗余,但适用于简单场景。需要指出这点,但引用中提到的动态代理更灵活,可能作为后续扩展的问题。 最后,生成相关问题时要围绕静态代理AOP应用、与动态代理对比等方面,帮助用户深入理解。</think>### Java静态代理实现AOP的原理及代码示例 #### 一、静态代理实现AOP的核心原理 静态代理通过**手动创建代理类**的方式,在目标方法执行前后插入横切逻辑(如日志、事务管理等),实现AOP的横切关注点分离[^2][^3]。其核心特征包括: 1. **显式代理类**:需要为每个被代理类编写独立代理类 2. **接口约束**:代理类与被代理类必须实现相同接口 3. **方法增强**:通过包装目标对象方法实现功能扩展 #### 二、代码实现步骤 ##### 1. 定义业务接口 ```java // 业务接口 public interface UserService { void saveUser(String username); } ``` ##### 2. 实现原始类 ```java // 被代理类(目标对象) public class UserServiceImpl implements UserService { @Override public void saveUser(String username) { System.out.println("保存用户: " + username); } } ``` ##### 3. 创建代理类(AOP实现) ```java // 静态代理类 public class UserServiceProxy implements UserService { private final UserService target; // 持有目标对象 public UserServiceProxy(UserService target) { this.target = target; } @Override public void saveUser(String username) { // 前置增强(AOP Before Advice) System.out.println("[日志] 开始保存用户: " + username); // 调用目标方法 target.saveUser(username); // 后置增强(AOP After Advice) System.out.println("[日志] 用户保存完成"); } } ``` ##### 4. 客户端调用 ```java public class Main { public static void main(String[] args) { // 创建目标对象 UserService realService = new UserServiceImpl(); // 创建代理对象(注入目标对象) UserService proxy = new UserServiceProxy(realService); // 通过代理对象调用方法 proxy.saveUser("张三"); } } ``` #### 三、执行结果 ```text [日志] 开始保存用户: 张三 保存用户: 张三 [日志] 用户保存完成 ``` #### 四、静态代理AOP特性分析 | 特性 | 实现方式 | AOP对应概念 | |--------------------|-----------------------------------|------------------| | 方法拦截 | 重写接口方法 | Pointcut | | 增强逻辑 | 在方法调用前后插入代码 | Advice | | 目标对象访问 | 通过代理类持有目标对象引用 | Target Object | | 代理关系 | 编译时确定 | Weaving | #### 五、优缺点对比 ✅ **优势**: - 代码结构清晰 - 无额外依赖 - 编译时检查错误 ❌ **局限**: - 需要为每个类创建代理类(代码冗余) - 接口修改需同步修改代理类 - 无法动态代理未实现接口的类[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值