searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Spring框架核心——基于IDEA实现IoC(二)

2022-11-28 09:01:14
38
0

0.引言

复习一下:IoC是用来解决代码耦合度过高所带来的修改难、维护难问题,即将创建对象的职责转移到了外部的IoC容器。我们写的对象都交给IoC容器管理,他们在容器中统称为Bean,需要的时候就从Bean中拿出来使用。

IoC的使用范围很广,本章节介绍一下IoC是如何实现AOP的

1.什么是AOP?为什么要用AOP?

大家都知道面向对象编程 ( OOP ),尤其是使用Java语言的开发者和学习者对于这个概念都很熟悉了。诚然面向对象有很多优势,但他也有着一些缺陷,其中很重要的一点就是:当需要为多个不具有继承关系的对象引入同一个公共行为时(例如日志、安全检测)我们只有在每个对象里单独引用公共行为,产生了大量的重复代码,程序不便于维护。
好像这句话有点抽象,举个简单的例子来说明一下:

我们在开发中很多时候都需要打印日志,比如说一个简单的加减乘除计算器类,就会有四个对应的方法,执行对应方法的前后都需要打印日志,如下:

public int add(int num1, int num2) {
    System.out.println("add方法的参数是[" + num1 + " , " + num2 "]");
    result = num1 + num2;
    System.out.println("add方法的结果是" + result);
    return result;
}

如上只是一个 加 方法,减乘除三个方法同样也需要写这样的日志输出,如果有更多的方法就需要写更多的日志输出。

细想一下就很恐怖了,如果以后需要修改计算方法或者日志输出的格式,那不是要修改非常多的类似的代码?实际上这些代码的逻辑都是一样的,那么有没有一种方法可以避免这个问题呢?这就是为什么我们要用到AOP的原因

有一张非常经典的图,很好地解释了AOP横向合并OOP纵向流程的逻辑:

把我们刚刚举例的计算器套入上面,那么这个横向的AOP流程就是日志输出,因为每一个方法都需要输出日志,只是参数不同而已

面向切面编程 ( AOP )关注横向,区别于 OOP 的纵向,是OOP的一种补充。开发者主要关心的是横切逻辑的编写,只需要很少的代码编写确定横切点有哪些,而不需要去为每个横切点添加横切逻辑,不然就是面向对象编程了

2.IoC与AOP的关系

讲了这么多AOP,这根我们的标题IoC有什么关系呢?实际上AOP是基于IoC的,他可以说是IoC在实际应用中的体现。

AOP的核心就是解耦,而解耦就是通过IoC来实现的

具体来说,我们使用代理(IoC容器)去管理我们需要实现AOP的类,在刚刚的例子里这个类是计算器。也就是说我们写输出日志的代码的时候,不可能还去new一个计算器对象然后对他进行操作,还是会产生重复代码,而且你怎么保证用户new对象的时候命名一致呢?这里我们就要用到IoC,反正是IoC容器创建的对象,我不需要关系他内部的逻辑是什么,只需要用get方法去获取这个对象里面的参数就可以了。

如果还理解不了,没关系我们后面写到实现步骤的时候还会说道。

3.利用IoC实现AOP编程

AOP包含了三个重要过程:
1.找到横切点:首要目标确定在程序的哪个位置进行横切逻辑
2.横切逻辑(业务代码):横切逻辑代码,这个就是横切业务代码,与aop无关
3.织入(weaving):将横切逻辑织入到横切点

3.1 找到横切点

还是用计算器的例子,很明显很切点就是执行计算方法的前后输出日志

3.2 横切逻辑(业务代码)

横切逻辑就是输出的这个日志,那么我们怎么利用IoC去编写这个逻辑代码呢?让我们先从计算器类的创建开始

1.首先我们定义一个接口,是基本的加减乘除操作;AOP实现必须定义接口!!!因为横切逻辑是绑定在基于接口的实现类上的

public interface CalAPI {
    public int add(int num1, int num2);
    public int sub(int num1, int num2);
    public int mul(int num1, int num2);
    public int div(int num1, int num2);
}

2.然后我们去写一个接口的实现类,非常简单的加减乘除,加上@C0omponent把这个类注入到IoC容器作为Bean来管理

package wy.springboot01.domain;

import org.springframework.stereotype.Component;
import wy.springboot01.Interface.CalAPI;
@Component
public class CalMachine implements CalAPI {

    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int sub(int num1, int num2) {
        return num1 - num2;
    }

