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.使用注解创建对象
@Component
public class User {
public void add(){
System.out.println();
}
}
@Component
public class UserProxy {
/**
* 前置通知
*/
public void before(){
System.out.println("before");
}
}
3.增强类上添加@Aspect注解
@Aspect
@Component
public 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.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名称空间用于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
*/
@Test
public 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");
}
}