目录
1、AOP基本概念
1、面向切片(方面)编程,利用AOP可以对业务逻辑各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
2、不通过修改源代码的方式添加新功能
2、AOP底层原理
1、AOP底层使用了动态代理
-动态代理有两种情况 :有接口和无接口情况
第一种情况,有接口情况,使用JDK动态代理
-获取代理对象需使用Proxy类要调用里面newProxyInstance方法
Object object = newProxyInstance(ClassLoader loader,class<?>[] interfaces,InvocationHandler h)
-方法有三个参数 :
第一个参数:类加载器
第二个参数:增强方法所在类,这个类实现的接口,支持多个接口
第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强方法
代码演示:
//创建UserDAO接口的实现类并重写方法 public class UserDAOImpl implements UserDAO { @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
//创建代理类 public class JDKProxy { public static void main(String[] args) { //Class类型的接口数组 Class[] interfaces = {UserDAO.class}; //需要增强的实现类对象 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); } } //创建代理对象代码 class UserDAOProxy implements InvocationHandler{ //1.把创建的是谁的代理对象,把谁传进来 //有参构造 private Object obj; public UserDAOProxy(Object object){ this.obj = object; } //增强逻辑 @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; } }
第二种情况,没有接口情况,使用CGLIB动态代理
3、AOP相关术语
1、连接点
类里那些方法可以被增强,这些方法就叫做连接点
2、切入点
实际真正被增强的方法就叫做切入点
3、通知(增强)
(1)实际增强的逻辑部分称为通知
(2)通知有多种类型:
-前置通知
-后置通知
-环绕通知
-异常通知
-最终通知
4、切面
把通知应用到切入点的过程就叫做切面
4、Spring中AOP操作
Spring框架一般都是基于AspectJ实现AOP操作
1、什么是AspectJ
-AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作的方式
(1)基于xml配置文件实现
(2)基于注解方式实现(常用)
3、想要使用AspectJ需要引入相关依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构:
execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
5、AOP操作(AOP注解)
1、在Spring配置文件中,开启注解扫描
2、使用注解创建User和UserProxy对象
3、在增强类上面添加注解@Aspect
4、在Spring配置文件中开启生成代理对象
5、配置不同类型的通知(增强类里,通知方法上方添加注解,使用切入点表达式)
6、多个增强类对一个方法进行增强可以使用@Order(数字类型数值)控制优先级,数值越小优先级越高
-添加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" 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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.atqjb.Spring5"></context:component-scan> <!--开启AspectJ生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
//完全注解开发不需要创建xml配置文件 //创建配置类 @Configuration @ComponentScan(basePackages = {"com.atqjb.Spring5"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop {}
-使用注解创建User和UserProxy对象,并使用@Aspect指定增强类t
package com.atqjb.Spring5; //被增强类 @Component public class User { public void add(){ System.out.println("add...."); } }
-在增强类里添加注解使之成为代理类
-在方法上添加注解使之成为不同增强方法
package com.atqjb.Spring5; //增强类 @Component @Aspect //生成代理对象 public class UserProxy { //前置通知 //在add方法之前执行 //@Before注解表示前置通知 @Before(value = "execution(* com.atqjb.Spring5.User.add(..))") public void before(){ System.out.println("before..."); } //最终通知,执行增强方法后执行,不管有没有异常都执行 @After(value = "execution(* com.atqjb.Spring5.User.add(..))") public void after(){ System.out.println("after..."); } //后置通知,返回值之后执行,增强方法有异常不执行 @AfterReturning(value = "execution(* com.atqjb.Spring5.User.add(..))") public void afterReturning(){ System.out.println("afterReturning..."); } //异常通知 @AfterThrowing(value = "execution(* com.atqjb.Spring5.User.add(..))") public void afterThrowing(){ System.out.println("afterThrowing..."); } //环绕通知,在增强方法之前之后都执行 @Around(value = "execution(* com.atqjb.Spring5.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕前..."); //被增强的方法执行 proceedingJoinPoint.proceed(); System.out.println("环绕后..."); } }
优化:相同切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.atqjb.Spring5.User.add(..))")
public void pointdemo(){}
//前置通知
//在add方法之前执行
//@Before注解表示前置通知
@Before(value = "pointdemo()")//调用切入点抽取方法即可
public void before(){
System.out.println("before...");
}
6、AOP操作(AOPXML配置)
<!--创建对象-->
<bean id="book" class="com.atqjb.Spring.Book"/>
<bean id="bookProxy" class="com.atqjb.Spring.BookProxy"/>
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.atqjb.Spring.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>