    @Override
    public int mul(int num1, int num2) {
        return num1 * num2;
    }

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

3.在工程的pom.xml里面引入aspect依赖,即切面依赖

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

3.3 织入

1.重点重点:创建一个切面类,切面类也要交给spring管理(即Component),这样通过before和after注解才可以把代码逻辑织入计算器类

@Component
@Aspect  //声明它是一个切面对象
public class LoggerAspect {
    //在Callculator中所有方法(*)执行之前都去执行beforecal
    @Before("execution(public int wy.springboot01.domain.Callculator.*(..))")
    public void beforecal(JoinPoint joinPoint) {
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //getArgs获取参数
        System.out.println(name + "方法的参数是" + Arrays.toString(joinPoint.getArgs()));
    }
    //在Callculator中所有方法(*)执行之后都去执行aftercal,且返回值映射到result
    @AfterReturning(value = "execution(public int wy.springboot01.domain.Callculator.*(..))", returning = "result")
    public void aftercal(JoinPoint joinPoint, Object result){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //getArgs获取参数
        System.out.println(name + "方法的结果是" + result);
    }

}

这里就有一些需要注意的点;

a.可以看到切面对象所有方法引入的参数都是JoinPoint(切入点),这个JoinPoint实际上就是利用IoC创建的一个计算器对象,所以我们才可以使用getName、getArgs等方法来获取它的各种属性,方法之前的注解定义了切入点的具体位置(before、after)

b.这里是在类的所有方法前后都要输出日志,如果只是某些方法,可以使用:@Before("execution(public int wy.springboot01.domain.CalMachine.add())")

 

2.配置IoC,这里我们不使用上一篇文章中的扫包+注解的办法,而是用xml配置,因为这样在实现AOP会比较便利并且更加灵活。

我们这几就创建一个IoC.xml配置文件,加入如下代码,非常简单,只需要引入相关的beans、配置扫包的范围、开启AOP自动代理即可

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置扫包范围-->
    <context:component-scan base-package="wy.springboot01">

    </context:component-scan>
    <!--开启AOP自动代理-->
    <aop:aspectj-autoproxy>

    </aop:aspectj-autoproxy>
</beans>

3.最后就是写一个测试代码,我们依然叫做IoCTest,如下(注意这里创建context的方法和上一篇文章中不一样,我们调用的是xml的配置)

public class IoCTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IoC.xml");
        //context.getBean(User.class)利用IoC生成对象
        CalMachine calMachine = context.getBean(CalMachine.class);
        calMachine.add(2,3);
        calMachine.sub(2,3);
        calMachine.mul(2,3);
        calMachine.div(2,3);
    }
}

可以看到我们还顺便写了计算器四个方法的测试代码,看下会不会有日志输出

4.测试,结果如下,可以看到日志都输出了:

 


我们今天的例子就到这里了,是一个非常基础但是使用的IoC实现AOP的逻辑,顺便也介绍了一些IoC的xml实现方法,后续会继续更新IoC这个spring中非常重要的核心概念的其他使用操作

0条评论
0 / 1000
才开始学技术的小白
23文章数
2粉丝数
才开始学技术的小白
23 文章 | 2 粉丝
原创

Spring框架核心——基于IDEA实现IoC(二)

2022-11-28 09:01:14
38
0

0.引言

复习一下:IoC是用来解决代码耦合度过高所带来的修改难、维护难问题,即将创建对象的职责转移到了外部的IoC容器。我们写的对象都交给IoC容器管理,他们在容器中统称为Bean,需要的时候就从Bean中拿出来使用。

IoC的使用范围很广,本章节介绍一下IoC是如何实现AOP的

1.什么是AOP?为什么要用AOP?

大家都知道面向对象编程 ( OOP ),尤其是使用Java语言的开发者和学习者对于这个概念都很熟悉了。诚然面向对象有很多优势,但他也有着一些缺陷,其中很重要的一点就是:当需要为多个不具有继承关系的对象引入同一个公共行为时(例如日志、安全检测)我们只有在每个对象里单独引用公共行为,产生了大量的重复代码,程序不便于维护。
好像这句话有点抽象,举个简单的例子来说明一下:

我们在开发中很多时候都需要打印日志,比如说一个简单的加减乘除计算器类,就会有四个对应的方法,执行对应方法的前后都需要打印日志,如下:

public int add(int num1, int num2) {
    System.out.println("add方法的参数是[" + num1 + " , " + num2 "]");
    result = num1 + num2;
    System.out.println("add方法的结果是" + result);
    return result;
}

如上只是一个 加 方法,减乘除三个方法同样也需要写这样的日志输出,如果有更多的方法就需要写更多的日志输出。

细想一下就很恐怖了,如果以后需要修改计算方法或者日志输出的格式,那不是要修改非常多的类似的代码?实际上这些代码的逻辑都是一样的,那么有没有一种方法可以避免这个问题呢?这就是为什么我们要用到AOP的原因

有一张非常经典的图,很好地解释了AOP横向合并OOP纵向流程的逻辑:

把我们刚刚举例的计算器套入上面,那么这个横向的AOP流程就是日志输出,因为每一个方法都需要输出日志,只是参数不同而已

面向切面编程 ( AOP )关注横向,区别于 OOP 的纵向,是OOP的一种补充。开发者主要关心的是横切逻辑的编写,只需要很少的代码编写确定横切点有哪些,而不需要去为每个横切点添加横切逻辑,不然就是面向对象编程了

2.IoC与AOP的关系

讲了这么多AOP,这根我们的标题IoC有什么关系呢?实际上AOP是基于IoC的,他可以说是IoC在实际应用中的体现。

