AOP和继承的区别
继承是横向的,当父类名称更改的时候,子类方法里面也需要更改
aop是横向的,通过创建动态代理对象(不是真正的对象,但是可以实现功能)来实现
AOP原理
底层都是是用动态代理实现,不修改源代码,只是增强功能。
AOP操作术语
public class User {
public void add() { }
public void update() { }
public void delete() { }
public void findAll() { }
}
Joinpoint(连接点)
类里面的哪些方法可以被增强,这些方法就叫做连接点,例如上面的这些方法都可以作为接入点。
Pointcut(切入点)
在类里面有很多方法可以被增强,但是在实际中,被增强的方法叫做切入点
比如add方法和update方法被增强。
Advice(通知/增强)
实际增强的逻辑,比如扩展日志的功能,那么这个日志功能就叫做增强
增强/通知分为:
- 前置通知:在方法之前执行
- 后置通知:在方法之后执行
- 异常通知:方法出现异常执行
- 最终通知:后置之后执行finally
- 环绕通知:方法之前和之后都会执行,例如计算方法时间
Aspect(切面)
把增强应用到具体切入点的过程
比如在日志功能用在add方法上面过程
Introduction(引介)
动态添加属性或者方法,一般不用
Target(目标对象)
需要增强的对象(User)
Weaving(织入)
把advice应用到target的过程
Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类
Spring里AOP的操作
Spring里面使用AspectJ来实现AOP操作
AspectJ是一个单独的AOP框架,本身不是Spring的一部分
Spring2.0之后,增加对AspectJ的支持
使用AcpectJ有两种操作:
- 基于xml方式
- 基于注解的方式
AOP准备工作
- 导入AOP相关的jar包
- 在核心配置文件导入AOP的约束
配置文件的方式实现AOP
首先需要配置约束:
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
"><!-- 要添加最后2行 -->
下载aspectj的jar包:
https://www.eclipse.org/aspectj/downloads.php
有两个对象:
public class Book {
public void add() {
System.out.println("add......");
}
}
public class MyBook {
public void before() {
System.out.println("前置增强");
}
}
我们想要在执行Book的add方法之前,先执行MyBook的before方法。
传统的方式是需要在代码里面创建新的对象。
表达式配置切入点:
execution(* aop.Book.add(..))
前面使用*表示任意访问修饰符 后面接方法名的全路径
第二种写法:
execution(* aop.Book.*(..))
表示对类里面的所有方法增强
第三种写法:
execution(* *.*(..))
所有的方法
AspectJ的AOP操作
首先需要配置对象。
<bean id="book" class="aop.Book"></bean>
<bean id="myBook" class="aop.MyBook"></bean>
配置AOP的操作
- 配置切入点(使用表达式的方式表示对哪个类进行增强)
- 配置切面(把增强用在方法上)
<aop:config>
<!--配置切入点-->
<aop:pointcut id="addPointCut" expression="execution(* aop.Book.*(..))"/>
<!--配置切面-->
<aop:aspect ref="myBook">
<!--配置增强类型-->
</aop:aspect>
</aop:config>
可以看到有很多种增强类型。
<aop:config>
<!--配置切入点-->
<aop:pointcut id="addPointCut" expression="execution(* aop.Book.*(..))"/>
<!--配置切面-->
<aop:aspect ref="myBook">
<!--配置增强类型-->
<aop:before method="before" pointcut-ref="addPointCut"></aop:before>
</aop:aspect>
</aop:config>
这里,如果报错,是因为没有导入这个包
添加方式:
编程测试
public class MyTest {
@Test
public void testAop() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Book book = (Book) context.getBean("book");
System.out.println(book);
book.add();
}
}
由测试结果可以看到,add方法作为切入点,已经被前置增强了
后置增强
<aop:config>
<!--配置切入点-->
<aop:pointcut id="addPointCut" expression="execution(* aop.Book.*(..))"/>
<!--配置切面-->
<aop:aspect ref="myBook">
<!--配置增强类型-->
<aop:before method="before" pointcut-ref="addPointCut"/>
<aop:after-returning method="after" pointcut-ref="addPointCut"/>
</aop:aspect>
</aop:config>
上面这个地方,用aop:after也是可以的。。。
环绕通知
增强代码
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//方法之前
System.out.println("方法之前");
//执行被增强的方法
proceedingJoinPoint.proceed();
//方法之后
System.out.println("方法之后");
}
配置增强
<aop:config>
<!--配置切入点-->
<aop:pointcut id="addPointCut" expression="execution(* aop.Book.*(..))"/>
<!--配置切面-->
<aop:aspect ref="myBook">
<!--配置增强类型-->
<aop:before method="before" pointcut-ref="addPointCut"/>
<aop:after-returning method="after" pointcut-ref="addPointCut"/>
<aop:around method="around" pointcut-ref="addPointCut"/>
</aop:aspect>
</aop:config>
在方法之前和之后做同样操作
但是在before之后
after之前