Spring-AOP注解的方式进行ApsectJ开发
- 引入相关 jar 包
- 创建配置文件
applicationContext.xml
- 如果是接着我上面文章下来的就不需要创建了,因为已经有了内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<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">
</beans>
- 主要约束内容如下:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
编写切面类
- 创建
MyAspect
类,内容如下:
/**
* @author: BNTang
**/
public class MyAspect {
public void before(){
System.out.println("before enhance");
}
}
- 配置刚刚创建好的切面类
- 修改
applicationContext.xml
...
<bean id="myAspect" class="top.it6666.aspect.MyAspect"/>
...
- 以注解的方式使用
AOP
对目标类对象进行增强- 在配置文件中
开启以注解的形式进行 AOP 开发
<aop:aspectj-autoproxy/>
- 在切面类上添加相关对应的注解来进行对对应的目标对象进行增强
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@Before(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
public void before(){
System.out.println("before enhance");
}
}
- 编写测试类之前先来配置一个 Bean
- 修改
applicationContext.xml
...
<aop:aspectj-autoproxy/>
<bean id="goodsDao" class="top.it6666.dao.impl.GoodsDaoImpl"/>
<bean id="myAspect" class="top.it6666.aspect.MyAspect"/>
...
编写测试类代码如下:
/**
* @author: BNTang
**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class GoodsDaoTest {
@Resource(name = "goodsDao")
private GoodsDao goodsDao;
@Test
public void Demo() {
goodsDao.save();
}
}
AOP相关注解
注解名称 | 作用 |
---|---|
@Before | 前置通知 |
@AfterReturning | 后置通知 |
@Around | 环绕通知 |
@AfterThrowing | 异常抛出通知 |
@After | 最终通知 |
- 也就是把我们之前的 XML 方式换成了注解的方式
- 前置通知我上面已经演示过了我在把剩下的演示一下即可
后置通知
没有返回值
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@AfterReturning(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
public void afterReturning(){
System.out.println("after notice");
}
}
有返回值
- 修改
GoodsDao
接口的返回值类型
/**
* @author: BNTang
**/
public interface GoodsDao {
String save();
}
- 修改
GoodsDaoImpl
类中的save
方法
/**
* @author: BNTang
**/
public class GoodsDaoImpl implements GoodsDao {
@Override
public String save() {
return "BNTang → save";
}
}
- 修改切面类
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@AfterReturning(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))", returning = "res")
public void afterReturning(Object res) {
System.out.println("after notice " + res);
}
}
- 测试类代码同上
- 测试之后把之前的接口和实现类代码在撤回到之前的样子继续往下走
环绕通知
- 修改切面类
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@Around(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("之前");
Object proceed = joinPoint.proceed();
System.out.println("之后");
return proceed;
}
}
- 测试类代码同上
异常抛出通知
没有获取异常信息
- 修改切面类
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@AfterThrowing(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
public void AfterThrowing() {
System.out.println("异常抛出通知");
}
}
- 自己模拟一个异常即可我就不贴图了
- 修改
GoodsDaoImpl
实现类
- 测试类代码同上
获取异常信息
- 修改切面类
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@AfterThrowing(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))", throwing = "e")
public void AfterThrowing(Throwable e) {
System.out.println("异常抛出通知 " + e);
}
}
- 测试类代码同上
最终通知
- 不管有没有异常都会执行
- 修改切面类
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@After(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
public void After() {
System.out.println("最终通知");
}
}
- 测试类代码同上
- 测试完成之后把之前
GoodsDaoImpl
实现类的异常去掉
AOP注解切入点的配置
- 有时候一个方法要添加前置通知对又要添加异常通知,又要添加最终通知
- 可以在切面类当中定义好切入点
- 在通知当中直接使用定义好的切入点表达式即可
- 也就是写一个空的方法如下图:
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
...
@Pointcut(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
private void pointcut1(){}
}
- 改造之前的写法如下图:
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@After(value = "MyAspect.pointcut1()")
public void After() {
System.out.println("最终通知");
}
...
}
定义多个切入点
- 一个通知同时定义到多个方法当中,也就是可以应用到多个方法中
- 修改
GoodsDao
接口,添加一个规范方法如下:
/**
* @author: BNTang
**/
public interface GoodsDao {
...
void delete();
}
- 在实现类当中实现该方法如下:
/**
* @author: BNTang
**/
public class GoodsDaoImpl implements GoodsDao {
...
@Override
public void delete() {
System.out.println("BNTang → delete");
}
}
- 修改切面类,修改的内容如下:
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
...
@Pointcut(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..)) || execution(* top.it6666.dao.impl.GoodsDaoImpl.delete(..))")
private void pointcut1(){}
}
- 修改测试类,修改之后的内容如下:
/**
* @author: BNTang
**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class GoodsDaoTest {
@Resource(name = "goodsDao")
private GoodsDao goodsDao;
@Test
public void Demo() {
goodsDao.delete();
}
}
- 将一个通知应用到多个切入点中的写法
/**
* @author: BNTang
**/
@Aspect
public class MyAspect {
@After(value = "MyAspect.pointcut1() || MyAspect.pointcut2()")
public void After() {
System.out.println("最终通知");
}
@Pointcut(value = "execution(* top.it6666.service.UserTest.save(..)) || execution(* top.it6666.dao.impl.GoodsDaoImpl.delete(..))")
private void pointcut1(){}
@Pointcut(value = "execution(* top.it6666.dao.impl.GoodsDaoImpl.save(..))")
private void pointcut2(){}
}
- 测试类代码如下:
/**
* @author: BNTang
**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class GoodsDaoTest {
@Resource(name = "goodsDao")
private GoodsDao goodsDao;
@Test
public void Demo() {
goodsDao.delete();
goodsDao.save();
}
}
使用接口和不使用接口内部代理的区别
使用接口
- 可以看到使用的是 JDK 的动态代理
没有使用接口
- 可以看到使用的是 CgLib 的动态代理