1.什么是AOP
Aspect Oriented Programming:面向切面编程,和OOP面向对象编程相辅相成,也是对OOP很好的补充。
怎么理解 "面向切面" 呢,我把他看做对系统的一次“重构”
在OOP的世界里,存在很多重复且与核心业务逻辑无关的代码,比如性能监控、日志记录、事务提交与回滚
虽然可以通过良好的设计将这些重复的代码抽象出来,但是调用这些公共方法的【客户端】仍然无法解脱出来的
AOP就是专门用来解决这些问题的,他把与核心业务无关的代码、以及客户端的调用都释放出来
使得原本交织在一起看起来像一张网一样的系统结构,变成像积木一样可任意选用、装配
因此我把AOP当做对系统的重构来了解,他完成了传统OOP很难甚至无法完成的任务
2. AOP 机制
分为静态AOP和动态AOP两种机制
静态AOP:在Java代码编译成字节码的过程中,修改字节码。优点性能高,缺点是灵活性不够。
动态AOP:动态修改字节码,比如 JDK动态代理、cglib类增强、自定义类加载器、AspectJ和Javassist动态修改字节码等方式都可以实现
3.AOP 家族成员
JoinPoint:连接点,比如 类初始化前、初始化后、方法调用前、调用后,也就是说连接点是客观存在的!
PointCut:切点,一种规则或者表达式,能匹配一组连接点。比如以 update 为前缀的方法,就能匹配到多个连接点
Advice:增强,比如以 update 开头的方法结束之后,统一提交或者回滚事务。
Target:目标对象,比如 UserServiceImpl 业务类的实例,也就是原生对象。
Proxy:代理对象,目标对象的代理。客户端要使用的其实是这个代理对象。
Aspect:切面,由PointCut + Advice 组成,也可以看作是 JoinPoint(点)---->PointCut(线)----->Aspect(面) 的一个过程
Weaving:织入,这是一个过程:把切面逻辑织入到切点匹配的连接点上,实际上就构造了最终的代理对象。
4.AOP 实现机制
4.1 JDK动态代理
package com.yli.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK 简单动态代理要求必须定义接口<br>
* 借助InvocationHandler和Proxy就可以生成代理类<br>
* InvocationHandler就是AOP概念中的切面 Advice <br>
* 在这个类的invoke方法内部可以代理原生类方法的一切行为<br>
* 原生类的所有方法就相当于JoinPoint,这一组JoinPoint构成了一个PoinCut
*
* @author yli
*/
public class JdkDynamicTest {
public static void main(String[] args) {
// 原生对象
IBusiness bi = new BusinessImpl();
// 代理对象
IBusiness proxyBI = UnitJdkDynamicUtil.getProxy(bi);
proxyBI.printHello("Tom");
}
}
/**
* 定义一个简单的工具类,提供一个泛型方法,返回任意类型的代理对象
*/
class UnitJdkDynamicUtil {
public static <T> T getProxy(T t) {
// 1.生成InvocationHandler
// InvocationHandler handler = new UnitJdkDynamicHandler(initObj);
InvocationHandler handler = new UnitJdkDynamicHandler(t);
// 2.获取类加载器
ClassLoader loader = t.getClass().getClassLoader();
// 3.获取接口(JDK 的动态代理要求必须定义接口...)
Class<?>[] interfaces = t.getClass().getInterfaces();
// 对原生对象包装,生成代理对象
return (T) Proxy.newProxyInstance(loader, interfaces, handler);
}
}
/**
* 定义InvocationHandler:目标对象依赖他生成代理对象
*/
class UnitJdkDynamicHandler implements InvocationHandler {
private Object obj;
public UnitJdkDynamicHandler(Object target) {
obj = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("*** 方法开始执行前 ****");
System.out.println("**** 方法入参 ****");
if (null != args) {
for (Object obj : args) {
System.out.println(String.valueOf(obj));
}
}
System.out.println("\n**** 准备执行方法 ****\n");
Object result = method.invoke(obj, args);
System.out.println("\n**** 方法执行完,返回结果 ****");
System.out.println(String.valueOf(result));
return result;
}
}
/**
* 定义业务接口
*/
interface IBusiness {
void printHello(String name);
}
/**
* 业务实现类
*/
class BusinessImpl implements IBusiness {
@Override
public void printHello(String name) {
System.out.println("hello, " + name);
}
}
class MyClassLoaderT extends ClassLoader {
byte[] classBytes;
public MyClassLoaderT(byte[] classBytes) {
this.classBytes = classBytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);
if (cl == null) {
throw new ClassNotFoundException();
}
return cl;
}
}
JDK 动态代理的核心实现过程,通过查看JDK原代码可以总结为以下几句
//得到对象字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
//InvocationHandler对应的class对象
Class[] constructorParams = { InvocationHandler.class };
//得到代理对象的Class对象
Class proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
//通过反射机制构造代理对象的实例
Constructor cons = cl.getConstructor(constructorParams);
Object proxy = (Object) cons.newInstance(new Object[] { handler });
4.2 cglib 类增强
package com.yli.aop;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* cglib:为原生类生成子类,子类即代理对象
*
* @author yli
*/
public class CglibDynamicTest {
public static void main(String[] args) {
// 创建一个织入器
Enhancer enhancer = new Enhancer();
// 设置父类 (不要求依赖于接口)
enhancer.setSuperclass(BusinessImpl.class);
// 设置需要织入的逻辑
enhancer.setCallback(new LogIntercept());
// 使用织入器创建子类
IBusiness newBusiness = (IBusiness) enhancer.create();
newBusiness.printHello("Tom");
}
static class LogIntercept implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("--->方法执行前");
// 执行原有逻辑,注意这里是invokeSuper
Object rev = proxy.invokeSuper(target, args);
System.out.println("--->方法执行后");
return rev;
}
}
}
4.3 Javassist 动态修改类的字节码
注意依赖于 javassist 这个jar包,可以网上去下载
package com.yli.aop;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
/**
* Javassist插件可以直接修改类的字节码
*/
public class SelfClassLoaderDynamicTest {
public static void main(String[] args) throws Exception {
// 用于取得字节码类,必须在当前的classpath中,使用全称
CtClass ctClass = ClassPool.getDefault().get("com.yli.aop.A");
// 需要修改的方法名称
String mname = "print1";
CtMethod mold = ctClass.getDeclaredMethod(mname);
// 修改原有的方法名称
String nname = mname + "$impl";
mold.setName(nname);
// 创建新的方法,复制原来的方法
CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
// 主要的注入代码
StringBuffer body = new StringBuffer();
body.append("{\nlong start = System.currentTimeMillis();\n");
body.append("System.out.println(\"before\");\n");
// 调用原有代码,类似于method();($$)表示所有的参数
body.append(nname + "($$);\n");
body.append("System.out.println(\"Call to method " + mname
+ " took \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");
body.append("}");
// 替换新方法
mnew.setBody(body.toString());
// 增加新方法
ctClass.addMethod(mnew);
// 类已经更改,注意不能使用A a = new A();
// 因为在同一个classloader中,不允许装载同一个类两次
A a = (A) ctClass.toClass().newInstance();
a.print();
}
}
/**
* 原生类:负责核心业务逻辑
*
* @author yli
*/
class A {
public void print() {
print1();
System.out.println("aaaaaaaaa");
}
private void print1() {
System.out.println("a1111111111");
}
}
5. AOP 框架
主流的AOP框架有
AspectJ:这是语言级别的AOP框架实现,可以再编译阶段即完成切面织入
Spring AOP:基于JDK动态代理和cglib类增强来实现,他不是为了提供一个完整的AOP框架,而是为了很好的融合Spring IOC,也支持将AspectJ整合进来使用。
Jboss AOP:Jboss维护的一个开源AOP框架,在JBoss应用服务器上有很好的实现和融合