Java 面向切面编程
目录
面向对象编程 (Object-Orentied Programming - OOP) 的特点是继承、多态和封装,其中封装指的是把属性和方法按类(Class) 进行划分。从而复用代码并降低编程的复杂性。在实际项目中,随着业务的变化,项目中的类一般会越来越多。这样会出现一些新的问题,比如:
- 不同类之间重复的代码越来越多,例如每个类的方法中都要打日志。
- 方法中除了业务逻辑之外,要实现大量与业务无关的功能,例如调试, 缓存, 鉴权等。
为了解决上述问题,第一个思路是把重复的代码或者业务逻辑之外的功能抽象出来,然后在新的类中实现。但是这样一来,新类与旧类在项目中就耦合了。即,新类的改变会影响旧的类。
第二个思路是 在旧类需要的时候(编译或运行时)动态地加入这些功能,这就是面向切面编程(Aspect-Oreinted Programming – AOP)的概念。切面代表了一个功能点,它一般是对类的功能的补充。站在业务的角度来看,切面是与业务逻辑无关的功能。
如果把面向对象编程看成是纵向的(类之间功能独立),那么面向切面编程就是横向的,它为纵向的类提供业务无关的能力,因此面向切面编程可以看成是对面向对象编程的补充。
下面以 Java 语言为例,介绍 AOP 的实现。
现在要实现一个Reminder
接口,其核心功能是发一条提醒的消息。除此之外,发消息之前需要取得授权,成功发消息之后需要打一条日志。在这里我们把授权和打日志看成是 切面 (非业务逻辑)。
为了实现切面功能, 我们用到设计模式中的「代理模式」。
// Reminder.java
public interface Reminder {
public void remind();
}
Reminder
的实现如下。
// ReminderImpl.java
public class ReminderImpl implements Reminder {
@Override
public void remind() {
System.out.println("Reminder: Wake up!");
}
}
静态代理
创建一个代理类 ReminderProxy
,只负责实现授权和打日志功能。业务逻辑的处理则调用 Reminder
中的方法。
// ReminderProxy.java
public class ReminderProxy implements Reminder {
private Reminder reminder; // 被代理的对象
public ReminderProxy(Reminder reminder) {
this.reminder = reminder;
}
private void authorize() {
System.out.println("Authorization: OK.");
}
private void log() {
System.out.println("Log info: Remind at " + new Date().getTime());
}
@Override
public void remind() {
authorize(); // 授权
reminder.remind(); // 调用代理类的业务逻辑
log(); // 打日志
}
}
Client 可以这样使用代理类。
// Client.java
public class Client {
@Test
public void clientOfProxy() {
Reminder reminder = new ReminderProxy(new ReminderImpl());
reminder.remind();
}
}
静态代理虽然能为类增加新的功能,但它依赖已经实现的类。当有多个实现类时,我们需要依次实现多个代理类,这样并不能减少重复代码。为了解决这个问题,我们利用 Java 的反射动态生成代理类,即在编译或运行时生成类。
动态代理
Java对动态代理提供了一下支持:
java.lang.reflect.Proxy
动态生成代理类和对象java.lang.reflect.InvocationHandler
实现对代理类和对象的访问
下面用动态代理的方法来实现上述功能 (即AOP的思想)。考虑一个非常简单的模型,即在代理对象执行的方法之前和之后分别加入bofore
和 after
的方法。
定义新功能的接口。
BeforeAdvice
: 在代理方法之前执行(例如,授权)AfterAdvice
: 在代理方法之后执行(例如,打日志)
// BeforeAdvice.java
public interface BeforeAdvice {
public void before();
}
// AfterAdvice.java
public interface AfterAdvice {
public void after();
}
创建代理工厂,利用 InvocationHandler
调用被代理的对象。
public class ProxyFactory {
private BeforeAdvice beforeAdvice;
private Object object; // 被代理的对象
private AfterAdvice afterAdvice;
public ProxyFactory(BeforeAdvice beforeAdvice, Object object, AfterAdvice afterAdvice) {
this.beforeAdvice = beforeAdvice;
this.object = object;
this.afterAdvice = afterAdvice;
}
// 用来调用被代理对象
private InvocationHandler invocationHandler = new InvocationHandler() {
/**
*
* @param proxy 代理对象
* @param method 被调用的方法
* @param args 被调用方法的参数
* @return 被调用方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用代理对象之前执行的方法
if (beforeAdvice != null) beforeAdvice.before();
// 调用被代理对象object的方法
Object result = null;
if (object != null) result = method.invoke(object, args);
// 调用代理对象之后执行的方法
if (afterAdvice != null) afterAdvice.after();
return result;
}
};
// 创建被代理对象
public Object createProxy() {
// Proxy.newProxyInstance参数:
// 1. ClassLoader: 定义代理类的类加载器
// 2. Class[]: 接口类的列表
// 3. InvocationHandler: 调用方法的处理器
return Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
object.getClass().getInterfaces(),
invocationHandler
);
}
}
Client通过实现 BeforeAdvice
和 AfterAdvice
为代理对象添加新的功能。
public class Client {
private static class ReminderBeforeAdvice implements BeforeAdvice {
@Override
public void before() {
System.out.println("Authorization: OK.");
}
}
private static class ReminderAfterAdvice implements AfterAdvice {
@Override
public void after() {
System.out.println("Log info: Remind at " + new Date().getTime());
}
}
@Test
public void clientOfProxyFactory() {
Reminder reminder = (Reminder)new ProxyFactory(
new ReminderBeforeAdvice(),
new ReminderImpl(),
new ReminderAfterAdvice()
).createProxy();
reminder.remind();
}
}