一、AOP基本概念
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
二、AOP底层原理
AOP通过动态代理机制降低各功能直接的耦合度,使可以在不修改源码的情况下添加新的功能,其中有两种动态代理机制用于增强类的方法
1.存在接口时,JDK动态代理机制
使用JDK动态代理机制,是通过创建接口实现类的动态代理对象,来增强类的方法
首先创建接口UserDao以及接口实现类UserDaoImpl
package spring02day.AOP_JDK_work01.UserDao;
public interface UserDao {
public int plus(int a,int b);
public void get();
}
package spring02day.AOP_JDK_work01.UserDao;
public class UserDaoImpl implements UserDao{
@Override
public int plus(int a, int b) {
System.out.println("plus....");
return a+b;
}
@Override
public void get() {
System.out.println("getting....");
}
}
创建执行类,在其中使用Proxy.newProxyInstance方法创建动态代理对象
package spring02day.AOP_JDK_work01.UserService;
import spring02day.AOP_JDK_work01.UserDao.UserDao;
import spring02day.AOP_JDK_work01.UserDao.UserDaoImpl;
import java.lang.reflect.Proxy;
public class Service {
public static void main(String[] args) {
//要创建代理对象,就要调用静态Proxy.newProxyInstance方法
//其中有三个参数,第一个是实现代理类的加载,比如是Service类实现的代理,那使用其加载器
//第二个参数是实现接口类的接口,可以实现多个接口,都放在一个接口数组中
//第三个是实现InvocationHandler接口的类,用于写代码增强部分
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
UserDao userDao1 = (UserDao)Proxy.newProxyInstance(Service.class.getClassLoader(), interfaces, new UserProxy(userDao));
int plus = userDao1.plus(1,2);
System.out.println(plus);
}
}
其中Proxy.newProxyInstance方法的第三个参数需要实现InvocationHandler接口的类,那创建一个UserProxy方法用于实现InvocationHandler接口
package spring02day.AOP_JDK_work01.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserProxy implements InvocationHandler {
private Object obj;
public UserProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强前......");
//使用名称来判断调用了哪个方法
if(method.getName().equals("plus")){
args[0] = (Integer)args[0] + 1;
}
//使用method.invoke用于调用需实现增强的方法,在其前写的代码属于增强前操作,其后属于增强后操作
Object invoke = method.invoke(obj, args);
System.out.println("增强后......");
return invoke;
}
}
InvocationHandler接口中的invoke方法就是增强方法,JDK代理机制的底层实现完毕。
2.CGLIB动态代理机制
使用CGLIB动态代理机制是没有接口时,创建类的继承子类的代理对象,增强类的方法
三、AOP术语
1.连接点
类中可以被增强的方法称为连接点
2.切入点
实际上被增强的方法,即在连击点的基础上,真正被增强的方法
3.通知
增强方法的逻辑部分被称为通知
通知有五种:(1)前置通知:方法执行前的通知
(2)后置通知:方法执行后的通知
(3)环绕通知:方法执行前后都可以实现的通知
(4)异常通知:方法执行中遇到异常的通知
(5)最终通知:方法执行最后无论是否有异常都会执行的通知,类似于finally
4.切面
是一种动作,即将通知应用于切入点的动作
四、AOP操作
1.准备阶段
(1)Spring 框架一般都是基于 AspectJ 实现 AOP 操作
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
(2)基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)
(3)切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
(注:*表示任意)
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))
即不写权限表示任意权限,第一个*表示任意返回类型,路径为 com.atguigu.dao.BookDao,方法为add(),任意参数
举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))
2.Aspect注解
(1)基本使用
Aspect注解在增强类前使用,并且需要配置xml文件,且最后需要切入点表达式连接被增强类
创建被增强类
package spring02day.AOP_JDK_work02.USER;
import org.springframework.stereotype.Component;
//被增强类
@Component
public class User {
//设置切入点
public void add(){
System.out.println("相加成功...");
}
}
创建增强类,其中五种通知的标签罗列在下代码中
package spring02day.AOP_JDK_work02.UserProxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知,使用@Before注解,后使用切入点表达式
@Before(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void before(){
System.out.println("before....");
}
//最终通知,使用@After注解,无论是否有异常,都会执行
@After(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void after(){
System.out.println("after....");
}
//后置通知,使用@AfterReturning注解
@AfterReturning(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning....");
}
//异常通知,使用@AfterThrowing注解,只有有异常抛出时才会执行
@AfterThrowing(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing....");
}
//环绕通知,使用@Around注解
//其中加入ProceedingJoinPoint proceedingJoinPoint参数用于分开前置后置
//在其前是前置,在其后是后置,如果没有该参数,那程序只会执行其前指令
@Around(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("around....");
proceedingJoinPoint.proceed();
System.out.println("around down....");
}
}
配置xml文件,其中要创建用于bean注解的context空间和代理注解的aop空间
<?xml version="1.0" encoding="UTF-8"?>
<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,aop空间,用于注释-->
<!--context扫描-->
<context:component-scan base-package="spring02day.AOP_JDK_work02"></context:component-scan>
<!--aop注解自动查找增强类-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(2)抽取相同切入点
使用@Pointcut注解抽取相同切入点,最后调用方法名可以直接使用
@Pointcut(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
public void point(){
}
//前置通知,使用@Before注解,后使用切入点表达式
@Before(value = "point()")
public void before(){
System.out.println("before....");
}
(3)如果存在多个增强类增强同一切入点,设置优先级
在类前使用@Order()注解,在@Order()括号中填入数字,数字越小,优先级越高,数字从0开始