一、AOP面向方面编程
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。OP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
AOP带来了新的编程革新。 使得软件设计更加细化,代码的编写顺序不再决定其运行顺序,诞生动态组件概念,结合IOC DI模式,可以实现软件编写和运行的最大灵活性,自诞生以来已经成为Java领域主流模式,典型框架有Spring或AspectJ框架。
在面向对象系统中,我们经常需要一些任务活动,如记录,交易的安全性,缓存等,这些活动是必要的,但不是业务逻辑的一部分,被称为"横切关注点"。
(横切关注==跨整个系统的常用功能)
从业务逻辑中分离横切关注点,为写出一个精心设计的解耦系统迈出了一大步。让我们关注思考如何处理横切关注点的分离。
1、继承Inheritance
继承的是那些通用功能,继承需要我们设计一个基类,如果我们要重用到多个地方可能需要修改这个基类。继承Inheritance == 难以在后来修改(无弹性代码)
2、委托Delegation
委托是一个处理横切关注的好办法,委托和组合分享一个通用功能,但是我们需要在很多地方调用这个委托对象,显得繁琐。委托 == 繁琐
AOP允许我们以模块化关注横向点并形成对象,称为Aspect,这样使用Aspect能够创建一个干净解耦的代码。
二、AOP相关概念
1、Concerns关注 – 这是基于功能的模块部分,有两种类型关注:. 1. 核心关注 2. 跨切面关注(Aspect). 核心关注是有关业务逻辑,比如生成工资单,让员工记录,做银行转帐。跨切面关注是配合业务的一些活动任务,如日志 缓存等。
2、Joinpoint连接点 – Joinpoint是在执行时的切入点,Aspect也就是跨切面关注的一些功能要加入主要业务功能的地方,一个连接点可以是一个被调用的方法。
3、Advice建议 – 每个Aspect都有一个目标,它的任务是什么,这个任务会被切入到哪个连接点,这些都被称为Advice. Advice能够定义Aspect什么时候执行任务,是在核心关注也就是主要业务活动的之前 之后或者前后执行?
4、Pointcut 切入点– 一个系统有很多连接点,但是并不是所有连接点都需要被选择切入Aspect的,Aspect从切入点能够获得帮助,选择哪个连接点介入。
5、Aspect方面 – Advice 和 Pointcut定义了一个方面Aspect.Advice定义了Aspect的任务和什么时候执行它,而切入点Pointcut定义在哪里具体地方切入,也就是说,Aspect定义了它是什么东西 什么时候切入和在哪里切入。
6、Target目标 – 目标是一个被切入的地方,它一般是核心关注,业务功能。
7、Proxy代理 – 当一个advice应用到目标对象时,这时一个代理对象将被创建. AOP容器创建和管理代理对象的生命周期。
8、Weaving织入 – Weaving是一个混合横向方面到目标业务对象的过程,织入可以是在编译时间,也可以在运行时间使用classload,Spring AOP缺省是在运行时间。
三、Spring AOP 简单样例
Spring 支持 Aspect J 的注解式方面编程。
下面通过一个例子展示Spring 的 AOP 的使用。
1、新建maven项目,编辑pom.xml 使之支持Spring AOP
<!-- 添加spring framework 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- spring aop 支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- aspectj 支持 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
2、编写拦截规则
/**
*
*/
package com.freesky.spring_aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 拦截规则的注解
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
3、编写使用注解的被拦截类
package com.freesky.spring_aop;
import org.springframework.stereotype.Service;
/**
*
* 使用注解的被拦截类
*
*/
@Service
public class DemoUseAnnotationService {
@Action(name="注解式拦截的add操作")
public void add() {}
}
4、编写使用方法的被拦截类
package com.freesky.spring_aop;
import org.springframework.stereotype.Service;
/**
*
* 使用方法规则被拦截类
*
*/
@Service
public class DemoUseMethodService {
public void add() {}
}
5、编写切面
package com.freesky.spring_aop;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
*
* 切面类
*
*/
@Aspect // 这里声明这是一个切面
@Component // 这里让此切面成为Spring容器的管理的一个Bean
public class MyAspect {
// 此处声明切点
@Pointcut("@annotation(com.freesky.spring_aop.Action)")
public void annotationPointcut() {};
// 这里声明一个建言,并使用 @PointCut 定义的切点
@After("annotationPointcut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
// 通过反射获取注解上的属性,然后做相关的操作
System.out.println("注解式拦截" + action.name());
}
// 声明一个建言,此建言直接使用拦截规则作为参数
@Before("execution(* com.freesky.spring_aop.DemoUseMethodService.*(..))")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截" + method.getName());
}
}
6、编写配置类:
package com.freesky.spring_aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.freesky.spring_aop")
@EnableAspectJAutoProxy // 这里开启 Spring 对 AspectJ 代理的支持
public class AopConfig {
}
7、运行测试代码
package com.freesky.spring_aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Spring AOP 测试程序
*
*/
public class App
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
DemoUseAnnotationService demoUseAnnotationService = context.getBean(DemoUseAnnotationService.class);
DemoUseMethodService demoUseMethodService = context.getBean(DemoUseMethodService.class);
demoUseAnnotationService.add();
demoUseMethodService.add();
context.close();
}
}
四、结束语
通过样例可以很明显的看出面向切面(方面)的优势,相比与OOP,在拦截日志方面优势明显。
关于AOP,更加深入的内容需要继续学习研究,深刻理解内部原理,就能举一反三了。
参考:
https://www.jdon.com/aop.html
《JavaEE开发的颠覆者 Spring Boot实战》