目录
一、引入AOP
二、核心AOP概念和术语
三、切点表达式
四、Spring实现AOP
(一)@AspectJ的支持
1 、基于注解开发
1.1 引入依赖
1.2 实现目标类
1.3 定义切面类(日志管理)
1.4 将目标类和切面类纳入Spring容器
1.5 开启组件扫描、自动代理
1.6 测试
2、基于XML方式开发
(二) 基于Schema的AOP支持
1.1 声明Aspect
1.2 编写Spring配置文件(目标类使用组件扫描)
1.3 测试
五、总结
AOP (Aspect Oriented Programming)意为面向切面编程,通过预编译和运行期间动态代理实现程序功能的一种技术。Spring中通过JDK动态代理和Cglib动态代理技术实现AOP。Spring可以灵活切换这两种动态代理方式,如果代理接口会默认使用JDK动态代理,如果代理某个类,这个类没有实现接口,就会切换Cglib。此外,我们也可以配置Spring选择。
一、引入AOP
系统开发中不仅要做核心业务, 还需要做一些系统业务,例如:事务管理、运行记录、安全日志等,这些系统业务被称为交叉业务。
如果在每一个业务处理过程当中,都掺杂这些交叉业务进去的话,存在两方面问题:
- 交叉业务代码在多个业务流程中反复出现,显然这些交叉代码没有得到复用。并且修改这些交叉代码,修改起来很麻烦。
- 程序员无法专注核心业务代码的编写,在编写核心代码的同时还需要处理这些交叉业务。
见上图,方便理解AOP思想。将非核心业务与核心业务进行分离,同时把这些非核心业务切入到业务流程当中的过程称为AOP。
二、核心AOP概念和术语
- JoinPoint(连接点)程序执行过程中的一个点,例如一个方法的执行或一个异常的处理。在Spring AOP中,一个连接点总是代表一个方法的执行。
- 切点(Pointcut)真正切入切面的方法,指定哪些方法需要切入
- 通知(Advice)一个切面在特定连接点采取的行动,根据通知类型确定执行流程。通知包括前置通知、后置通知、环绕通知等。
- Aspect(切面)通知 + 切面
-
Target object(目标对象) 被一个或多个切面所 advice 的对象。也被称为 "advised object"。由于Spring AOP是通过使用运行时代理来实现的,这个对象总是一个被代理的对象。
-
AOP proxy(代理对象) 一个由AOP框架创建的对象,以实现切面契约(advice 方法执行等)。在Spring框架中,AOP代理是一个JDK动态代理或CGLIB代理。
-
Weaving(织入)将通知应用到目标对象的过程。
三、切点表达式
切点表达式用来定义通知往哪些方法切入。
切点表达式语法格式
execution( [访问权限修饰符] 返回值类型 [全限定类名] 方法名(形式参数列表)[异常] )
构成 @Pointcut
注解的值的 pointcut 表达式是一个常规的AspectJ pointcut表达式。关于AspectJ的 pointcut 语言的全面讨论,请参见 《AspectJ编程指南》(以及 AspectJ 5开发者笔记 的扩展)或关于AspectJ的书籍之一(如Colyer等人的《Eclipse AspectJ》,或Ramnivas Laddad的《AspectJ in Action》)。
四、Spring实现AOP
(一)@AspectJ的支持
1 、基于注解开发
1.1 引入依赖
<!--spring context依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
1.2 实现目标类
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserMongoDao userMongoDao;
@Override
public User login(String name, String password) {
Document doc = new Document();
doc.put("name", name);
doc.put("password", password);
ArrayList<Document> list = userMongoDao.queryAll(doc);
User user = null;
if (! list.isEmpty()) {
Document document = list.get(0);
user = new User();
user.setId((ObjectId) document.get("_id"));
user.setName((String) document.get("name"));
}
return user;
}
}
1.3 定义切面类(日志管理)
package com.zookin.service.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LoggerAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspect.class);
@Pointcut("execution(public * com.zookin.service.*.*(..))")
public void logPointcut() {}
@Before("logPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
LOGGER.info("before advice:" + joinPoint.getSignature().getName());
}
@Around("logPointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
LOGGER.info("Method:" + joinPoint.getSignature().getName() + " execution time:" + (endTime - startTime) + "ms");
return result;
}
@AfterReturning(pointcut = "logPointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
LOGGER.info("After returning from method: " + joinPoint.getSignature().getName() + ", result: " + result);
}
@AfterThrowing(pointcut = "logPointcut()", throwing = "exception")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
LOGGER.error("Exception thrown from method: " + joinPoint.getSignature().getName() + ", exception: " + exception.getMessage());
}
}
1.4 将目标类和切面类纳入Spring容器
目标类添加@Service注解,切面类添加@Component、@Aspect注解。
1.5 开启组件扫描、自动代理
<?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.xsd">
<context:component-scan base-package="com.zookin.service"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
1.6 测试
@Test
public void loginTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
User login = userService.login("zookin", "zookin");
System.out.println(login);
}
2、基于XML方式开发
略....
(二) 基于Schema的AOP支持
如果喜欢基于XML的格式,Spring也提供了对使用
aop
命名空间标签定义切面的支持。它支持与使用@AspectJ
风格时完全相同的 pointcut 表达式和advice种类。要使用本节描述的aop
命名空间标签,你需要导入spring-aop
schema,如 基于XML schema的配置 中所述。在你的Spring配置中,所有的aspect和 advisor元素都必须放在一个
<aop:config>
元素(你可以在一个应用上下文配置中拥有多个<aop:config>
元素)。一个<aop:config>
元素可以包含 pointcut、 advisor 和 aspect 元素(注意这些必须按顺序声明)。
1.1 声明Aspect
通过使用 <aop:aspect>
元素来声明一个切面,并通过使用 ref
属性来引用支持 Bean,如下例所示。
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
支持切面的Bean(本例中的 aBean
)当然可以像其他Spring Bean一样被配置和依赖注入。
1.2 编写Spring配置文件(目标类使用组件扫描)
<?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.xsd">
<context:component-scan base-package="com.zhj.service"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="timerAspect" class="com.zhj.service.aspect.TimerAspect"></bean>
<aop:config>
<aop:pointcut id="aa" expression="execution(* com.zhj.service.CommodityService.*(..))"/>
<aop:aspect ref="timerAspect">
<aop:around method="time" pointcut-ref="aa"/>
</aop:aspect>
</aop:config>
</beans>
1.3 测试
@Test
public void commodityTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CommodityService commodityService = applicationContext.getBean("commodityService", CommodityService.class);
ArrayList<Commodity> commodities = commodityService.queryCommodity("自行车", 0, null);
}
五、总结
AOP是Spring框架的核心组件,AOP蕴含的底层原理十分值得了解和挖掘。Spring 框架自5.0版本以来,集成了Log4j、SLF4j框架,本文我简单地整合日志框架来说明AOP的实现,此外我们可以使用AOP实现事务管理,见上一篇博客。上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!Spring-事务支持https://mp.csdn.net/mp_blog/creation/editor/134687195