spring aop 概念

概览

编程范式:面向过程,面向对象,函数式编程,事件驱动编程,面向切面
面向切面解决特定问题是面向对象的补充,使非功能性需求与功能性需求分离
应用:权限控制,缓存控制,事务控制,审计日志,性能监控,分布式追踪,异常处理

Spring AOP是什么?你都拿它做什么?

使用

主要注解

AspectJ注解:@Aspect, @Pointcut, Advice

切面表达式(Ponitcut expression)

config使用@Aspect, @Component注解

  1. desiganators(指示器):
    匹配方法: execution()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     // ? 表示可以省略
    execution(
    modifier-pattern? //修饰符
    ret-type-pattern //返回值
    declaring-type-pattern? //描述包名?
    name-pattern(param-pattern) //方法名,方法参数
    throws-pattern? //异常
    )

    @Pointcut("execution(public * com.example.*Service.*(..) throws java.lang.IllegalAccessException)")
    public void method(){}

    匹配注解:
    @target():类注解
    @args():参数注解
    @within():类注解
    @annotation():方法注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //AdminOnly.java
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface AdminOnly {
    }
    //AspectConfig.java
    @Aspect
    @Component
    public class AspectConfig {

    @Pointcut("@annotation(AdminOnly)")
    public void method(){
    }
    @Before("method()")
    public void before(){
    System.out.println("handle");
    }
    }

    匹配包/类型: within()

    1
    2
    3
    4
    5
    6
    //匹配ProductService类里的所有方法
    @Pointcut("within(com.example.service.ProductService)")
    public void matchType(){ }
    //匹配com.example包及子包下所有的类方法
    @Pointcut("within(com.example..*)")
    public void matchPackage(){ }

    匹配对象: this(), bean(), target()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class DemoDao implements IDao{ }
    //匹配DemoDao的aop代理对象的方法 ==》 aop代理是指?
    @Pointcut("this(com.example.DemoDao)")
    public void beanDemo(){ }
    //匹配实现IDao接口的目标对象的方法
    @Pointcut("target(com.example.IDao)")
    public void beanDemo(){ }
    //匹配以Service结尾的bean里的方法
    @Pointcut("bean(*Service)")
    public void beanDemo(){ }

    匹配参数: args(), excution()

    1
    2
    3
    4
    @Pointcut("execution(**..find*(Long))") //只有一个参数
    @Pointcut("execution(**..find*(Long,..))") //第一个参数是Long
    @Pointcut("args(Long)")
    @Pointcut("args(Long,..)")
  2. wildcards(通配符):
    *匹配任意数量的字符
    +匹配指定类及其子类
    ..匹配任意数的子包或参数

  3. operators(运算符):
    &&与,||或,!

5种主要的Advice

@Before 前置通知
@After 后置通知,方法执行完之后
@AfterReturning 返回通知,成功执行之后,有returning数据
@AfterThrowing 异常通知,抛出异常之后
@Around 环绕通知
上下文:ProceedingJoinPoint

原理

设计:代理模式,职责链模式
实现:JDK实现,Cglib实现
注意事项-AOP不能拦截内部调用

代理

静态代理: 实现同一接口,调用目标对象的方法。
动态代理:基于接口代理,基于继承代理
InvocationHandler(JDK), MethodInterceptor(cglib-github)
对比:

  1. JDK只能针对有接口的类的接口方法进行动态代理
  2. cglib基于继承,因此无法对static,final方法进行代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Subject {
public void request();
public void hello();
}

public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("subject request");
}

@Override
public void hello() {
System.out.println("hello");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// jdk
public class JdkProxySubject implements InvocationHandler {
private RealSubject realSubject;

public JdkProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = null;
try {
result = method.invoke(realSubject,args);
}catch (Exception e){
System.out.println("ex:"+e.getMessage());
throw e;
}finally {
System.out.println("after");
}
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// cglib
public class DemoMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib before");
Object result = null;
try{
result = methodProxy.invokeSuper(o,objects);
}catch (Exception e){
System.out.println("cglib ex:"+e.getMessage());
throw e;
}finally {
System.out.println("cglib after");
}

return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Client {
public static void main(String[] args) {
// jdk
Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(),
new Class[]{Subject.class},
new JdkProxySubject(new RealSubject()));

subject.request();
subject.hello();

// cglib
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new DemoMethodInterceptor());
Subject subject1 = (Subject) enhancer.create();
subject1.hello();
subject1.request();
}
}

顺带一提IOC与DI

IoC可以认为是一种全新的设计模式。
控制反转(Inversion of Control),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection)。
现有的框架实际上使用以下三种基本技术的框架执行服务和部件间的绑定:

  1. (基于接口): 可服务的对象需要实现一个专门的接口,该接口提供了一个对象,可以重用这个对象查找依赖(其它服务)。早期的容器Excalibur使用这种模式。
  2. (基于setter): 通过JavaBean的属性(setter方法)为可服务对象指定服务。HiveMind和Spring采用这种方式。
  3. (基于构造函数): 通过构造函数的参数为可服务对象指定服务。PicoContainer只使用这种方式。HiveMind和Spring也使用这种方式

简单情况:网络请求直接传对象需要的参数,然后spring直接用类来接收,一个类实体就被实例化了。
IOC可以理解为把Spring当做一个容器,用来管理各种service、dao等。不用再去手动new。