Spring AOP学习

104 阅读4分钟

学习AOP初级的第一天

AOP 的好处
  • 降低模块之间的耦合度
  • 使系统容易扩展
  • 更好的代码复用
  • 非业务代码更加集中,不分散,便于统一管理

AOP 是对面向对象编程的一个补充,在运行时,动态的将代码切入到类的指定方法,指定位置,的编程思想就是面向切面编程

首先 创建一个maven 工程
pom.xml 添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.11.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.11.RELEASE</version>
    </dependency>

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.11.RELEASE</version>
        <scope>compile</scope>
</dependency>
</dependencies>

创建一个动态代理类

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

public class MyInvocationHandler implements InvocationHandler {
    // 接受委托对象
    private Object object;
    // 返回代理对象
    public  Object bind(Object object){
        this.object = object;
        return   Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }
    // 动态代理
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法的参数"+ Arrays.toString(args));
        Object result = method.invoke(this.object, args);
        System.out.println(method.getName()+"结果是:"+result);
        return result;
    }
}

在CalImpl里实现接口Cal的方法

public class CalImpl implements Cal {
    public  int add(int num1, int num2) {
        return num1+num2;
    }

    public int minus(int num1, int num2) {
        return num1-num2;
    }

    public int multi(int num1, int num2) {
        return num1*num2;
    }

    public int div(int num1, int num2) {
        return num1/num2;
    }

}

测试类Test

MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Cal cal1  =(Cal)myInvocationHandler.bind(cal);
cal1.add(1,2);
cal1.minus(2,3);
cal1.div(4,5);
cal1.multi(7,6);

上述是使用动态代理类实现AOP的过程
如果没有使用动态代理类,常规写法:
CalImpl里:

public class CalImpl implements Cal {
    public  int add(int num1, int num2) {
        System.out.println("方法参数");
        int result = num1+num2;
        System.out.println("方法结果"+result);
        return result;
    }

    public int minus(int num1, int num2) {
        System.out.println("方法参数");
        int result = num1-num2;
        System.out.println("方法结果"+result);
        return result;
    }

    public int multi(int num1, int num2) {
        System.out.println("方法参数");
        int result = num1*num2;
        System.out.println("方法结果"+result);
        return result;
    }

    public int div(int num1, int num2) {
        System.out.println("方法参数");
        int result = num1/num2;
        System.out.println("方法结果"+result);
        return result;
    }
}

测试类Test:

Cal cal = new CalImpl();
cal.add(1,2);
cal.minus(2,3);
cal.div(4,5);
cal.multi(7,6);

实现接口的类里就需要写很多重复的语句,而且修改起来很麻烦,他们运许结果相同,动态代理类明显的解决了耦合作用,但是动态代理类用起来很麻烦,Spring封装了动态代理类
使用Spring

@Aspect
@Component
public class LoggerAspects {
    @Before("execution(public int cn.learnbyheart.impl.CalImpl.*(..))")
    public void before(JoinPoint joinPoint){ // JoinPoint 连接点
        // 在执行CalImpl里的方法前,先执行before()  输出第一条日志
        //   System.out.println(method.getName()+"方法的参数"+Arrays.toString(args));
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        // 获取参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"参数:"+args);

    }
    @After("execution(public int cn.learnbyheart.impl.CalImpl.*(..))")
    public void after(JoinPoint joinPoint){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"执行完毕");
    }

// 返回结果值
    @AfterReturning(value = "execution(public int cn.learnbyheart.impl.CalImpl.*(..))",returning = "result")
    public void afterReturing(JoinPoint joinPoint,Object result){ // 两个要一致  否则报错
            String name = joinPoint.getSignature().getName();
            System.out.println("方法的结果值"+result);
    }

// 抛异常
    @AfterThrowing(value = "execution(public int cn.learnbyheart.impl.CalImpl.*(..))",throwing = "exception")
        public void throwExp(JoinPoint joinPoint,Exception exception){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法抛出异常"+exception);
        
    
    }
}

注意在返回值那里,两个参数要一致,否则报错
@Aspect:表示该类是切面类
@Component:表示该类的对象注入到IOC容器中
具体的方法注解
@Before: 表示方法执行的具体位置和时机

同时要注意,CamImpl类也需要@Component交给IOC容器来管理

resources底下创建spring-aop.xml里配置AOP

<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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--自动扫描-->
<context:component-scan base-package="cn.learnbyheart"></context:component-scan>

<!-- aspect注解生效  为目标类自动生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
  • context:component-scan:将cn.learnbyheart包中的所有类扫描,如果该类也有@Component注解,那就扫描进IOC容器中
  • aop:aspectj-autoproxy 自动生成动态代理对象

测试类Test里:

public class Test2 {
    public static void main(String[] args) {
        // 加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        // 获取代理对象
        Cal proxy = (Cal) applicationContext.getBean("test");
        // 如果CalImpl的@Component没有写 就用类名首字母小写代替
        // Cal proxy = (Cal) applicationContext.getBean("calImpl");
        proxy.add(1,2);

    }
}

@Component("test") 作用相当于

<bean id="test" class="cn.learnbyheart.Impl.CalImpl"></bean>

最后:
切面:横切关注点被模块化的抽象对象
通知:切面对象完成的工作
目标:被通知的对象,即被横切的对象
代理:切面,通知,目标混合之后的对象,
连接点:通知要插入业务代码的具体位置
切点:AOP通过切点定位到连接点