BinaryTom

导航

使用SpringAOP

面向切面编程

Aspect Oriented Programming(AOP)面向切面编程是一种范式和思想,并不是特指某一种编程语言。下面这句话是知乎上的一个总结:

在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

可以看到,将某一种通用的非功能代码,从业务逻辑中剥离,形成一个切面并在指定的时机切入到指定位置上的思想,就是AOP得思想。同样,减少重复代码、专注业务、分离功能代码和非功能代码就是AOP的主要职责。SpringAOP框架可以在运行时将切面代码植入切点,在功能代码周围切入非功能代码,达到不侵入业务代码的情况下,完成指定操作。可以说,AOP是OOP的延续和补充,大锤砸墙、小锤抠缝,配合好了才能达到想要的效果。

应用场景

  1. 权限控制
  2. 缓存控制
  3. 事物控制
  4. 审计日志
  5. 性能监控
  6. 分布式追踪
  7. 异常处理

AOP的主要应用场景就是在某一业务逻辑的前后(切入点),插入一些与业务逻辑无关却必要的工作(切面)。想象一下在某一层方法前后,都添加调用该方法的Log信息,以往的做法会在每个方法前后分别添加一行log.info(...),像这样:

funA (args) {
    log.info(funA start);
    {funAbody}
    log.info(funA end);
}

可是这样会产生大量重复代码,因为每个方法前后都需要添加这两行代码。

Don’t Repeat Yourself.

这是在我们常听到的一句话,也是我们义不容辞的使命。在学习面向对象后,就会想,应该可以把这个重复的代码可以抽象到父类里面,子类实现父类的抽象方法,像这样:

abstract class BaseClass {
    fun (args) {
        log.info(funA start);
        funA(args);
        log.info(funA end);
    }
    abstract funA(args);
}

这样代码是少了,可是使用的灵活性上面收到了很大的限制。我们必须要继承父类才可以实现方法,同时方法调用时也只能通过父类的包装方法来完成调用。在使用的时候就会很不方便。于是,面向切面就出现了。AOP解决此类问题的方法是把这些与方法逻辑无关,却对整体有帮助的代码,抽象到一个切面中,切入到每个方法调用的前后。这样,这些重复的代码成功的完成了抽象,剩下的就只需要在合适的切入点将这些切面切入到方法调用前后。

AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

SpringAOP

SpringAOP注解

SpringAOP使用示例

想象如下场景:想要对Service的方法调用前,对参数进行校验。接下来通过一下的代码,来看一下在SpringAOP中怎么将校验的代码织入到制定的位置:

/**
 * 定义一个切面,切点和要织入的方法
 * 1 使用@Conponent将类托管给Spring (必须要有,不然不会织入切面代码)
 * 2 使用@Aspect注解声明切面
 * 3 使用@Pointcut  声明切点(代码织入点)
 * 4 声明Advice: 注解声明织入代码时机(@After,@Before,@Around,@AfterThrowing,@AfterReturning)
 *              方法体内写具体的织入代码
 */
@Component
@Aspect
public class ParamCheck {

    @Pointcut("within(com.lieh666.cloud.aop..*Service*)")
    public void matchPak(){}

    @Before(value = "matchPak()")
    public void checkParamBefore() {
        System.out.println("####check param before");
    }

    @After(value = "matchPak()")
    public void checkParamAfter() {
        System.out.println("####check param after");
    }
}

/**
 * 定义一个被切入的类,此次为Service
 */
@Service
public class UserService {
    public void sayHi (String name) {
        System.out.println("hi," + name);
    }
}

/**
 * 实现一个Test来测试是否织入成功
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {SpringAOPService.class})
public class UserServiceTest {
    @Autowired
    UserService userService;

    @Test
    public void sayHi() throws Exception {
        userService.sayHi("lieh");
    }

}

原理

AOP主要是基于动态代理来实现的,同时AOP也是动态代理模式的一种典型应用。Spring在此基础上加以扩展,添加了Pointcut、Advisor等一些接口使其更加灵活。

SpringAOP基于以下两种实现方式:
1. jdk动态代理
2. Cglib动态代理

SpringAOP对两种代理方式的选择策略如下:
1. 如果目标对象实现了接口,则默认选择JDK动态代理
2. 如果目标对象没有实现接口,则采用Cglib进行代理
3. 如果目标对象实现了接口,且强制Cglib代理,则使用Cglib代理

AOP的主要思想,就是拆分、解耦功能模块和非功能模块。并且提高了非功能模块代码的复用。

问题及注意事项

1、区分业务逻辑代码和非业务逻辑代码,不要将业务逻辑代码放在AOP中,不然会导致业务功能不清晰。

2、不能代理内部方法调用,内部调用会使用this关键字,而不是生成的代理类。

3、不能代理private、final、static方法。

参考文章:
- JavaWeb过滤器.监听器.拦截器
- 什么是面向切面编程AOP? - 知乎
- java动态代理原理及解析

posted on 2018-02-02 15:39  BinaryTom  阅读(137)  评论(0编辑  收藏  举报