美文网首页
探秘Spring AOP

探秘Spring AOP

作者: Hey_Shaw | 来源:发表于2018-05-04 14:03 被阅读46次

编程范式概览

  • 面向过程编程
  • 面向对象编程
  • 函数式编程
  • 事件驱动编程
  • 面向切面编程

AOP是什么

  • 是一种编程范式,不是编程语言
  • 解决特定问题,不能解决所有问题
  • 是OOP的补充,不是替代

AOP的初衷

  • DRY:Don't Repeat Yourself 解决代码重复性问题
  • SoC:Separation of Concerns 解决关注点问题
    • 水平分离:展示层 --> 服务层 --> 持久层
    • 垂直分离:模块划分(订单、库存等)
    • 切面分离:分离功能性需求与非功能性需求(把非功能性需求从功能性需求中分离出来,从而实现DRY)

使用AOP的好处

  • 集中处理某一关注点/横切逻辑
  • 可以很方便地添加/删除关注点
  • 侵入性少,增强代码可读性及可维护性

AOP应用场景

  • 权限控制
  • 缓存控制
  • 事务控制
  • 审计日志
  • 性能监控
  • 分布式追踪
  • 异常处理

支持AOP的编程语言

  • Java
  • .Net
  • C/C++
  • Ruby
  • Python
  • PHP

注解:

  • @Aspect - 申明成切面类
  • @Pointcut("@annotation(AdminOnly)") - 申明表达式,拦截有AdminOnly的注解;
  • @Before("adminOnly()") - 在adminOnly()方法执行之前,执行该注解方法。

Spring AOP使用方式

  • XML配置
  • 注解方式

主要注解

  • AspectJ 注解
    • @Aspect:用来标注说明这个Java类是一个切面配置类
    • @Pointcut:描述在哪些类的哪些方法进行植入你的代码
    • Advice:你想要在这些方法的执行什么时机进行植入

Pointcut expression:切面表达式

  • designators:execution() 、 ...
  • wildcards:* / .. / +
  • operators:&& / || / !

Wildcards(通配符)

  • * :匹配任意数量的字符
  • + :匹配指定类及其子类
  • .. :一般用于匹配任意数的子包或参数

Operators(运算符)

  • && :与操作符
  • || :或操作符
  • ! :非操作符

designators

  • 匹配方法:execution()
  • 匹配注解:
    • @target()
    • @args()
    • @within()
    • @annotation()
  • 匹配包/类型
    • within()
  • 匹配对象
    • this()
    • bean()
    • target()
  • 匹配参数
    • args()

匹配包/类型

// 匹配ProductService类里头的所有方法
@Pointcut("within(com.imooc.service.ProductService)")
public void matchType(){}

// 匹配com.imooc包及子包下所有类的方法
@Pointcut("within(com.imooc..*)")
public void matchPackage(){}

匹配对象

// 匹配AOP对象的目标对象为指定类型的方法,即DemoDao的aop代理对象的方法
@Pointcut("this(com.imooc.DemoDao)")
public void thisDemo(){}

// 匹配实现IDao接口的目标对象(而不是aop代理后的对象)的方法,这里即DemoDao的方法
@Pointcut("target(com.imooc.IDao)")
public void targetDemo(){}

// 匹配所有以Service结尾的bean里头的方法
@Pointcut("bean(*Service)")
public void beanDemo(){}

参数匹配

// 匹配任何以find开头而且只有一个Long参数的方法
@Pointcut("execution(* *..find*(Long))")
public void argsDemo1(){}

// 匹配任何只有一个Long参数的方法
@Pointcut("args(Long)")
public void argsDemo2(){}

// 匹配任何以find开头的而且第一个参数为Long型的方法
@Pointcut("execution(* *..find*(Long,..))")
public void argsDemo3(){}

// 匹配第一个参数为Long型的方法
@Pointcut("args(Long,..)")
public void argsDemo4(){}

匹配注解

// 匹配方法标注有AdminOnly的注解的方法
@Pointcut("@annotation(com.imooc.demo.security.AdminOnly)")
public void annoDemo(){}

// 匹配标注有Beta的类底下的方法,要求的annotation的RetentionPolicy级别为CLASS
@Pointcut("@within(com.google.common.annotations.Beta)")
public void annoWithinDemo(){}

