spring入门之再再再次理解AOP

再再再再次理解AOP
面向切面编程,这是对面向对象思想的一种补充。“也就是在程序运行的时候,不改变程序源码的情况下,动态的增加方法的功能”

  1. 日志
  2. 事务
  3. 数据库操作

这些场景都会使用到AOP,并且AOP也是用来解决模板化的代码,消除臃肿的。

在这里插入图片描述

基于JDK的动态代理实现的AOP
MyCalculator 接口

package org.wzw.aop;

public interface MyCalculator {
    //这里定义一个方法
    int add(int a,int b);
}

MyCalculatorImpl

package org.wzw.aop;

public class MyCalculatorImpl implements  MyCalculator{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

CalculatorProxy

package org.wzw.aop;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CalculatorProxy {
    //这里写一个计算器的代理类,用来增加他的功能
    public static Object getInstance(MyCalculatorImpl myCalculator){
        return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
            /**
             *
             * @param proxy 代理对象
             * @param method 代理方法
             * @param args 方法参数
             * @return 方法返回值
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method+"方法开始了");
                Object invoke = method.invoke(myCalculator, args);
                System.out.println(method+"方法结束了");
                return invoke;
            }
        });
    }

}

MainAop

package org.wzw.aop;


public class MainAop {
    public static void main(String[] args) {
        //创建实例
        MyCalculatorImpl myCalculator = new MyCalculatorImpl();
        //调用那个方法,输入参数实例
        MyCalculator instance = (MyCalculator) CalculatorProxy.getInstance(myCalculator);
        //这个instance就有对应的方法了
        int add = instance.add(3, 4);
        System.out.println(add);
    }
}

Spring中的AOP
他有五种类型

  1. 前置通知
  2. 后置通知
  3. 异常通知
  4. 返回通知
  5. 环绕通知

首先是前置通知
这里也是尝试给计算器增加方法,首先导入依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

@Component这个注解好像是能扫描成实体类的 这个就相当于是

这次的代码任务是需要再加一个日志功能。
MyCalculator

package org.wzw.aop;

public interface MyCalculator {
    //这里定义一个方法
    int add(int a,int b);

    void min(int a,int b);
}

MyCalculatorImpl

package org.wzw.aop;

import org.springframework.stereotype.Component;

@Component
//这个注解好像是能扫描成实体类的 这个就相当于是 <bean id="" class=""> //到时候是用来使用java配置扫描的
public class MyCalculatorImpl implements  MyCalculator{
    @Action
    @Override
    public int add(int a, int b) {
        System.out.println(a+"-"+b+"="+(a-b));
        return a+b;
    }

    @Override
    public void min(int a, int b) {
        System.out.println(a+"-"+b+"="+(a-b));
    }
}

再开始定义切点:
Action

package org.wzw.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {

}

先看看这几个注解:@Target是表示注解的作用目标,@Retention是注解的保留位置
Action就是作为一种标记
这个annotation就对应上了刚刚的Action@Before("@annotation(Action)")这个的意思 就是你的有action方法 开始执行的之后,这个方法会激活
LogAspect

package org.wzw.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
//第二个注解的意思 就是定义他是一个切面
public class LogAspect {
    //先定义before通知
    //这个annotation就对应上了刚刚的Action
    @Before("@annotation(Action)")
    //这个的意思 就是你的有action方法 开始执行的之后,这个方法会激活
    public void before(JoinPoint joinPoint){
        //JoinPoint就是接入点的意思
        //这里执行对应的方法名字
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行了");
    }
}

这里有个很有意思的点就是

MyCalculatorImpl bean = acac.getBean(MyCalculatorImpl.class);

在不实现接口的时候,也就是单独是一个类的时候就是成立的,但是当你是通过implement来的,并且都将@component放在了MyCalculatorImpl,依然会报错
JavaConfigAop

package org.wzw.aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class JavaConfigAop {
}

MainAAop

package org.wzw.aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainAAop {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(JavaConfigAop.class);
        MyCalculator bean = acac.getBean(MyCalculator.class);
        bean.add(1,1);
        bean.min(4,3);
    }
}

在这里插入图片描述

然后聊聊AOP的另外四种类型
修改LogAspect 加入后置通知

package org.wzw.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.wzw.ioc.javaconfig.SayHello;

@Component
@Aspect
//第二个注解的意思 就是定义他是一个切面
public class LogAspect {
    //先定义before通知
    //这个annotation就对应上了刚刚的Action
    @Before("@annotation(Action)")
    //这个的意思 就是你的有action方法 开始执行的之后,这个方法会激活
    public void before(JoinPoint joinPoint){
        //JoinPoint就是接入点的意思
        //这里执行对应的方法名字
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行了");


    }

    /**
     * 后置 在目标方法执行之后执行
     * @param joinPoint
     */
    @After("@annotation(Action)")
    public void after(JoinPoint joinPoint){
        //也和那个差不多
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了");
    }

    /**
     * 可以在该方法中返回目标方法的返回值
     * @param joinPoint
     * @param r 参数名称
     */
    //再通过返回通知来获取方法返回值
    @AfterReturning(value = "@annotation(Action)",returning = "r")
    public void returning(JoinPoint joinPoint, Integer r){
        //这个r 就是返回的通知 并且要和r保持一致  而且只能是那种需要返回的吧,不然的话 试试看 无返回值和返回的类型不是String
       //证明了一下 如果没有返回值的话,是不会出现这个输出的
        //然后 如果变成了String的话 这个输出只会输出对应参数类型是String的
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法返回通知"+r);

    }

    /**
     * 异常通知,当目标函数抛出异常的时候,他就会被处罚
     * @param joinPoint
     * @param e 异常参数,和方法的参数名一一对应,注意异常的类型
     */
    @AfterThrowing(value = "@annotation(Action)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常"+e.getMessage());
    }
}

(要注意,对于最后一个方法,是在异常的情况下出现的)
比如在对应的Action类下写入:int i=1/0, 运行一下
在这里插入图片描述
然后最后再讲一下环绕通知

@Around("@annotation(Action)")
public Object around(ProceedingJoinPoint pjp){
    return null;
}

(ps 当然是需要先把int i=1/0这个给注释掉)

运行这个的话 会出现一个错误
在这里插入图片描述
是因为不能把 int 强转为null
改成这样

@Around("@annotation(Action)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    //这个有点类似于method,invoke方法,我们可以在这个方法的前后分别添加日志,相当于前置/后置通知

    Object proceed = pjp.proceed();
    return proceed;
}

就正常运行了
在这里插入图片描述
所以对AOP的理解就是,拦截并加功能

统一定义切点和非侵入式定义切点

如果是统一定义切点的话
创建一个 pointcut

@Pointcut("@annotation(Action)")
public void pointcut(){}

并在这些注释上 加入pointcut这个方法,诸如:
也就是把 @annotation(Action) 替换为 pointcut()

@Before("pointcut()")

运行发现是可以的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值