Spring(五)-AOP
一、AOP简介
1.1、什么是 AOP
1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
3)使用例子说明 AOP
二、AOP原理说明
2.1、JDK动态代理原理
1)创建接口,定义方法
package com.dianchou.spring.proxy; public interface UserDao { public int add(int a,int b); public String update(String id); }
2)创建接口实现类,实现方法
package com.dianchou.spring.proxy; public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { System.out.println("add方法执行了....."); return a+b; } @Override public String update(String id) { System.out.println("update方法执行了....."); return id; } }
3)使用 Proxy 类创建接口代理对象
package com.dianchou.spring.proxy; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class JDKProxy { public static void main(String[] args) { //增强方法所在的类,这个类实现的接口,支持多个接口 Class[] interfaces = {UserDao.class}; //创建接口实现类代理对象 //可以创建匿名内部类 /** * Proxy.newProxyInstance方法有三个参数: * 第一参数,类加载器 * 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口 * 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分 */ // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // return null; // } // }); UserDaoImpl userDao = new UserDaoImpl(); //创建接口实现类代理对象 UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int result = dao.add(1, 2); System.out.println("result:"+result); } } //创建代理对象代码,实现InvocationHandler接口 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(obj, args); //方法之后 System.out.println("方法之后执行...."+obj); return res; } }
三、AOP相关术语
3.1、连接点
类里面那些方法可以被增强,这些方法叫做连接点
3.2、切入点
实际被真正增强的方法,叫做切入点
3.3、通知
实际被增强的逻辑部分叫做通知
通知的种类
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
3.4、切面
把通知应用到切入点过程
四、AOP操作
4.1、准备工作
1)AspectJ 介绍
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)
2)引入AOP相关依赖
3)切入点表达式介绍
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强 execution(* com.atguigu.dao.BookDao.add(..)) 举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强 execution(* com.atguigu.dao.BookDao.* (..)) 举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强 execution(* com.atguigu.dao.*.* (..))
4.2、AOP操作(基于注解)
1)创建被增强的类,并定义方法
package com.dianchou.spring.aop; import org.springframework.stereotype.Component; /** * @author lawrence * @create 2020-07-08 14:24 */ //被增强的类 @Component public class User { public void add(){ // int i = 10 / 0; System.out.println("User add ..."); } }
2)创建增强类(编写增强逻辑)
package com.dianchou.spring.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @author lawrence * @create 2020-07-08 14:25 */ //增强的类 @Component @Aspect //生成代理对象 public class UserProxy { //前置通知 //@Before 注解表示作为前置通知 @Before(value = "execution(* com.dianchou.spring.aop.User.add())") public void before(){ System.out.println("UserProxy before ..."); } //后置通知(返回通知) @AfterReturning(value = "execution(* com.dianchou.spring.aop.User.add())") public void afterReturning(){ System.out.println("UserProxy afterReturning ..."); } //最终通知 @After(value = "execution(* com.dianchou.spring.aop.User.add())") public void after(){ System.out.println("UserProxy after ..."); } //异常通知 @AfterThrowing(value = "execution(* com.dianchou.spring.aop.User.add())") public void afterThrowing(){ System.out.println("UserProxy afterThrowing ..."); } //环绕通知 @Around(value = "execution(* com.dianchou.spring.aop.User.add())") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("UserProxy around 之前..."); proceedingJoinPoint.proceed(); System.out.println("UserProxy around 之后..."); } }
3)配置文件
<?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:component-scan base-package="com.dianchou.spring.aop"></context:component-scan> <!-- 开启 Aspect 生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
4)测试
@Test public void testAOP(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("user", User.class); user.add(); }
当出现异常时:
4.3、相关细节
1)相同的切入点抽取
package com.dianchou.spring.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @author lawrence * @create 2020-07-08 14:25 */ //增强的类 @Component @Aspect //生成代理对象 public class UserProxy { @Pointcut(value = "execution(* com.dianchou.spring.aop.User.add())") public void pointdemo(){} //前置通知 //@Before 注解表示作为前置通知 @Before(value = "pointdemo()") public void before(){ System.out.println("UserProxy before ..."); } //后置通知(返回通知) @AfterReturning(value = "pointdemo()") public void afterReturning(){ System.out.println("UserProxy afterReturning ..."); } //最终通知 @After(value = "pointdemo()") public void after(){ System.out.println("UserProxy after ..."); } //异常通知 @AfterThrowing(value = "pointdemo()") public void afterThrowing(){ System.out.println("UserProxy afterThrowing ..."); } //环绕通知 @Around(value = "pointdemo()") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("UserProxy around 之前..."); proceedingJoinPoint.proceed(); System.out.println("UserProxy around 之后..."); } }
2)有多个增强类多同一个方法进行增强,设置增强类优先级
在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component @Aspect @Order(1) public class PersonProxy
4.4、完全使用注解开发
1)创建配置类,不需要创建xml配置文件
@Configuration @ComponentScan(basePackages = {"com.dianchou.spring.aop"}) @EnableAspectJAutoProxy public class Config { }
2)测试
@Test public void testAOP2(){ ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); User user = context.getBean("user", User.class); user.add(); }
4.5、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"> <!--创建bean--> <bean id="user" class="com.dianchou.spring.xml.User"></bean> <bean id="userProxy" class="com.dianchou.spring.xml.UserProxy"></bean> <!--配置aop增强--> <aop:config> <!--配置切入点--> <aop:pointcut id="p" expression="execution(* com.dianchou.spring.xml.User.add())"/> <!--配置切面--> <aop:aspect ref="userProxy"> <aop:before method="before" pointcut-ref="p"></aop:before> </aop:aspect> </aop:config> </beans>
作者:Lawrence
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
扫描上面二维码关注我
如果你真心觉得文章写得不错,而且对你有所帮助,那就不妨帮忙“推荐"一下,您的“推荐”和”打赏“将是我最大的写作动力!
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.