基本概念
面向切面编程(Aspect Oriented Programming,AOP),AOP 是一个概念,一个规范,是面向对象编程OOP的一种补充,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
OOP(面向对象编程)是从静态角度考虑程序的结构,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP是从动态角度考虑程序运行过程,是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
AOP最终的目的是为了实现对业务功能和非业务功能进行解耦,即将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
实现方法
在Spring框架中,AOP底层是采用动态代理来实现的,采用了两种代理——JDK的动态代理、Cglib的动态代理。
重要概念
- Aspect(切面):切面泛指交叉业务逻辑,例如事务处理、日志处理就可以理解为切面,实际就是对主业务逻辑的一种增强。
- Weaving(织入):织入是指将切面代码插入到目标对象的过程。
- Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
- Pointcut(切入点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
- Target(目标对象):目标对象指将要被增强的对象,即包含主业务逻辑的类对象。
- Advice(通知):Advice 定义了在 pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
附录:
代理模式介绍
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
UML类图:
1、静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态,也就是在程序运行前就已经存在代理类的字节码文件(.class文件),代理类和委托类的关系在运行前就确定了。
- 优点:
可以在不修改目标对象的前提下,扩展目标对象的功能,这也是代理共有的优点。 - 缺点:
1)冗余。代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)不易维护。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。
静态代理实现
- 接口类
public interface SomeService {
void doSomething();
}
- 目标类
public class SomeServiceImpl implements SomeService {
@Override
public void doSomething() {
System.out.println("----------doSomething-----------");
}
}
- 代理类
public class Proxy implements SomeService {
private SomeService someService;
public Proxy(SomeService someService) {
this.someService = someService;
}
@Override
public void doSomething() {
// 扩展功能1
System.out.println("-----------日志打印------------");
// 源目标对象方法
someService.doSomething();
// 扩展功能2
System.out.println("-----------事务处理------------");
}
}
- 测试类
public class Main {
public static void main(String[] args) {
// 目标对象
SomeService someService = new SomeServiceImpl();
// 代理对象
Proxy proxy = new Proxy(someService);
// 代理方法
proxy.doSomething();
}
}
- 运行结果
2、动态代理
动态代理是指,程序在整个运行过程中根本不存在目标类的代理类,目标对象的代理对象只是由代理生成工具在程序运行时由JVM根据反射等机制动态生成的,即编译完成后没有实际的.class文件,而是在运行时动态生成类字节码,并加载到JVM中,代理对象与目标对象的代理关系在程序运行时才确立。
2.1 JDK的动态代理
Java通过java.lang.reflect包提供的三个类——Proxy、Method、InvocationHandler,来支持代理模式,要求目标对象必须实现接口。
- java.lang.reflect.Proxy
public class Proxy implements java.io.Serializable {
...
// 创建代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{...}
}
"loader"指定当前目标对象使用类加载器,“interfaces”指定目标对象实现的接口的类型,“h”指定事件处理器,方法返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
2. java.lang.reflect.InvocationHandler
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
调用处理器接口,实现对目标对象的控制访问和功能增强,可以截取对目标对象方法的访问调用,“proxy”是系统生成的代理对象,“method”是目标方法,“args”是目标方法的参数,返回目标方法的执行结果。
3. java.lang.reflect.Method
public final class Method extends Executable {
...
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{...}
}
表示目标方法,invoke()用于执行目标对象的方法。
2.1.1 JDK的动态代理实现
- 目标接口类
public interface SomeService {
void doSomething();
void doOther();
}
- 目标对象类
public class SomeServiceImpl implements SomeService {
@Override
public void doSomething() {
System.out.println("----------doSomething-----------");
}
@Override
public void doOther() {
System.out.println("----------doOther-----------");
}
}
- 调用处理器实现类
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 根据方法名称控制增加额外功能的方法对象
String methodName = method.getName();
if("doSomething".equals(methodName)){
System.out.println("------------日志打印-----------");
result = method.invoke(target, args);
System.out.println("------------事务处理-----------");
}else{
result = method.invoke(target, args);
}
return result;
}
}
- 测试类
public class Main {
public static void main(String[] args) {
// 1、创建目标对象
SomeService someService = new SomeServiceImpl();
// 2、创建代理对象(需先创建调用处理器对象)
SomeService proxy = (SomeService) Proxy.newProxyInstance(
someService.getClass().getClassLoader(),
someService.getClass().getInterfaces(),
new MyInvocationHandler(someService));
// 3、通过代理对象,执行业务方法
proxy.doSomething();
System.out.println("====================");
proxy.doOther();
}
}
- 运行结果
2.2 Cglib的动态代理
Cglib(Code Generation Library),Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展,所以目标对象类和目标方法都不能是final、static。
2.2.1 Cglib的动态代理实现
- 导入Cglib的jar包到项目(cglib-full-2.0.2.jar)
- 定义目标类
public class Service {
public String doSome(){
System.out.println("------------业务处理-------------");
return "abcd";
}
}
- 定义方法拦截器类
public class MyInterceptor implements MethodInterceptor {
private Object target;
public MyInterceptor(Object target) {
this.target = target;
}
/**
* 类似于JDK中InvocationHandler的invoke()方法
* @param o 系统生成的代理对象
* @param method 代理目标方法
* @param objects 目标方法的参数列表
* @param methodProxy 目标方法的代理对象
* @return 目标方法的执行结果(可在此方法内做修改)
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
// 调用目标方法
result = method.invoke(target, objects);
// 修改执行结果
if(result != null){
result = ((String)result).toUpperCase();
}
return result;
}
}
- 定义代理工厂类(封装代理对象的创建)
public class ProxyFactory {
public Object createProxy(Object target){
// 1、创建Cglib中的Enhancer对象
Enhancer enhancer = new Enhancer();
// 2、指定目标类对象
enhancer.setSuperclass(Service.class);
// 3、指定方法拦截器对象
enhancer.setCallback(new MyInterceptor(target));
// 4、创建代理对象
return enhancer.create();
}
}
- 定义测试类
public class Test {
public static void main(String[] args) {
// 1、创建目标对象
Service service = new Service();
// 2、创建代理对象:新建工具类对象并调用创建代理方法
Service proxy = (Service) new ProxyFactory().createProxy(service);
// 3、通过代理对象执行业务方法
System.out.println("通过代理调用的方法结果:" + proxy.doSome());
}
}
- 输出结果