一、切点表达式基本使用
Service层
@Service
public class NormalService {
private Logger logger = Logger.getLogger(getClass());
public void testArgNames(String str,Integer i){
logger.info("有参正常返回StringInteger");
}
public void testArgNames(Integer i,String str,String str2){
logger.info("有参正常返回IntegerStringString");
}
public void testArgNames(){
logger.info("无参正常返回");
}
}
切面
@Aspect
@Component
public class NormalAspect {
private Logger logger = Logger.getLogger(getClass());
// 拦截参数为任意的testArgNames方法
@Pointcut(value = "execution(public * com.gome.rec.af.consumer1.service.NormalService.testArgNames(..))")
public void normalLog() {
}
@Before(value = "normalLog()")
public void before() {
System.out.println("调用参数为任意的testArgNames方法...");
}
}
Controller层
@RestController
public class NormalController {
@Autowired
private NormalService normalService;
@RequestMapping("testArgNames2")
public void testArgName(@RequestParam Integer i,String str){
normalService.testArgNames();
normalService.testArgNames(str,i);
normalService.testArgNames(i,str,str);
}
}
请求localhost:8066/testArgNames2?i=1&str=abc,三个方法都拦截,控制台显示:
注: 一个Aspect中不可以有两个名字相同的切面
二、通知
@Aspect
public class UserLogAspect {
private Logger logger = Logger.getLogger(getClass());
@Pointcut("bean(*Service) && within(com.gome.rec.af.consumer1.service.User*)")
public void pointCut() {
}
@Before("pointCut()")
public void beforeLog() {
logger.info("开始执行前置通知 日志记录");
}
@After("pointCut()")
public void afterLog() {
logger.info("开始执行后置通知 日志记录");
}
@AfterReturning("pointCut()")
public void afterReturningLog() {
logger.info("方法成功后执行通知 日志记录");
}
@AfterThrowing("pointCut()")
public void afterThrowingLog() {
logger.info("方法抛出异常后执行通知 日志记录");
}
@Around("pointCut()")
public Object aroundLog(ProceedingJoinPoint joinPoint) {
Object result = null;
try {
logger.info("环绕通知开始 日志记录");
long start = System.currentTimeMillis();
// 执行拦截的方法
result = joinPoint.proceed();
long end = System.currentTimeMillis();
logger.info("总共执行时长:" + (end - start) + "毫秒");
logger.info("环绕通知结束 日志记录");
} catch (Throwable t) {
logger.info("出现错误");
}
return result;
}
}
请求打印控制台
执行顺序:环绕开始-@Before-执行-环绕结束-@After-@AfterReturning
若有异常:环绕开始-@Before-执行-@AfterThrowing-@After-@AfterReturning
三、权限+自定义注解
定义@AuthChecker注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthChecker {
}
@Aspect
public class HttpAopAdviseDefine {
// @annotation:匹配由指定注解所标注的方法
@Pointcut("@annotation(com.gome.rec.af.consumer1.aspect.AuthChecker)")
public void pointcut(){}
@Around("pointcut()")
public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable{
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 检查用户所传递的 token 是否合法
String token = getUserToken(request);
if (!token.equalsIgnoreCase("123456")) {
return "错误, 权限不合法!";
}
return joinPoint.proceed();
}
}
只有符合这个格式的请求才可以,否则给出提示
四、事务应用
最简单就是加入:@Transactional注解,属性如下进行设置即可
propagation 属性:事务的传播行为,默认值为 Propagation.REQUIRED
REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务
SUPPORTS :如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
MANDATORY :如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
REQUIRES_NEW :重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
NOT_SUPPORTED :以非事务的方式运行,如果当前存在事务,暂停当前的事务。
NEVER :以非事务的方式运行,如果当前存在事务,则抛出异常。
NESTED :和 Propagation.REQUIRED 效果一样。
isolation 属性:隔离级别,默认值为 Isolation.DEFAULT,底层数据库默认的隔离级别
timeout 属性:默认为-1,超过时间未提交则回滚,单位为秒
readOnly 属性:默认false,忽略读数据select,则设置为true
rollbackFor 属性:指定能够触发事务回滚的异常类型,可以指定多个异常类型
noRollbackFor 属性
基本的AOP实践就是这些,具体需要是实际中应用。