AOP概念
-
什么是AOP?
面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提升程序的可重用性,同时提高开发效率。
通俗来讲就是:不通过修改源代码的形式,在主干功能上对功能进行增强
-
AOP底层原理
AOP底层使用动态代理
-
第一种情况:有接口,使用JDK动态代理技术
创建接口实现类代理对象
-
第二种情况:没有接口,使用CGLIB动态代理技术
创建当前类子类的代理对象
-
-
AOP底层原理(JDK动态代理技术)
场景模拟:我们已有一个UserDaoImpl类,实现了UserDao接口的方法,我们现在要对这两个方法进行增强,在不改变源代码的情况下
public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a+b; } @Override public String update(String username) { return username; } }
首先要用到的是Proxy类,调用Proxy.newProxyInstance方法,可以创建UserDao接口的一个代理对象
看一下Java8文档,这个方法需要三个参数
- 第一个参数:类加载器
- 第二个参数:实现类的接口
- 第三个参数:是一个接口对象,我们可以使用匿名内部类,也可以创建一个该接口的实现类,该实现类中的方法用于增强代码
public class MyProxy { public static void main(String[] args) { Class[] interfaces = {UserDao.class}; UserDaoImpl userDao = new UserDaoImpl(); UserDao dao = (UserDao) Proxy.newProxyInstance(MyProxy.class.getClassLoader(), interfaces, new UserProxy(userDao)); int add = dao.add(10, 20); System.out.println("增强后结果为:" + add);//60 } } class UserProxy implements InvocationHandler { //这里本可以写UserDao对象,为了通用性将他写出Object对象 private Object obj; public UserProxy(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //解释一下参数,method表示我们在外部调用的方法名,args表示方法中带有的参数 //执行方法前增强代码 System.out.println(method.getName() + "方法之前,参数为:" + Arrays.toString(args)); //被增强的方法执行 Object result = method.invoke(obj, args); //执行方法后增强代码 System.out.println("原方法结果为:" + result);//30 //这段代码只适用于add方法,是我为了测试看能不能修改返回值 int result1 = (int)result * 2; return result1; } }
-
AOP相关术语
- 连接点:类里面哪些方法可以被增强,这些方法称为连接点
- 切入点:真正被增强的方法,称为切入点
- 通知(增强):实际增强的逻辑部分,称为通知,有五个通知种类
- 前置通知:在被增强的方法前执行
- 后置通知:在被增强的方法后执行
- 环绕通知:在被增强的方法前后执行
- 异常通知:被增强的方法抛出异常后执行
- 最终通知:最后执行
- 切面:把通知应用到切入点的过程称为切面
-
使用AOP的一些准备工作
-
Spring框架一般都是基于AspectJ实现AOP操作
AspectJ不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
-
基于AspectJ实现AOP操作
- 基于xml配置文件实现
- 基于注解方式实现(使用)
-
除了之前导入的相关jar包,我们还需要导入AOP相关依赖
-
切入点表达式
写法:execution(【权限修饰符】【返回类型】【类全路径】【方法名称】(【参数列表】))
举个例子,对com.yellowstar.dao.UserDao类下面的add方法进行增强
返回值类型可以忽略不写,参数列表用…代替
excution(* com.yellowstar.dao.UserDao.add(…))
-
-
AOP操作(基于注解实现)
-
创建一个被增强类和一个增强类的代理类
//被增强类 public class User { //被增强方法 public void add(){ System.out.println("user add......"); } } //增强代理类 public class UserProxy { public void before(){ System.out.println("before......"); } }
-
配置xml文件,并使用注解方式创建两个类的对象
<?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" 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"> <!--扫描注解--> <context:component-scan base-package="com.yellowstar"></context:component-scan> </beans>
//被增强类 @Component public class User { //被增强方法 public void add(){ System.out.println("user add......"); } } //增强代理类 @Component public class UserProxy { public void before(){ System.out.println("before......"); } }
-
为增强类添加@Aspect注解,并在xml文件中配置开启生成代理对象
//增强代理类 @Component @Aspect public class UserProxy { public void before(){ System.out.println("before......"); } }
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
在增强类的通知方法前,设置所对应的通知注解,使用切入点表达式
//增强代理类 @Component @Aspect public class UserProxy { //前置通知 @Before(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void before() { System.out.println("before......"); } //最终通知 @After(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void after() { System.out.println("after......"); } //后置通知 @AfterReturning(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void afterReturning() { System.out.println("afterReturning......"); } //异常通知 @AfterThrowing(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void afterThrowing() { System.out.println("afterThrowing......"); } //环绕通知 @Around(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void around(ProceedingJoinPoint point) throws Throwable { System.out.println("add之前around......"); //在此语句上的代码都是在方法执行前,下面的代码在方法执行后 point.proceed(); System.out.println("add之后around......"); } } 执行结果: add之前around...... before...... user add...... add之后around...... after...... afterReturning......
以上已经完成了AOP操作,关于以上操作,还有两个细节可以优化
-
对相同切入点进行抽取
我们发现这些通知的切入点都一样那么我们可以将他抽取出来
创建一个方法,配置上Pointcut注解,将切入点表达式配置上,在需要相同切入点的地方调用方法即可
//增强代理类 @Component @Aspect public class UserProxy { @Pointcut(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void point(){} //前置通知 @Before(value = "point()") public void before() { System.out.println("before......"); } }
-
有多个增强方法,如何设置执行的先后顺序
使用注解Order(数字值),数值越小代表优先级越高
//增强代理类1 @Component @Aspect @Order(3) public class UserProxy { @Pointcut(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void point(){} //前置通知 @Before(value = "point()") public void before() { System.out.println("before......"); } } //增强代理类2 @Component @Aspect @Order(0) public class UserProxyMax { @Before(value = "execution(* com.yellowstar.spring5.AopAnnocation.User.add(..))") public void before(){ System.out.println("UserProxyMax before..."); } } //如上设置两个增强代理类的order,UserProxyMax中的增强方法会优先执行
-
-
AOP操作(基于xml配置文件实现)
-
创建一个被增强类和一个增强类
//被增强类 public class Book { public void buy(){ System.out.println("book buy..."); } } //增强类 public class BookProxy { public void before(){ System.out.println("BookProxy before..."); } }
-
在xml文件中进行如下配置
<!--创建两个类的对象--> <bean id="book" class="com.yellowstar.spring5.AopXml.Book"></bean> <bean id="bookProxy" class="com.yellowstar.spring5.AopXml.BookProxy"></bean> <!--配置AOP增强点--> <aop:config> <!--配置切入点--> <aop:pointcut id="p" expression="execution(* com.yellowstar.spring5.AopXml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--配置方法--> <aop:before method="before" pointcut-ref="p"></aop:before> </aop:aspect> </aop:config>