12 . AOP
什么是AOP
面向切面编程,通过预编译方式和运行期动态代理 实现程序功能的统一维护的一种技术 。 利用AOP可以对业务逻辑的各个部分进行隔离 , 从而使得业务逻辑各部分耦合度降低,提高程序可用性,同时提高了开发的效率
- 横切关注点 :跨越多个应用程序多个模块的方法或功能 。 例如日志、安全、缓存、事物等
- 切面(ASPECT): 被模块化的特殊对象,是一个类
- 通知 (Advice) : 切面必须要完成的工作(切面类中的一个方法)
- 目标 (Target): 被通知的对象
- 代理(Proxy) : 向目标对象应用通知后创建的对象(代理角色)
- 切入点 (PointCut):切面通知执行的地点
- 连接点(JoinPoint) : 与切入点匹配的执行点
AOP的5种Advice
- 前置增强 (MethodBeforeAdvice): 在目标方法执行之前来实施增强
- 后置增强 (AfterReturningAdvice) : 在目标方法执行之后来实施增强
- 环绕增强 (MethodInterceptor) : 在目标方法执行前后同时实施增强
- 异常抛出增强 (ThrowsAdvice) : 目标方法抛出异常之后实施增强
- 引介增强 (introductioninterceptor) : 在目标方法中添加一些新的方法和属性
使用准备
导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7.M2</version>
</dependency>
使用方式
代码环境
//抽象角色
public interface UserService {
void add();
void delete();
void update();
void query();
}
//实现类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
以日志Log为横切面
public class PreLog implements MethodBeforeAdvice {
/*
* 1.method 要执行的目标对象的方法
* 2. args 方法的参数
* 3. object :目标对象
* */
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
/*
* 1. returnValue 目标对象方法返回值
* 2. method 要执行的目标对象的方法
* 3. args 方法的参数
* 4. object :目标对象
* */
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + method.getName() + "返回结果为" + returnValue);
}
}
一、使用spring的API接口
applicationContext.xml配置文件 需要导入AOP约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
<?xml version="1.0" encoding="UTF8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.liu.service.UserServiceImpl"></bean>
<bean id="preLog" class="com.liu.log.PreLog"></bean>
<bean id="afterLog" class="com.liu.log.AfterLog"></bean>
<!--配置AOP 需要导入AOP的约束-->
<aop:config>
<!--切入点 : 执行的地点 expression(要执行的位置)-->
<!--限制到com.liu.service.UserServiceImpl下的所有方法 .*(..)-->
<aop:pointcut id="pointcut" expression="execution(* com.liu.service.UserServiceImpl.*(..))"/>
<!--执行增强advice-->
<aop:advisor advice-ref="preLog" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
public class Test {
@org.junit.Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口 , 得到的是一个一系列类的接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
//执行结果
com.liu.service.UserServiceImpl的add被执行了
增加了一个用户
执行了add返回结果为null
二、使用自定义类实现AOP
自定义类——切面
//自定义类
public class DiyPointCut {
public void before() {
System.out.println("方法执行前操作");
}
public void after() {
System.out.println("方法执行后操作");
}
}
配置文件
<bean id="diy" class="com.liu.diy.DiyPointCut"></bean> <!--自定义类需要纳入spring管理-->
<aop:config>
<!--自定义切面 ref要引入的类——自定义类id -->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.liu.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"></aop:before> <!--前置增强-->
<aop:after method="after" pointcut-ref="pointcut"></aop:after> <!--后置增强-->
</aop:aspect>
</aop:config>
public class Test {
@org.junit.Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
//执行结果
方法执行前操作
增加了一个用户
方法执行后操作
execution表达式
execution(public * *(…))
匹配所有目标类的public方法*,但不匹配SmartSeller和protected voidshowGoods()方法。第一个代表返回类型,第二个代表方法名,而…代表任意入参的方法;
execution(* *To(…))
匹配目标类所有以To为后缀的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一个代表返回类型,而To代表任意以To为后缀的方法;
execution(* com.baobaotao.Waiter.*(…))
匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。*第一个 * 代表返回任意类型,com.baobaotao.Waiter.代表Waiter接口中的所有方法;
execution(* com.baobaotao.*(…))l
匹配com.baobaotao包下所有类的所有方法;
方式三、使用注解实现AOP
@Aspect
标注一个类为一个切面
@Before
前置增强注解
@After
后置增强注解
@Around
环绕增强注解
配置文件
<?xml version="1.0" encoding="UTF8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.liu"></context:component-scan> <!--注解扫描配置-->
<bean id="preLog" class="com.liu.log.PreLog"></bean>
<bean id="afterLog" class="com.liu.log.AfterLog"></bean>
<!--AOP注解配置-->
<aop:aspectj-autoproxy/>
</beans>
自定义切面类
@Component //组件注册注解
@Aspect //切面注解
public class DiyPointCut {
@Before(value = "execution(* com.liu.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("方法执行前操作");
}
@AfterReturning(value = "execution(* com.liu.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("方法执行后操作");
}
@Around(value = "execution(* com.liu.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
jp.proceed();//执行方法 , 只有执行这条语句 , 对应的方法才会被执行
System.out.println("环绕后");
}
}
测试类
public class Test {
@org.junit.Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
//测试结果
环绕前 //对应函数还没有执行 , 先环绕
方法执行前操作 //要执行方法了, 进行前置增强
增加了一个用户 //执行方法
方法执行后操作 //方法执行完毕 , 进行后置增强
环绕后 //结束环绕
使用cglib进行动态代理(默认为jdk)
<!--AOP注解配置-->
<aop:aspectj-autoproxy proxy-target-class="true"/> <!--proxy-trarget-class默认为false JDK实现 ,true为 cglib实行-->