SpringAOP学习中
springAOP面向切面编程,非编程语言,是编程范式,只能解决特定问题,是OOP的补充而非替代。
初衷:解决代码重复性问题;
关注点分离(水平分离:分展示层、服务层、持久层;垂直分离:按功能模块;切面分离:分离功能性需求和非功能性需求)
使用AOP的好处:
集中处理某一关注点/横切逻辑
方便添加/删除关注点
侵入性少,增强代码可读性与可维护性
AOP的应用场景:
权限控制、缓存控制、事务控制、审计日志、性能监控、分布式追踪、异常处理。
支持AOP的编程语言:
JAVA .net python c/c++... ruby...
例如:举一个场景的例子,添加或删除的操作必须由管理员完成,这是两个方法,用普通方法,是添加和删除方法中,要先加【判断是否为管理员的代码】,运用AOP的方式,则是把【判断是管理员】的代码切出来
SpringAOP的使用方式
1、XML配置
2、注解方式在java文件中
@Aspect:在类前,用于说明该类是切面的配置的类
@Pointcut:描述在哪些类/哪些方法植入你的代码
Advice:你想在以上方法的什么时候进行植入,之前?之后?
@Component:让这个类给spring托管
一、切面表达式Pointcut expression:
designators:指示器,通过什么方式去匹配想要的java的类的方法
image.png
wildcards:通配符 (*:任意数量; +:匹配指定类及子类; 。。:匹配任意数的子包或参数
operators:运算符,组合(&&;||;!)
(一)designators:指示器
1、within表达式:限定在哪个类或哪个包
image.png
2、对象匹配:
image.png
常默认this和target一样
3、参数匹配:
两种方法:定义execution方法(要返回值啥的,麻烦)和args方法
4、注解匹配:4个,一个是匹配方法级别,2个匹配类级别,一个匹配参数级别
image.png
1匹配注解级别,2、3匹配类级别,一般默认相同;4匹配方法级别
5、execution表达式:
修饰符pattern
返回值类型pattern
描述包名
描述方法名(描述参数)
匹配方法抛出异常
(1 3 5可省略)
二、Advice注解
@Before:前置通知,方法前执行
@After(finally):后置通知,方法执行完之后
@AfterReturning:返回通知,成功执行之后,有返回值
@AfterThrowing:异常通知,抛出异常之后
@Around:环绕通知,万能注解
image.png
SpringAOP实现原理:
aop织入时机:
1、编译期(AspectJ)
2、类加载时(AspectJ 5+)
3、运行时(Spring AOP)
运行时织入:代理对象
【代理:
静态代理:
动态代理:基于接口代理/基于继承代理】
设计模式:代理模式、责任链模式
一、代理模式(静态代理)
委托类和代理类实现同一接口,并写同一个方法,代理类中有委托类的一个实例,在其方法中,还是用委托类的实例来完成这个方法。
image.png
缺点:重复方法;
二、动态代理:
两类实现:基于接口/基于继承代理
两类实现代表:JDK代理与Cglib代理
1、JDK代理:
通过类java.lang.reflect.Proxy动态生成代理类
代理类需要实现InvocationHandler接口(接口有invoke方法,代理类中还需引用委托类对象)
jdk代理只能基于接口进行动态代理
优点:如果有多个方法,也无需在代理类中重写,只需在接口中写
2、JDK代理源码解析
image.png
方法,去调用其他3个方法,
最后生成字节码用java反射生成实例
3、Cglib动态代理:通过继承的方式来实现代理
image.png
4、spring对两种实现的选择:
目标对象实现接口,默认采用JDK代理;
目标对象无实现接口,采用cglib代理;
目标对象实现了接口但强制cglib代理,则cglib
二、责任链模式:
多个AOP链式调用 ,
1、抽象类Handler中有HanlerProcess()抽象方法,需要实现类实现,同时实现类又组合了Handler,通过这种successor的判断来实现链式调用
image.png
successor用于判断接下来还有没有人去处理。
对象存在,则接下来调用链式的execute()
2、创建多个继承Hanler抽象类的类,然后在各自的handlerProcessor()执行各自的业务逻辑
2、客户端中设置多个类的调用关系,只调用第一个的execute()
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
hanlerA.execute()
所有代码:
Hanler抽象类:
public abstract class Handler {
private Handler successor;
public Handler getSuccessor() {
return successor;
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public void execute(){
handlerProcess();
if(successor!=null){
successor.execute();
}
}
protected abstract void handlerProcess();
}
客户端Client类,其中有三个内部静态类ABC继承hanler类
public class Client {
static class HandlerA extends Handler{
@Override
protected void handlerProcess() {
System.out.println("hanler by a");
}
}
static class HandlerB extends Handler{
@Override
protected void handlerProcess() {
System.out.println("hanler by b");
}
}
static class HandlerC extends Handler{
@Override
protected void handlerProcess() {
System.out.println("hanler by c");
}
}
public static void main(String[] args) {
Handler handlerA = new HandlerA();
Handler handlerB = new HandlerB();
Handler handlerC = new HandlerC();
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
handlerA.execute();
}
}
缺点:在于必须手动把链式调用的链式关系写出来,比较麻烦。
所以进行改进,将上面那块进行封装成一个类
Chain中有一个内部List<handler>,然后把所有的handler存进去,内部实现游标的跳转,不断调用下一个。
ChainHandler:
public abstract class ChainHandler {
public void execute(Chain chain){
handleProcess();
chain.proceed();//调用下一个游标
}
protected abstract void handleProcess();
}
Chain:
import java.util.List;
public class Chain {
private List<ChainHandler> handlers;
private int index=0;
public Chain(List<ChainHandler> handlers){
this.handlers=handlers;
}
public void proceed(){
if(index>=handlers.size()){
return;
}
handlers.get(index++).execute(this);
}
}
ChainClient:
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
public class ChainClient {
static class ChainHandlerA extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by chain a");
}
}
static class ChainHandlerB extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by chain b");
}
}
static class ChainHandlerC extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by chain c");
}
}
public static void main(String[] args){
List<ChainHandler> handlers= Arrays.asList(
new ChainHandlerA(),
new ChainHandlerB(),
new ChainHandlerC()
);
Chain chain=new Chain(handlers);
chain.proceed();
}
}
springAOP也是使用这种Chain方式,用游标的方式。
使用springAOP注意事项:
1、不把重要的业务逻辑放到AOP中处理,容易被忽略,应该放比较通用的业务
2、无法拦截static、final、private方法
3、无法拦截内部方法调用