1、spring AOP概述
1.1什么是springAOP?
AOP是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,实际项目中我们一般将面向对象理解为一个静态过程,而现象切面包含一个一个动态过程,是属于在对象运行时动态织入一些功能。
1.2AOP一般是帮助 我们解决哪类问题?
实际项目中我们一般会将系统分为两大部分:核心关注点 和非核心关注点。
编程过程中首先要完成的就是核心关注点,也就是我们业务的核心。
而一些非核心关注点我们应该如何切入到系统中?两种方式:一种是硬编码,这种是违背了OCP的,另一种就是我们推荐使用的AOP、
1.3AOP在项目中的应用场景
AOP通常用户处理日志信息,事务管理,权限处理,缓存处理。
AOP在项目中的定位属于 在控制层和业务层直接用于获取业务层的信息,抓取业务层的数据,对业务层进行处理。
1.4AOP底层原理分析
1)如果使用AOP处理的业务层实现了一个接口,那么底层默认使用jdk动态代理的机制为目标实现类创建一个代理对象(则生成的代理对象同样实现了该接口、)
2)假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)
1.5AOP的相关术语
切面(aspect):横切面对象,一般为一个具体类的对象,借助@Aspect声明。
连接点(joinpoint):程序执行过程中某个特定的点、一般指被拦截到的方法。
切入点(pointcut):对连接点拦截内容的一种定义、一般认为多个连接点的集合。
通知(Advice):在切面的某个特定连接点上执行的动作(指对选定的方法在什么时候进行操作)
这里听着不是那么明白,后面会详细介绍。
2.1AOP的编程步骤
1)新建maven project
2)在 pom文件添加aop依赖
3)配置aop核心
4)定义核心业务(核心关注点)
5)基于特定的核心业务去定义非核心关注点、
6)配置实现非核心业务
2.2AOP基于xml实现(日志管理)
2.2.1添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
2.2.2创建接口
public interface HelloService {
void sayHello(String msg);
}
2.2.3创建 实现类
public class HelloServiceImpl implements HelloService {
public void sayHello(String msg) {
System.out.println(msg);
}
}
2.2.4日志处理类(将此日志处理类定义为横切面,通过此横切面实现扩展业务)
public class LogAspect {
public void beforeMethod(){
System.out.println("LogAspect.beforeMethod()");
}
public void afterMethod(){
System.out.println("LogAspect.afterMethod()");
}
}
2.2.5配置bean
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 核心业务对象 -->
<bean id="helloService"
class="spring.beans.HelloServiceImpl"/>
<!-- 配置非核心业务对象(日志处理对象):切面 -->
<bean id="log"
class="spring.aop.LogAspect"/>
<!-- AOP配置(切入点,切面) -->
<aop:config>
<!-- 配置切入点(bean,@annotation,within) -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)"
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="log" >
<aop:before method="beforeMethod"
pointcut-ref="logPointCut"/>
<aop:after method="afterMethod"
pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>
最后编写测试方法进行测试
3.3.Spring AOP 编程增强
Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
指示符 作用
- bean 用于匹配指定bean id的的方法执行
- within 用于匹配指定包名下类型内的方法执行
- @annotation 用于匹配指定注解修饰的方法执行
execution 用于进行细粒度方法匹配执行具体业务
bean应用于类级别,实现粗粒度的控制:
bean(“userServiceImpl”)) 指定一个类
bean("*ServiceImpl") 指定所有的后缀为service的类
within应用于类级别,实现粗粒度的切面表达式定义:
within(“aop.service.UserServiceImpl”) 指定类,只能指定一个类
within(“aop.service.") 只包括当前目录下的类
within("aop.service…”) 指定当前目录包含所有子目录中的类
execution方法级别,细粒度的控制:
语法:execution(返回值类型 包名.类名.方法名(参数列表))
execution(void aop.service.UserServiceImpl.addUser()) 匹配方法
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为字符串
execution(* aop.service….(…)) 万能配置
@annotaion应用于方法级别,实现细粒度的控制:
@annotation(“com.jt.common.anno.RequestLog”)) 指定一个需要实现增强功能的方法
在AOP编程中有五种类型的通知:
1)前置通知 (@Before) 方法执行之前执行
2)返回通知 (@AfterReturning) 方法return之后执行
3)异常通知 (@AfterThrowing) 方法出现异常之后执行
4)后置通知 (@After) : 又称之为最终通知(finally)
5)环绕通知 (@Around) :重点掌握
执行顺序我们可以通俗的认为
其结构如下:
Try{
@Before
核心业务
@AfterReturning
}catch(Exception e){
….
@AfterThrowing
}finally{
….
@After
}
这么写大家肯定都已经知道执行顺序了,如果是初学者,不知道这段代码 的执行顺序的话,不妨去写一个小demo测试一下
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:before method="beforeMethod" pointcut-ref="pc"/>
<aop:after method="afterMethod" pointcut-ref="pc"/>
<aop:after-returning method="returnMethod"
pointcut-ref="pc"/>
<aop:after-throwing method="throwMethod"
pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
切入点及环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
3.2.2.注解方式通知配置增强
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
@Aspect
@Service
public class LogAspect {
@Pointcut("bean(orderServiceImpl)")
public void doLog(){}
@Before("doLog()") //orderServiceImpl类中的具体方法
public void doBefore(){
System.out.println("log before");
}
@After("doLog()") //orderServiceImpl类中的具体方法
public void doAfter(){
System.out.println("log after");
}
@AfterReturning("doLog()")
public void doAfterReturning(){
System.out.println("log doAfterReturning");
}
Throwing*/
@AfterThrowing("doLog()")
public void doAfterThrowing(){
System.out.println("log doAfterThrowing");
}
}
切入点及环绕通知的配置
@Component
@Aspect
public class TxManager {
@Pointcut("execution(com.company.spring.service..*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
try{
System.out.println("事务开始");
Object result = joinPoint.proceed();//执行目标方法
System.out.println("提交事务");
return result;
}catch(Exception e){
System.out.println("回滚事务");
throw e;
}finally{
System.out.println("释放资源");
}
}
}