Spring学习笔记-第四天:AOP基于cglib的动态代理/AOP的相关概念-通知切点等/XML注解方式实现AOP
cglib的动态代理
需要的角色:
- 目标对象
- 增强方法
- 最终测试
public static void main(String[] args) {
//目标对象
final Target target=new Target();
//获得增强对象
Advice advice=new Advice();
//返回值 就是动态生成的代理对象 基于cglib
//1.创建增强器-cglib提供
Enhancer enhancer=new Enhancer();
//2.设置父类(目标)
enhancer.setSuperclass(Target.class);
//3.设置回调函数
//MethodInterceptor是Callback的一个子接口
enhancer.setCallback(new MethodInterceptor() {
//intercept方法相当于invoke方法,内部四个参数,
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();//执行前置
Object invoke = method.invoke(target, args);
advice.afterReturning();//执行后置
return invoke;
}
});
//在此刻赋值成功
//4.创建代理对象
Target proxy =(Target) enhancer.create();
proxy.save();
}
AOP
AOP的相关概念
AOP的相关术语
- Target:代理的目标对象
- Proxy:代理对象
- Joinpoint:连接点,指那些被拦截到的点。在Spring中,这些点指方法,因为Spring只支持方法类型的连接点(可以被增强的方法)
- Pointcut:切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice:(通知/增强)是指拦截到Joinpoint之后要做的事情就是通知
- Aspect:切面是切入点和通知(增强)的结合
- Weaving:织入实质把将切点和增强结合的过程是织入过程,Spring是动态代理织入,AspectJ是采用编译期织入和类装载期织入
连接点是可以被增强的方法,切点是实际上被增强过的方法,连接点范围更大
AOP开发明确的事项
- 需要编写的内容
- 编写核心业务代码(Target的目标方法-切点)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件当中,配置织入关系,即将那些通知与哪些连接点进行结合
- AOP技术实现的内容
Spring框架会监控切入点方法的执行--通过配置文件决定哪些方法是切点;一旦监控到切入点方法被运行,即使用代理机制,动态创建目标对象(切点)的代理对象。然后根据配置文件配置的增强类型(通知类别),在代理对象(切点)对应的位置,将通知对应的功能织入,完成完整的代码逻辑运行。
配置文件-执行切点方法-监控到后动态创建代理对象,调用代理对象同名方法-内部调用目标方法,进行增强方法的介入。 - AOP底层使用哪种代理方式
Spring会根据目标类是否实现了接口来决定采用哪种动态代理。
知识要点
- aop:面向切面编程
- aop底层实现: 基于jdk/Cglib的动态代理
- aop的重点概念:
- Pointcut:切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice:(通知/增强)是指拦截到Joinpoint之后要做的事情就是通知
- Aspect:切面是切入点和通知(增强)的结合
- Weaving:织入实质把将切点和增强结合的过程是织入过程,Spring是动态代理织入,AspectJ是采用编译期织入和类装载期织入
- 开发明确事项
- 谁是切点
- 谁是通知
- 将切点和通知进行织入配置(配置文件)
基于XML的AOP开发
快速入门
- 导入AOP相关坐标
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面类的对象创建权交给Spring
- 在applicationContext.xml中配置织入关系(最难)
- 测试代码
导入AOP相关坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!-- aspectj更轻量,更高效实现aop-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
创建目标接口和目标类
首先创建目标接口和实现。
然后创建切面类
切面类不完全等同之前的直接设置增强类,而是内部有增强的方法
public class MyAspect {
public void before(){
System.out.println("前置增强...");
}
}
将目标类和切面类的对象创建权交给Spring容器
<!-- 配置目标对象-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!-- 配置切面对象-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>
在配置文件当中,告诉Spring框架哪些方法需要进行哪些增强
首先,要引用AOP命名空间和aop配置的位置
<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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
然后配置织入
<aop:aspect ref="myAspect"></aop:aspect>
用来表示myAspect是切面 method表示哪些方法是哪些增强,然后pointcut指定pointcut,其中需要execution调用切点方法的全限定名。
<aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save)"></aop:before>
这句代码表明,当访问这个save()方法时,这个方法需要前置增强,而这个前置增强是在myAspect这个切面的before方法当中封装。
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="myAspect">
<!-- 切面:切点+通知 -->
<aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"></aop:before>
</aop:aspect>
</aop:config>
测试
要进行Spring测试,需要引入Spring测试的包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.6</version>
</dependency>
然后指定RunWith,详见前文Spring整合Junit的过程
代码实现:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
//这里使用接口接住,因为jdk代理,代理对象与目标对象皆为接口类型,不是实现类型
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
切点表达式的配置:
表达式语法:execution([修饰符] 返回值类型 包名 类名 方法名(参数))
execution(public void com.itheima.aop.Target.save())
[]代表可写可不写,包名可能有多层,倒数第一个是方法名,倒数第二个是类名
- 访问修饰符可以省略
- 返回值类型,包名,类名,方法名可以使用*代表任意
- 包名与类名之间一个点代表当前包下面的类,两个点代表当前包及其子包下的类
- 参数列表可以使用两个点..表示任意个数,任意类型的参数列表
execution(public void com.itheima.aop.Target.method())//指定某包下的方法
execution(void com.itheima.aop.Target.*(..))//代表Target下的所有方法,任意参数
execution(* com.itheima.aop.*.*(..))//常用的,aop包下的所有函数
execution(* com.itheima.aop..*.*(..))//aop包及其子包下的
execution(* *..*.*(..))
通知类型
通知的配置语法
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"/>
类型:
- 前置通知 before 在切入点之前
- 后置通知 after-returning 在切入点之后
- 环绕通知 around 在切入点前后
- 异常抛出通知 throwing 增强方法出现异常时通知
- 最终通知 after 不管增强方法出不出现异常都通知
演示:
around需要加入参数(其他也可以加参数)
//ProceedingJoinPoint 正在进行的 连接点=切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强...");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强...");
return proceed;
}
在验证过程中,发现出现异常时,只会有前置增强和环绕前增强
xml配置文件相关代码:
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="myAspect">
<!-- 切面:切点+通知 -->
<!-- <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"></aop:before>-->
<aop:before method="before" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:before>
<aop:after-returning method="afterReturning" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after-returning>
<aop:around method="around" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:around>
<aop:after-throwing method="afterThrowing" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after-throwing>
<aop:after method="after" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after>
</aop:aspect>
</aop:config>
切面配置相关代码
public class MyAspect {
public void before(){
System.out.println("前置增强...");
}
public void afterReturning(){
System.out.println("后置增强...");
}
//ProceedingJoinPoint 正在进行的 连接点=切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强...");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强...");
return proceed;
}
public void afterThrowing(){
System.out.println("异常抛出异常....");
}
public void after(){
System.out.println("最终增强...");
}
}
切点表达式的抽取-进一步解耦
实际上它们的切点表达式可能是一样的,可以进行相应的抽取。在增强中使用pointcut-ref进行引用
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="myAspect">
<!-- 切面:切点+通知 -->
<!-- 抽取切点表达式-->
<aop:pointcut id="mypointCut" expression="execution(* com.itheima.aop.*.*(..))"/>
<aop:around method="around" pointcut-ref="mypointCut"></aop:around>
</aop:aspect>
</aop:config>
知识要点
- aop织入的配置-xml文件修改
- 通知的类型
- 切点表达式的写法

浙公网安备 33010602011771号