今天开始复习的学习spring AOP的应用
AOP 面向切面编程,主要解决的是针对一批组件的通用逻辑编程的问题 ,它采用了公共类+配置的方式,实现这个逻辑,可以降低公共类和组件之间的耦合度。
Spring AOP包括
方面组件、目标组件、切入点和通知
方面组件:方面组件是封装公共处理的组件,该组件被作用到其他目标组件的方法上
目标组件:一个或多个方面所作用的对象
切入点:用以指定那些组件和方法使用方面功能在Spring中使用一个表达式来指定切入目标
1、常用的切入点表达式
1)匹配类型
用于匹配哪些类型的方法启用方面组件,语法格式如下
within(类型)
--匹配容器中HibernateCostDaoImpl的所有方法
within(com.tarena.dao.HibernateCostDaoImpl)
--匹配com.tarena包下所有类的所有方法
within(com.tarena.*)
--匹配com.tarena包及子包的所有类的所有方法
within(com.tarena..*)
2)匹配方法
用于匹配哪些方法启用方面组件,语法格式如下
execution(修饰符? 返回类型 方法名(参数列表) throws异常?)
--匹配所有对象的delete方法
execution(* delete(..))
--匹配HibernateCostDaoImpl的delete方法
execution(* com.tarena.dao.HibernateCostDaoImpl.delete(..))
--匹配HibernateCostDaoImpl的所有方法
execution(* com.tarena.dao.HibernateCostDaoImpl.*(..))
--匹配com.tarena包下所有类的所有方法
execution(* com.tarena.*.*(..))
--匹配com.tarena包及子包下所有类的所有方法
execution(* com.tarena..*.*(..))
3)匹配bean名称
用于匹配bean的id属性,语法格式如下
bean(id属性值)
--匹配id="costDao"的组件的所有方法
bean(costDao)
--匹配所有id以Dao结尾的组件的所有方法
bean(*Dao)
4)匹配参数
用于匹配参数类型和个数,代码格式如下
args(参数列表)
--匹配有一个参数并且为String类型的所有方法
args(java.lang.String)
通知:通知时用于指定方面组件和目标组件的作用时机,例如方面组件是在目标组件的方法之前执行还是在 目标组件方法之后执行。
通知包括:前置通知 ,后置通知,最终通知,环绕通知,和异常通知
未使用annotation进行声明组件的方法之前
开发步骤
- 创建一个方面组件
创建一个类,充当方面组件,实现通用业务逻辑
- 声明方面组件
可以使用非annotation的方式 ,
也可以使用
annotation(注解)方式进行声明:
在applicationContext.xml文件中开启aop注解扫描
<aop:aspect-autoproxy proxy-target-class=”true” />
使用@Component标注这个类 ,将其声明为组件
使用@Aspect标注这个类,将其声明为方面组件
- 使用方面组件
在组件的方法上,使用注解将方面组件作用到目标组件的方法上,并设置通知类型以确认方面组件调用的时机。
前置通知:使用前置通知,在方面组件方法上增加注释:
@Before(“within(controller..*)”)
Public void log(){
}
后置通知,最终通知 使用方式与前置通知一致,只需要将注释改为@AfterReturning 、@After即可
环绕通知:使用环绕通知,在方面组件方法上增加注释
@Around(“within(controller..*)”)
Public Object log(ProceedingJoinPoint p) throws Throwable{
//此处代码在目标组件前执行
Object obj = p.proceed();//执行目标组件的方法
//此处代码在目标组件后执行
}
异常通知:使用异常通知,在方面组件方法上增加注释
@AfterThrowing(pointcut=”within(controller..*)”,throwing=”e”)
Public void log(Exception e){
}
通过配置方式声明SpringAOP:
package com.tarena.aspect;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerBean {
public void log1() {
//模拟记录日志
System.out.println("记录日志...");
}
public Object log2(ProceedingJoinPoint p )throws Throwable{
String className = p.getTarget().getClass().getName();
String method = p.getSignature().getName();
String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
String msg = "-->用户在"+date+",执行了"+className +"." +method+"()";
System.out.println(msg);
Object obj = p.proceed();
System.out.println("-->调用目标组件业务方法之后...");
return obj;
}
public void log3(Exception e){
StackTraceElement[] elems = e.getStackTrace();
System.out.println("-->"+elems[0].toString());
}
}
package com.tarena.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/emp")
public class EmpController {
@RequestMapping("/findEmp.do")
public String find(Model model) {
System.out.println("查询全部员工数据...");
return "emp/emp_list";
}
}
<!-- 注册组件 -->
<bean id="logger"
class="com.tarena.aspect.LoggerBean"/>
<!-- AOP配置 -->
<aop:config>
<!-- 声明方面组件 -->
<aop:aspect ref="logger">
<!--
aop:before,是前置通知;
aop:after-returning,是后置通知;
aop:after,是最终通知;
method,是方面组件中的方法;
pointcut,是切入点。
-->
<aop:after-returning method="log1"
pointcut="within(com.tarena.controller..*)"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspect ref="logger">
<aop:before method="log1" pointcut="within(com.tarena.controller..*)"/>
<aop:around method="log2" pointcut="within(com.tarena.controller..*)"/>
<aop:after-throwing method="log3" throwing="e" pointcut="within(com.tarena.controller..*)" />
</aop:aspect>
</aop:config>