在学习SSM框架,黑马的课程,在这里做一点笔记
AOP核心概念(核心本质是代理模式)
1.连接点:程序执行中的任意位置,粒度为执行方法,抛出异常,设置变量
在springAop中理解为方法的执行
2.切入点:匹配连接点的式子(切入点一定在连接点之中)
在springAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
3.通知:在切入点执行的操作,也就是共性功能
在springAOP中,功能最终以方法的形式呈现
4.通知类:定义通知的类
5.切面:描述通知与切入点的对应关系(共性关系??)
目标对象:原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
代理:目标对象无法直接完成工作,需要对其进行功能回填,通过原始功能的代理对象实现
入门案例思路分析
案例设定:测定接口执行效率
简化设定:在接口执行前输出当前系统时间
开发模式:XML or 注解
1.导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.制作连接点方法(原始操作,Dao接口与实现类)
目的是为了给update加上一个打印时间方法
public void save() { System.out.println(System.currentTimeMillis()); System.out.println("book dao save"); } public void update() { System.out.println("book dao update"); }
3.制作共性功能
4.定义切入点
5.绑定切入点与通知关系(切面)
//加入到spring 控制
@Configuration
//提醒当成AOP
@Aspect
public class MyAdvice {
//定义切入点
@Pointcut("execution(void hammer.Dao.Impl.BookDaoimpl.update())")
private void pt(){}
@After("pt()")//将通知和切入点进行绑定
//定义好通知
public void method(){
System.out.println(System.currentTimeMillis());
}
}
AOP工作流程
1.spring容器启动
2.读取所有切面配置中的切入
@Configuration
//提醒当成AOP使用
@Aspect
public class MyAdvice {
//定义在谁那里执行方法
@Pointcut("execution(void hammer.Dao.Impl.BookDaoimpl.update())")
private void pt(){}
@Pointcut("execution(void hammer.Dao.Impl.BookDaoimpl.save())")
private void up(){}
@After("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
匹配失败,创建对象
匹配成功,创建原始对象
//成功匹配的 造了一个代理对象
hammer.Dao.Impl.BookDaoimpl@3eb25e1a
class com.sun.proxy.$Proxy19
//匹配失败的 使用的原始对象
hammer.Dao.Impl.BookDaoimpl@36fc695d
class hammer.Dao.Impl.BookDaoimpl
4.获取bean执行方法
获取bean,调用方法并执行,完成操作
获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP切入点表达式
切入点:要进行增强的方法
切入点表达式:要进行增强的方法的描述方式
描述方式一:执行Dao包下的BookDao实现类下的无参数的update方法
execution("execution(void hammer.Dao.Impl.BookDaoimpl.update()")
描述方法二:执行Dao包下的bookDao接口下的无参房update方法
execution("execuion(void hammer.Dao.BookDao.update()")
动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
访问修饰符:public private 可以省略
返回值
报名
类/接口名
方法名
参数
异常名:
AOP中可以使用通配符描述切入点,快速描述
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * hammer.*.UserService.find*(*))
匹配hammer包下的任意包中的UserService类或接口中所有find开头的带有参数的方法
..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
+:专用于匹配子类类型
execution(* *..*Service+.*(..))
AOP通知类型
AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
1.前置通知
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
2.后缀通知
@After("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
3.环绕通知
@Around("pt()")
public void arount(Porceeding JoinPoint pjp) throws Throwable{
System.out.println("around before advice");
//表示对原始方法的调用
pjp.proceed();
System.out.println("around after advice");
}
有一个问题就是说如果方法存在返回值 ,那么就会造成一个错误
@Pointcut("execution(int hammer.Dao.Impl.BookDaoimpl.select())")
private void pt2(){}
@Around("pt2()")
public Object methods(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println(" win 1");
proceedingJoinPoint.proceed();
System.out.println("win2 ");
return 1200;
}
最下面的1200 强制换成了 select 的返回值
输出结果
win 1
win2
1200
1.环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后添加通知
2.通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
3.对原始方法的调用可以不接受返回值。 通知设置成void 即可,如果接收返回值,必须设定Object类型