AOP的核心就是解耦,而解耦就是通过IoC来实现的

具体来说,我们使用代理(IoC容器)去管理我们需要实现AOP的类,在刚刚的例子里这个类是计算器。也就是说我们写输出日志的代码的时候,不可能还去new一个计算器对象然后对他进行操作,还是会产生重复代码,而且你怎么保证用户new对象的时候命名一致呢?这里我们就要用到IoC,反正是IoC容器创建的对象,我不需要关系他内部的逻辑是什么,只需要用get方法去获取这个对象里面的参数就可以了。

如果还理解不了,没关系我们后面写到实现步骤的时候还会说道。

3.利用IoC实现AOP编程

AOP包含了三个重要过程:
1.找到横切点:首要目标确定在程序的哪个位置进行横切逻辑
2.横切逻辑(业务代码):横切逻辑代码,这个就是横切业务代码,与aop无关
3.织入(weaving):将横切逻辑织入到横切点

3.1 找到横切点

还是用计算器的例子,很明显很切点就是执行计算方法的前后输出日志

3.2 横切逻辑(业务代码)

横切逻辑就是输出的这个日志,那么我们怎么利用IoC去编写这个逻辑代码呢?让我们先从计算器类的创建开始

1.首先我们定义一个接口,是基本的加减乘除操作;AOP实现必须定义接口!!!因为横切逻辑是绑定在基于接口的实现类上的

public interface CalAPI {
    public int add(int num1, int num2);
    public int sub(int num1, int num2);
    public int mul(int num1, int num2);
    public int div(int num1, int num2);
}

2.然后我们去写一个接口的实现类,非常简单的加减乘除,加上@C0omponent把这个类注入到IoC容器作为Bean来管理

package wy.springboot01.domain;

import org.springframework.stereotype.Component;
import wy.springboot01.Interface.CalAPI;
@Component
public class CalMachine implements CalAPI {

    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int sub(int num1, int num2) {
        return num1 - num2;
    }

    @Override
    public int mul(int num1, int num2) {
        return num1 * num2;
    }

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

3.在工程的pom.xml里面引入aspect依赖,即切面依赖

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

3.3 织入

1.重点重点:创建一个切面类,切面类也要交给spring管理(即Component),这样通过before和after注解才可以把代码逻辑织入计算器类

@Component
@Aspect  //声明它是一个切面对象
public class LoggerAspect {
    //在Callculator中所有方法(*)执行之前都去执行beforecal
    @Before("execution(public int wy.springboot01.domain.Callculator.*(..))")
    public void beforecal(JoinPoint joinPoint) {
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //getArgs获取参数
        System.out.println(name + "方法的参数是" + Arrays.toString(joinPoint.getArgs()));
    }
    //在Callculator中所有方法(*)执行之后都去执行aftercal,且返回值映射到result
    @AfterReturning(value = "execution(public int wy.springboot01.domain.Callculator.*(..))", returning = "result")
    public void aftercal(JoinPoint joinPoint, Object result){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //getArgs获取参数
        System.out.println(name + "方法的结果是" + result);
    }

}

这里就有一些需要注意的点;

a.可以看到切面对象所有方法引入的参数都是JoinPoint(切入点),这个JoinPoint实际上就是利用IoC创建的一个计算器对象,所以我们才可以使用getName、getArgs等方法来获取它的各种属性,方法之前的注解定义了切入点的具体位置(before、after)

b.这里是在类的所有方法前后都要输出日志,如果只是某些方法,可以使用:@Before("execution(public int wy.springboot01.domain.CalMachine.add())")

 

2.配置IoC,这里我们不使用上一篇文章中的扫包+注解的办法,而是用xml配置,因为这样在实现AOP会比较便利并且更加灵活。

我们这几就创建一个IoC.xml配置文件,加入如下代码,非常简单,只需要引入相关的beans、配置扫包的范围、开启AOP自动代理即可

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置扫包范围-->
    <context:component-scan base-package="wy.springboot01">

    </context:component-scan>
    <!--开启AOP自动代理-->
    <aop:aspectj-autoproxy>

    </aop:aspectj-autoproxy>
</beans>

3.最后就是写一个测试代码,我们依然叫做IoCTest,如下(注意这里创建context的方法和上一篇文章中不一样,我们调用的是xml的配置)

public class IoCTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IoC.xml");
        //context.getBean(User.class)利用IoC生成对象
        CalMachine calMachine = context.getBean(CalMachine.class);
        calMachine.add(2,3);
        calMachine.sub(2,3);
        calMachine.mul(2,3);
        calMachine.div(2,3);
    }
}

可以看到我们还顺便写了计算器四个方法的测试代码,看下会不会有日志输出

4.测试,结果如下,可以看到日志都输出了:

 


我们今天的例子就到这里了,是一个非常基础但是使用的IoC实现AOP的逻辑,顺便也介绍了一些IoC的xml实现方法,后续会继续更新IoC这个spring中非常重要的核心概念的其他使用操作

文章来自个人专栏
Java开发者绕不开的技术
12 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
1
0