AOP:面向切面,在不修改源码的情况下增加新的功能
1.底层使用动态代理,有两种情况:
1.有接口情况,使用JDK的动态代理,创建接口实现类代理对象,增强类方法(java基础有例子)
2.无接口,使用CGLIB动态代理,创建子类的代理对象,增强类方法
spring会根据要代理的类是否有接口动态选择代理方式
2.AOP术语
1.连接点
类中哪些方法可以被增强,这些方法就叫做连接点
2.切入点
实际上真正被增强的方法
3.通知(增强)
1.方法中实际增强的逻辑部分,也就是在原来基础上新增加的部分
2.通知有多种类型:
前置通知:在原方法之前执行
后置通知:在原方法之后执行
环绕通知:在原方法前后都执行
异常通知:在原方法出异常后执行
最终通知:类似于finally,即有无异常都会执行
4.切面
是一个动作,把通知应用到切入点的过程
3.AOP操作(准备)
1.spring一般是基于AspectJ实现AOP操作
AspectJ是独立的AOP框架,一般是AspectJ和Spring一起进行AOP操作
2.AspectJ实现AOP
3.引入依赖
4.切入点表达式
1.作用:知道对哪个类的哪个方法进行增强
2.语法:
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )
例1:对add方法增强
execution( com.dao.BookDao.add(..)):权限修饰可以省略,表示任意返回类型,(..)表示参数
例1:对BookDao中所有方法增强
execution( com.dao.BookDao.(..))
4.AOP操作(AspectJ注解)
1.创建类,定义方法
public class User {public void add(){System.out.println();}}
2.创建增强类(要增强的逻辑)
public class UserProxy {/*** 前置通知*/public void before(){System.out.println("before");}}
3.进行通知的配置
1.开启注解扫描
<!-- 开启注解扫描--><context:component-scan base-package="com.spring5.aopAnnotation"/>
2.使用注解创建对象
@Componentpublic class User {public void add(){System.out.println();}}
@Componentpublic class UserProxy {/*** 前置通知*/public void before(){System.out.println("before");}}
3.增强类上添加@Aspect注解
@Aspect@Componentpublic class UserProxy {/*** 前置通知*/public void before(){System.out.println("before");}}
4.开启生成代理对象
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 除了注解扫描的context名称空间外,还有个aop名称空间用于AspectJ实现AOP操作--><!-- 开启注解扫描--><context:component-scan base-package="com.spring5.aopAnnotation"/><!-- 开启AspectJ自动生成代理对象--><aop:aspectj-autoproxy/></beans>
4.配置不同类型的通知
/*** 前置通知*///before注解表示作为前置通知,值使用切入点表达式配置@Before(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void before() {System.out.println("before");}/*** 后置通知*/@AfterReturning(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void afterReturning() {System.out.println("afterReturning");}/*** 最终通知,注意和@AfterReturning区别*/@After(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void after() {System.out.println("after");}/*** 异常通知*/@AfterThrowing(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void afterThrowing() {System.out.println("afterThrowing");}/*** 环绕通知*/@Around(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕之前...");//执行被增强的方法proceedingJoinPoint.proceed();System.out.println("环绕之后...");}
测试:
/*** 测试AOP*/@Testpublic void testAopAnno(){ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");//增强的哪个类,就获得哪个类的对象User user = context.getBean("user", User.class);user.add();}
5.抽取公共切入点
/*** 抽取公共切入点*/@Pointcut(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void publicPoint(){}/*** 前置通知*///调用publicPoint()配置切入点@Before(value = "publicPoint()")public void before() {System.out.println("before");}
6.多个增强类对同一个方法增强,设置增强类优先级
在增强类上加注解@Order(数字值),越小优先级越高
/*** 增强类2*/@Component@Aspect@Order(3)public class PersonProxy {/*** 抽取公共切入点*/@Pointcut(value = "execution(* com.spring5.aopAnnotation.User.add(..))")public void publicPoint() {}@Before(value = "publicPoint()")public void before() {System.out.println("person before");}}


