这里基本上都是B站上孙哥Spring的视频,还有平时写代码总结来的。
AOP本质上是原始功能加上额外功能,使用的是代理模式的思想,一个典型的例子是:
- 房东(原始功能)只想出租房子,但是嫌自己找租客太麻烦
- 于是找到中介,中介替他去找租客(额外功能)
静态代理:
最开始是使用静态代理的模式:额外功能实现目标类相同的接口。缺点是要为每个原始类写一个代理类,并且修改扩展功能比较麻烦
public class User {}
public interface UserService {
void test(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void test(User user) {
System.out.println("原始类的test方法》》》》》》》》》");
}
}
/**
* 静态代理类编码实现
*/
public class UserServiceProxy implements UserService { // 实现原始类相同的接口
private UserService userService = new UserServiceImpl(); // 原始类
@Override
public void test(User user) {
System.out.println("额外功能》》》》》》》"); // 额外功能
userService.test(user); //调用原始类的原始功能
}
}
动态代理
针对静态代理出现的问题,有了Spring的动态代理开发。
步骤:
1,创建原始方法
2,定义额外功能
3,定义切入点(切入点表达式比较重要)
4,组合(把2,3对应起来)
使用Spring开发AOP有两种方式,XML和注解,
XML方式
(暂时不写了,除了最开始学习,平时工作也基本没用过这种方式)
注解方式
1,原始方法
这个没什么说的,就是我们原来的方法
2,额外功能+切入点+组合
@Component
@Aspect // 1.表示这是一个切面类
public class testAspect {
@After(value = "execution(* com.mz.testSpring.controler.testSpringControler.sayHello(..))") //2.切入点表达式,标明原始方法位置
public void afterMethod(JoinPoint joinPoint){
System.out.println("I'm fine ,thank you");
}
}
这里注意1和2两个地方,1表示这是一个切面类,2处的切入点表达式相当于XML方式的步骤3,4,也就是切入点和组合两部分组合起来了,加上下面的额外功能,相当于我们只要实现了这个切面类就实现了AOP编程的全部步骤,这样比XML方式方便的多,所以我在实际编码过程中XML方式一次也没有用过,不过想深入了解AOP的原理(其实就是上面代理模式+spring配置文件+反射那一套),还是需要了解下XML方式的。
@After 后置通知,表示额外功能是在原始方法执行完之后执行的,与他相似的注解还有
@Before 前置通知,原始方法之前执行
@Around 环绕通知,在原始方法之前,之后都执行
@AfterThrowing 异常通知,发生异常之后执行,我一般用它来手动回滚操作
切入点表达式:
-
execution()
:切入点函数 -
* *(..)
:切入点表达式
execution是切入点函数,也是功能最强大的,与他相似的还有args,within等(这几个我都没怎么用过,感觉一个execution就足够了)
切入点表达式用来将额外功能定位到原始功能上,具体含义如下
* * (..) --> 所有方法
* ---> 修饰符 返回值
* ---> 方法名
() ---> 参数表
.. ---> 对于参数没有要求 (参数有没有,参数有⼏个都行,参数是什么类型的都行)
一个例子:
* com.mz.testSpring.controler.testSpringControler.sayHello(..)
表示我不在意返回值,额外功能对com.mz.testSpring.controler.testSpringControler类的sayHello方法生效,这里testSpringControler还可以换成*,表示controler包下的所有类,只要有sayHello方法,就对他的sayHello方法生效,或者sayHello也写成*,相当于包切入点,以此类推。最后一个参数,可以填写具体的参数类型。此外切入点表达式支持逻辑运算,如下
@After(value = "execution(* com.mz.testSpring.controler.testSpringControler.sayHello(..))" +
" || execution(* com.mz.testSpring.controler.testSpringControler.sayGoodBye(..))")
public void afterMethod(JoinPoint joinPoint){
System.out.println("I'm fine ,thank you");
}
测试
GET http://localhost:8080/test/sayGoodBye?name=Africa frends
Accept: application/json
###
GET http://localhost:8080/test/testAnno?name=Indian frends
Accept: application/json
动态代理的底层:
-
JDK:通过 实现接口,做新的实现类 创建代理对象
-
Cglib:通过 继承父类,做新的子类 创建代理对象
基于注解的AOP开发可以通过设置配置文件来实现切换
<aop:aspectj-autoproxy proxy-target-class="true"/>
下面是B站孙哥spring的一张总结图,感觉不错,粘过来了