// 匹配标注有Repository的类底下的方法,要求的annotation的RetentionPolicy级别为RUNTIME
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void annoTargetDemo(){}

// 匹配传入的参数类标注有Repository注解的方法
@Pointcut("@args(org.springframework.stereotype.Repository)")
public void annoArgsDemo(){}

execution()表达式

格式

execution(
    modifier-pattern?   - 修饰符
    ret-type-pattern    - 返回值
    declaring-type-pattern? - 描述包名
    name-pattern(param-pattern) - 描述方法名(方法参数)
    throws-pattern? - 匹配方法抛出的异常 
)

标注?表示可省略的!

5种Advice注解

1、@Before,前置通知
2、@After(finally),后置通知,方法执行完之后
3、@AfterReturning,返回通知,成功执行之后
4、@AfterThrowing,异常通知,抛出异常之后
5、@Around,环绕通知

Advice中的参数及结果绑定

// 拦截获取方法的参数进行校验
@Before(value="annoTargetVsWithinDemo() && within(com.imooc..*) && args(userId)")
public void beforeWithArgs(JoinPoint joinPoint, Long userId){
    System.out.println("before, args:"+userId) ;
}

// 打印方法的返回值
@AfterReturning(value="annoTargetVsWithinDemo() && within(com.imooc..*)",returning="returnValue")
public void getReulst(Obgect returnValue){
    if(returnValue != null){
        System.out.println("after return, result:"+returnValue) ;
    }
}

原理概述:织入的时机

1、编译期(AspectJ)
2、类加载时(AspectJ 5+)
3、运行时(Spring AOP)

原理概述:运行时织入

  • 运行时织入是怎么实现的? 代理对象
  • 从静态代理到动态代理
    • 静态代理的缺点:每当代理的方法越多时,重复的逻辑也越多。
    • 动态代理的两类实现:基于接口代理与基于继承代理。
    • 两大实现的代表:JDK代理与Cglib代理。
  • 基于接口代理与基于继承代理
  • JDK实现要点

    • 类:java.lang.reflect.Proxy
    • 接口:InvocationHandler
    • 只能基于接口进行动态代理
  • JDK代理源码解析

    • Proxy.newProxyInstance:生成代理类的实现类
    • getProxyClass0:生成指定的代理类、ProxyClassFactory、ProxyGenerator
    • newInstance

Cglib实现

static class DemoCglibInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
        System.out.println("before...");
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("after...");
        return result ;
    }
}

public static void main(String[] args){
    Subject subject = (Subject)getProxy(RealSubject.class, new DemoCglibInterceptor());
    subject.request();
}

JDK与Cglib代理对比

  • JDK只能针对有接口的类的接口方法进行动态代理
  • Cglib基于继承来实现代理,无法对static、final类进行代理
  • Cglib基于继承来实现代理,无法对private、static方法进行代理

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。

DefaultAopProxyFactory

@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable{

    @Override
    public AopProxy creatAopProxy(AdvisedSupport config) throws AopConfigException{
        if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)){
            Class<?> targetClass = config.getTargetClass();
            if(targetClass == null){
                throw new AopConfigException("TargetSource cannot determine target class " + 
                        "Either an interface or a target is required for proxy creation.")
            }
            if(targetClass.isInterface() || Proxy.isProxyClass(targetClass)){
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }else{
            return new JdkDynamicAopProxy(config);
        }
    }
}

Spring创建代理的规则为:
1、如果目标对象实现了接口,则默认才用JDK动态代理。
2、如果目标对象没有实现接口,则才用CGLIB动态代理。
3、如果目标对象实现了接口,并指定为CGLIB代理,则使用CGLIB动态代理。

强制使用Cglib代理

@SpringBootApplication
// 强制使用cglib代理
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AopDemoApplication{
    public static void main(String[] args){
        SpringApplication.run(AopDemoApplication.class, args);
    }
}

安全校验@PreAuthorize

  • MethodSecurityInterceptor
  • PreInvocationAuthorizationAdviceVoter
  • ExpressionBasedPreInvocationAdvice

缓存@Cacheable

  • AnnotationCacheAspect
  • CacheInterceptor
  • CacheAspectSupport

相关文章

网友评论

      本文标题:探秘Spring AOP

      本文链接:https://www.haomeiwen.com/subject/kdqvlftx.html