Spring AOP

 

AOP : 在不惊动原始编程设计的思想上,增强文件的功能。

通知:一般我们把一个需要多个方法执行的功能(公共的)抽出来,封装到一个方法里,封装成一个类,成为通知类;

切入点:

另外上面说了一大堆就是,将公共的功能抽出来,挂载需要丰富功能的功能身上,增强功能。(功能 == 方法)

最好单独设一个包。 

 

Mydvice.java


//通知类必须配置成Spring管理的bean
// 这2没区别
@Component
@ComponentScan
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,要求配置在方法上方 返回值 全类名.方法名()
    @Pointcut("execution(void com.itheima.dao.BookDao.save())")
    private void pt() {
    }

    //绑定切入点和通知,这个在配置下方运行
    @After("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}


// 我们公共功能抽出来, 设置切入点,你要切入的方法,看的出这个功能和原方法功能是独立的,

 SpringConfig.java

@Configuration
@ComponentScan("com.itheima")
//开启注解开发功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP工作流程

 

先理解2个概念:如果我们的切入点找到连接点,最后执行的对象是代理对象,已经加了公共的功能了。

目标对象:原始功能去掉共性功能对应产生的类产生的对象,也被称为原始对象。

代理:目标对象无法直接完成代理工作,需要代理对象完成。

我们的共性功能,没有成功绑定,假如成功绑定上,打印出的getClass(),应该会被重写。

 AOP切入点表达式

切入点:要进行增强的方法。

切入掉表达式:要进行增强方法的描述方法。

execution(public void com.qhx.dao.BookDao.update() 异常)

其中 public 和异常能省略,看来大家能看出来,访问修饰符、... 包名、类名、方法名等...

当我们要绑定多个方法时,万一数量大,需要写多个切入点,那不是很麻烦?

所以

 注意,*  必须至少有1个,..  是0个以上。这个通配符设置在* 里面能任意被替换。

  •  总结一下:就是访问修饰符一般省略,因为一般是public,返回值类型,
  • 包名,通常是* 代替单个包名;
  • 接口名,如UserService 用 *Service代替。实现类,尽量不采用。
  • 方法名:名词采用*匹配,动词采用精准匹配。如:getById 书写成 getBy*,看情况,selectAll 就不用替代。
  • 异常一般省略,

AOP通知类型。

执行共性功能的位置

  // 环绕通知:比较特殊,会把原方法覆盖掉,因此必须要设置调用原方法。理论上,环绕能把Before和After等能替代掉。
    @Around("pt()")
    public Object method(ProceedingJoinPoint point) throws Throwable {
        System.out.println("before...");
        System.out.println("after...");
      
        point.proceed();

      // 假如原方法有返回值,这个就是返回值,并返回出去,才算代理结束
      //  Integer proceed = (Integer) point.proceed();
      //  return proceed;
    }
}

// 了解这2 
   @AfterReturning("pt()") // 原方法正常结束,才执行
    @AfterThrowing("pt()") // 原方法异常抛出,才执行
    @After
    @Before

 案例:为service包中每个*Service接口中每个方法,绑定一个测量万次执行时间的功能

 

1.我们这是个测试所以要引入测试类.

2.丰富方法的方法,所以要引入AOP

test包下的测试文件,配置Spring容器环境

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {
    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById() {
        Account ac = accountService.findById(2);
    }

 aop包下的PrpjectAdvice.java  advice 一般用来充当aop的通知类

@Component
@Aspect
public class ProjectAdvice {

    // 默认监视业务层所有的方法,
    // 1.返回值类型不确定,所以用* 替代;
    // 2.方法名也不一致,所以 用* 替代
    @Pointcut("execution(* com.itheima.service.*Service.*())")
    public void servicePt() {
    }

    @Around("ProjectAdvice.servicePt()")
    // 这是类名+方法名绑定,无所谓
    public void service(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature(); // 拿到你绑定方法的签名
        String typeName = signature.getDeclaringTypeName(); // 绑定的类
        String name = signature.getName(); // 绑定的方法
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            point.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("万次执行" + typeName + name + (start - end) + "ms");
    }

}

好家伙,假如谁,你要为不同的方法装不同的逻辑呢?可以限制上述同一个共性功能。有不同的方法调用

 环绕通知获取参数并调用

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();  // 获取调用参数
        args[0] = 1212; // 修改原参数
        Object proceed = null;
        // 卧槽,我们可以获取调用方法的参数,但是在环绕通知中,本质上不就是要我们自己调用才能执行原方法吗?
        // 我们可以对传入的参数进行矫健,如果不满足条件,就报错
        try {
            proceed = pjp.proceed(args); // 调用原方法传入修改
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return proceed;
    }

(了解)返回通知获取返回值,不过没啥用 。同样,异常通知获取异常对象,就把returning 换成throwing即可。

 @AfterReturning(value = "pt()", returning = "ret")
    public void returning(JoinPoint joinPoint, Object ret) {

        System.out.println(ret);
    }

案例:百度网盘分享衔接输入密码,自动删除空格 

1.我们输入的密码,(有时候会莫名其秒多打一个空格),放到数据库里,进行比对,假如有空格会报错。

因此,我们可以用环绕通知对前端获取的参数进行删除空格操作,其实前端也有,这样的操作。

 @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))") //
    private void servicePt() {
    }

    @Around("DataAdvice.servicePt()")
    public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs(); // 获取参数
        // 单纯看类型是看不出的,
        for (int i = 0; i < args.length; i++) {
            //判断参数是不是字符串
            if (args[i].getClass().equals(String.class)) {
                args[i] = args[i].toString().trim(); // 如果是,将空格删除赋值给原先参数
            }
        }
        Object ret = pjp.proceed(args); // 利用新参数调用
        return ret;
    }

看清楚,这个就是通过Class来对比,因为这个是类的反射是唯一的。           

总结:

AOP本质上通过代理模式。

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

本郡主是喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值