概念

什么是AOP

  • 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

为什么要用Aop

  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 在不改变原有方法的基础添加一些功能 , 比如:

    • 日志记录
    • 性能统计
    • 安全控制
    • 事务处理
    • 异常处理等等

Aop 术语

连接点(JoinPoint)

程序执行到某个特定位置,如类开始初始化前,类初始化后,某个方法调用前/后,方法抛出异常后,一些具有边界性质的特定点就是连接点, Spring 仅支持方法级的连接点(方法执行前,方法完成后,抛出异常后)

切点(Pointcut)

从连接点的基础上引出的概念,是指特定的连接点,一个类有好多方法,每个方法又有多个连接点,则需要切点来限定一个小范围的连接点,在Spring中是使用类和方法作为连接点的查询条件

通知、增强处理(Advice)

就是指你所需要添加的功能及这个功能什么时候(通知)实现 , 比如一个业务方法需要实现日志功能 , 那么就需要专门在一个地方定义好需要做什么,然后定义什么时候执行(方法执行前?,方法完成后?,抛出异常?。。。)

Spring 切面可应用的 5 种通知类型:

  1. Before——在方法调用之前调用通知
  2. After——在方法完成之后调用通知,无论方法执行成功与否
  3. After-returning——在方法执行成功之后调用通知
  4. After-throwing——在方法抛出异常后进行通知
  5. Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

引入(introduction)

特殊的增强,为类添加一些属性和方法,这样即使一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类

切面(Aspect)

切面由切点和增强组成 , 及包括横切逻辑的定义,也包括切点的定义,

目标对象(Target)

增强逻辑的织入目标类 , 如果没有Aop,那么目标对象就要自己实现(日志记录,性能统计,安全控制,事务处理,异常处理)这些功能,那么一个方法就会变成很杂乱

织入(Weaing)

将增强添加到目标对象的具体连接点上, Spring使用动态代理织入

Aop有三种织入方式

  1. 编译期织入
  2. 类装载期织入
  3. 动态代理织入: 在运行期间为目标类添加增强生成子类的方式

Aop 实现

  • JDK动态代理

  • CGLib动态代理

代理知识总结

  • Spring使用JDK动态代理和CGLib动态代理技术在运行期织入增强,要使用JDK动态代理,目标类必须实现接口,而CGLib不对目标类作任何限制,他是通过动态生成目标类子类的方式提供代理
  • JDK在创建代理对象时的性能高于CGLib,但生成的代理对象的运行性能却比CGLib的低,如果无需频繁的创建代理对象比较适合采用CGLib动态代理技术,反之比较适合JDK动态代理技术

AOP示例

  • 创建切面类ServiceAspectj,这个方法用于定义切面,功能是打印方法的执行前的输入参数及输出结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Slf4j
@Aspect
@Component
public class ServiceAspectj {

@Pointcut(value = "execution(* org.springframework.iframe.service..*(..))")
public void pointcut() {

}

@Around("pointcut()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
String className = pjp.getSignature().getDeclaringType().getSimpleName();
String methodName = pjp.getSignature().getName();

log.info("=> [request method: {}#{}]",className, methodName);
log.info("=> [request body: {}]", JSONObject.toJSONString(pjp.getArgs()));

Object result = pjp.proceed();

log.info("=< [response method: {}#{}]",className, methodName);
log.info("=< [response result: {} ]", JSONObject.toJSONString(result));
return result;
}

}
  • 需要切的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface UserService {

User findUserByName(String userName);

}


@Service
public class UserServiceImpl implements UserService {

@Autowired
User user;

@Autowired
private RoleService roleService;

@Override
public User findUserByName(String userName) {
User user = new User(userName,18);
//Role role = roleService.findRoleByUserName(userName);
user.setRole(new Role());
return user;
}

}
  • Spring 配置文件 beans/bean.xml 添加<aop:aspectj-autoproxy/>配置,开启AOP开关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


<bean class="org.springframework.iframe.entity.User">
<property name="userName" value="shop"/>
</bean>

<!-- ComponentScanBeanDefinitionParser-->
<context:component-scan base-package = "org.springframework.iframe.*"/>

<aop:aspectj-autoproxy/>

</beans>
  • 测试类及测试结果
1
2
3
4
5
6
7
@Test
public void test1 () {
ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("beans/bean.xml");
UserService userService = xmlApplicationContext.getBean(UserService.class);
User user1 = userService.findUserByName("sd");
log.info("user1:{}", user1);
}
1
2
3
4
11:12:06,216 [INFO ] [org.springframework.iframe.aop.ServiceAspectj] - => [request method: UserService#findUserByName]
11:12:06,307 [INFO ] [org.springframework.iframe.aop.ServiceAspectj] - => [request body: ["sd"]]
11:12:06,307 [INFO ] [org.springframework.iframe.aop.ServiceAspectj] - =< [response method: UserService#findUserByName]
11:12:06,372 [INFO ] [org.springframework.iframe.aop.ServiceAspectj] - =< [response result: {"age":18,"role":{},"userName":"sd"} ]

总结

  • AOP的工作重心就是将增强应用与目标对象的连接点上,这里包括两部分内容:
    • 如何通过切点和增强定位到连接点上
    • 如何在增强中编写切面的逻辑

参考

  • 《精通Spring+4.x++企业应用开发实战》