1.什么是AOP
- 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发速度。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。
2.AOP底层原理
AOP底层使用动态代理,有两种情况的动态代理
-
有接口情况,使用JDK动态代理
-
没有接口情况,使用CGLIB动态代理
3.AOP(JDK动态代理)
使用JDK动态代理需要使用Proxy类里面的方法创建代理对象
public class Proxy
extends Object
implements Serializable
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
public static Object newProxyInstance(ClassLoader loader,
类<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
参数
loader - 类加载器来定义代理类
interfaces - 代理类实现的接口列表
h - 调度方法调用的调用处理函数
结果
具有由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例
代码演示:
public interface UserDao02 {
public int add(int a, int b);
public int reduce(int a, int b);
}
public class UserDao02Impl implements UserDao02 {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int reduce(int a, int b) {
return a - b;
}
}
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao02.class};
UserDao02 userDao02 = new UserDao02Impl();
UserDao02 o = (UserDao02)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao02));
System.out.println(o.add(1, 2));
System.out.println(o.reduce(4, 1));
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
//创建是谁的代理对象 把谁传递过来
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行:" + method.getName() + ":传递的参数:" + Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(this.obj, args);
//方法之后
System.out.println("方法之后执行:" + this.obj);
return res;
}
}
4.AOP(术语)
- 连接点:类里面哪些方法可以被增强,这些方法称为连接点
- 切入点:实际被增强的方法,称为切入点
- 通知(增强):实际增强的逻辑部分称为通知(增强)
通知有多种类型:
a)前置通知
b)后置通知
c)环绕通知
d)异常通知
e)最终通知 - 切面:切面是一个动作,表示把通知应用到切入点的过程
5.AOP操作(准备)
- Spring框架一般都是基于AspectJ实现AOP操作
- AspectJ不是Spring组成部分,是独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
- 基于AspectJ实现AOP操作:(a)基于xml配置文件实现 (b)基于注解方式实现 (一般都是基于注解实现)
- 切入点表达式:作用 :知道对哪个类里面的哪个方法进行增强; 语法结构:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表] ))
6.AOP操作(AspectJ注解)
6.1.创建类,在类里面定义方法
@Component
public class User01 {
public void add(){
System.out.println("user add......");
}
}
6.2.创建增强类,编写增强逻辑
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
//前置通知
@Before(value = "execution(* com.yulong.aopannot.User01.add(..))")
public void before(){
System.out.println("before.......");
}
//后置通知
@AfterReturning(value = "execution(* com.yulong.aopannot.User01.add(..))")
public void afterReturning(){
System.out.println("after returning..............");
}
//环绕通知
@Around(value = "execution(* com.yulong.aopannot.User01.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前...............");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后...............");
}
//异常通知
@AfterThrowing(value = "execution(* com.yulong.aopannot.User01.add(..))")
public void afterThrowing(){
System.out.println("after throwing.........");
}
//最终通知
@After(value = "execution(* com.yulong.aopannot.User01.add(..))")
public void after(){
System.out.println("after.........");
}
}
6.3.进行通知(增强)的配置
(a)在Spring配置文件中,开启注解扫描
(b)使用注解创建User和UserProxy对象
(c)在增强类上添加@Aspect注解
(d)在spring配置文件中开启生成代理对象
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.yulong"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
6.4.配置不同类型的通知:在增强类里面,在作为通知的方法上面添加通知类型注解,使用切入点表达式配置
6.5.测试输出的结果
环绕之前...............
before.......
user add......
after returning..............
after.........
环绕之后...............
在user01.add()方法中制造异常
@Component
public class User01 {
public void add(){
int a = 10/0;
System.out.println("user add......");
}
}
--------------------------------------------------
环绕之前...............
before.......
after throwing.........
after.........
java.lang.ArithmeticException: / by zero