AOP:Aspect Oriented Programming 面向切面编程
OOP:Object Oriented Programming 面向对象编程
面向切面编程:基于OOP基础之上新的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在处理日志、安全管理、事务管理等方面有非常重要的作用。AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。通俗点说的话就是在程序运行期间。
在不修改原有代码的情况下 增强跟主要业务没有关系的公共功能代码到 之前写好的方法中的指定位置 这种编程的方式叫AOP.
AOP的底层用的代理,代理是一种设计模式。
1.JDK动态代理
public interface ICalculator {
public Integer add(Integer i ,Integer j);
public Integer sub(Integer i,Integer j);
public void test();
}
<a name="LpcaF"></a>
## 1.2 创建业务类
package com.xixi.dynamic;
public class Calculator implements ICalculator { @Override public Integer add(Integer i, Integer j) { Integer result = i + j; return result; }
@Override
public Integer sub(Integer i, Integer j) {
Integer result = i - j;
return result;
}
@Override
public void test() {
System.out.println("test.............");
}
}
<a name="qIWMR"></a>
## <br />
<a name="nAQ57"></a>
## 1.3 创建代理测试类
package com.xixi.dynamic;
import org.junit.Test;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
//使用JDK动态代理实现增加日志 public class MainTest {
@Test
public void test() {
Calculator calculator = new Calculator();
ICalculator o = (ICalculator)creteInstance2(calculator);
System.out.println(o.add(1, 2));
System.out.println(o.sub(2, 1));
o.test();
}
//使用外部InvocationHandler private Object creteInstance(Object needProxy) { //动态代理 //ClassLoader loader, 类加载器,通常指定的被代理类的接口的类加载器 //Class<?>[] interfaces,指定的被代理类的接口的接口类型 //InvocationHandler h 委托执行的增强处理类 ClassLoader classLoader = needProxy.getClass().getClassLoader(); Class<?>[] interfaces = needProxy.getClass().getInterfaces(); InvocationHandler handler = new MyInvocationHandler(needProxy); Object res = Proxy.newProxyInstance(classLoader, interfaces, handler); return res; }
//使用内部匿名类创建代理对象
private Object creteInstance2(Object needProxy) {
//动态代理
//ClassLoader loader, 类加载器,通常指定的被代理类的接口的类加载器
//Class<?>[] interfaces,指定的被代理类的接口的接口类型
//InvocationHandler h 委托执行的增强处理类
Object o = Proxy.newProxyInstance(needProxy.getClass().getClassLoader(), needProxy.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before....");
Object o1 = method.invoke(needProxy,args);
System.out.println("after....");
return o1;
}
});
return o;
}
}
<a name="OGww6"></a>
## 1.4 创建外部InvocationHandler(creteInstance方法需要创建)
package com.xixi.dynamic;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
//代理类的执行方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("日志输入前....");
Object res = method.invoke(target,args);
System.out.println("日志输入后....");
return res;
}
}
<a name="GmtjZ"></a>
# 2 AOP的概念
**AOP的核心概念及术语**
- 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schema-based approach)或者在普通类中以@Aspect注解(@AspectJ 注解方式)来实现。
- 连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。
- 通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”, “before” and “after”等等。通知的类型将在后面的章节进行讨论。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
- 切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
- 引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。
- 目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。
- AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
- 织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。
**AOP的通知类型**
- 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
- 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
- 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。
- 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
- 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。
<a name="KzkdG"></a>
## 2.1 切面编写
package com.xixi.aspect;
import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;
/* 切面
- 声明为切面,将切面交给Spring去管理
*/ @Component @Aspect public class LogUtil {
// 前置通知 @Before(value = “execution( com.xixi.service...*(..))”) public static void before() {
/* System.out.println(method.getName()+"方法运行前,参数是"+ (args==null?"": Arrays.asList(args).toString()));*/ System.out.println("方法前");
}
// 后置通知 @After(value = “execution( com.xixi.service...*(..))”) public static void after() {
/* System.out.println(method.getName() +"方法运行后,参数是"+ (args==null?"": Arrays.asList(args).toString()));*/ System.out.println("方法后");
}
// 后置异常通知 @AfterThrowing(value = “execution( com.xixi.service...*(..))”) public static void afterException() {
// System.out.println("方法报错了:"+ex.getMessage()); System.out.println("方法异常");
}
// 后置返回通知 @AfterReturning(value = “execution( com.xixi.service...*(..))”) public static void afterEnd() {
//System.out.println("方法结束,返回值是:"+returnValue); System.out.println("方法返回");
}
}
```