底层原理
AOP底层为动态代理模式,有两种方式:JDK代理、CGLIB代理
jdk代理:基于接口的动态代理技术
条件:必须有接口
生成的动态代理对象和被代理对象为兄弟管理
cglib代理:基于父类的动态代理技术
条件:被代理对象不能使用final修饰
导入cglib坐标
生成的动态代理对象是被代理对象的子类
Spring的AOP简介
思想:面向切面编程
目的:代码之间的解耦,不破坏源码的前提下,对代码进行增强(增加业务)
底层:动态代理
AOP相关术语:
aop术语:
Target(目标对象): 要被增强的对象(UserServiceImpl)
Proxy(代理对象) : 增强的对象 (proxy_obj)
Joinpoint(连接点): 可以被增强的方法(save delete update find)
Pointcut(切入点): 要开始对连接点增强(save delete)
Advice(通知/增强): 增强的业务
前置通知、后置通知、异常通知、最终通知
Aspect(切面): 切入点+通知=切面 (目标方法和增强方法合成在一起 叫做切面)
Weaving(织入):通知整合切入点形成切面的过程 我们称之为织入
形成的最终结果是一个切面 (底层实现技术就是动态代理)
AOP开发:基于XML开发、基于注解开发
基于XML的AOP开发
1.pom导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
代码:
<!--通知类-->
<bean id="adviceConfig" class="cn.itcast.advice.AdviceConfig"></bean>
<!--spring的aop:
关注点1:通知(增强方法)
关注点2:织入(将通知和切入点合并成切面的过程) 使用aop标签
// 将增强方法和目标方法合同在一起的过程
-->
<!--aop的置入-->
<aop:config>
<!--指向通知-->
<aop:aspect ref="adviceConfig">
<!--
before:之前
method:通知类中的某个方法
pointcut:指向的是切入点(要增强的方法)
表达式:
execution(public void cn.itcast.serviceimpl.AccountServiceImpl.save(..))
修饰符:可以省略不写
返回值:可以使用通配符
包名:cn.itcast.serviceimpl
可以使用通配符代替 但是只能代替一个
类名:AccountServiceImpl
可以使用通配符代替 但是只能代替一个
方法名:save(..)
可以使用通配符代替 但是只能代替一个
通配符:* 可以代替所有
..:用于方法参数 用于包名
方法参数:代表参数可有可无
包名:cn.. 带表cn下的所有子包 cn.itcast.. 带表是cn.itcast下的所有子包
-->
<!--抽取切入点-->
<aop:pointcut id="pt" expression="execution(* cn.itcast.*.AccountServiceImpl.*(..))"></aop:pointcut>
<!--前置 后置 异常 最终 顺序无序 建议单独使用-->
<aop:before method="before" pointcut-ref="pt"></aop:before>
<aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
<aop:after method="after" pointcut-ref="pt"></aop:after>
<!--环绕通知-->
<!-- <aop:around method="around" pointcut="execution(public void cn.itcast.serviceimpl.AccountServiceImpl.save(..))"></aop:around>-->
</aop:aspect>
</aop:config>
基于注解的AOP开发
1.application.xml中开启aop的注解
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.在通知类中添加注解
@Component //放在ioc容器中
@Aspect //声明当前类是一个通知类
public class AdviceConfig {
....
}
3.在通知方法上添加切入点表达式(也可先不加)
public class AdviceConfig {
/*抽取切入点表达式:
* 定义无参无返回值无内容的方法
* 在方法头上加统一注解:@Pointcut("execution(* cn.itcast.*.AccountServiceImpl.save(..))")
* 谁用谁调方法名
*
* */
@Pointcut("execution(* cn.itcast.*.AccountServiceImpl.save(..))")
public void pt(){}
//通知
//@Before("pt()")
public void before(){
System.out.println("之前。。。");
}
//通知
//@AfterReturning("pt()")
public void afterReturning(){
System.out.println("之后。。。");
}
//通知
//@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("异常。。。");
}
//通知
//@After("pt()")
public void after(){
System.out.println("最终。。。");
}
// 通知--给环绕用
@Around("pt()")
public void around(ProceedingJoinPoint pj){
try {
// 之前
System.out.println("之前。。。");
// 原方法
pj.proceed(); //invoke
// 之后
System.out.println("之后。。。");
}catch (Throwable e){
e.printStackTrace();
System.out.println("异常。。。");
}finally {
System.out.println("最终。。。");
}
}
}
4.抽取切入点表达式
/*抽取切入点表达式:
* 定义无参无返回值无内容的方法
* 在方法头上加统一注解:@Pointcut("execution(* cn.itcast.*.AccountServiceImpl.save(..))")
* 谁用谁调方法名
*
* */
@Pointcut("execution(* cn.itcast.*.AccountServiceImpl.save(..))")
public void pt(){}
// 通知--给环绕用
@Around("pt()")
public void around(ProceedingJoinPoint pj){
try {
// 之前
System.out.println("之前。。。");
// 原方法
pj.proceed(); //invoke
// 之后
System.out.println("之后。。。");
}catch (Throwable e){
e.printStackTrace();
System.out.println("异常。。。");
}finally {
System.out.println("最终。。。");
}
}