Spring IoC 与 AOP 的基本概念

一、 IoC

IoC (inversion of control) 控制反转:将对象的创建进行反转,常规情况下,对象都是由开发者手动创建的,使用 IoC,开发者不再需要创建对象,而是由 IoC 容器 根据需求自动创建项目所需要的对象。

不用 IoC:所有对象由开发者手动创建

使用 IoC:对象不在由开发者创建,而是由 Spring 框架完成

普通人找对象需要主动付出,而皇帝找对象只需在后宫三千中选择。所以说 IoC 给予了开发者皇帝般的享受。

1. pom.xml

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.15</version>
</dependency>

2. 配置要让 Spring 框架创建的对象

方法1:基于 XML;方法2:基于注解

准备一个类

@Data // lombok注解之一,用于生成 get 和 set 方法
public class DataConfig {
  private String url;
  private String driverName;
  private String username;
  private String password;
}
2.1 基于 XML

开发者把需要交给Spring框架创建的对象在 XML 中进行配置,Spring 框架读取这个配置文件,根据配置文件的内容来创建对象

2.1.1 在 resources 目录下创建 spring.xml (名称任意),并配置对象
<beans>
  <!--创建一个名字为 config 的 DataConfig 的对象-->
  <bean class="com.mohanqing.ioc.DataConfig" id="config"> 
    <!-- 给属性赋值 -->
    <property name="driverName" value="Driver"></property>
    <property name="url" value="localhost:3306/mohanqing"></property>
    <property name="password" value="1234"></property>
  </bean>
</beans>
2.1.2 获取对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); // IoC 容器
System.out.println(context.getBean("config")); // 通过名字获取
2.2 基于注解

方法1:配置类;方法2:注解 + 扫包

2.2.1 配置类

用一个 Java 类来替代 XML 文件,把在 XML 中配置的内容放到配置类中

@Configuration // 配置类注解
public class BeanConfiguration { // 这个类相当于 XML 文件

  //@Bean // 这个方法相当于 XML 文件中的 <bean>,默认 id 为方法名 dataConfig
  @Bean(value = "config") // 自定义 id,也可以使用 name = "config"
  public DataConfig dataConfig(){
    DataConfig dataConfig = new DataConfig();

    dataConfig.setDriverName("Driver");
    dataConfig.setUrl("localhost:3306/mohanqing");
    dataConfig.setUsername("root");
    dataConfig.setPassword("1234")

    return dataConfig;
  }
}
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class);
System.out.println(context.getBean(DataConfig.class)); // 通过类型获取
// System.out.println(context.getBean("dataConfig")); // 通过默认 id(方法名)获取
System.out.println(context.getBean("config")); // 通过自定义的 id 获取

// ApplicationContext context = new AnnotationConfigApplicationContext("com.mohanqing.configuration"); // 扫包
// System.out.println(context.getBean("config"));
2.2.2 注解 + 扫包

不需要配置类,而是直接将 bean 的创建交给目标类,在目标类添加注解

@Data
@Component // 将这个类交给 IoC 容器管理,创建这个类的对象
public class DataConfig {
  @Value("localhost:3306/mohanqing") // 赋值
  private String url;
  @Value("Driver")
  private String driverName;
  @Value("root")
  private String username;
  @Value("1234")
  private String password;
}
ApplicationContext context = new AnnotationConfigApplicationContext("com.mohanqing.ioc"); // 扫描这个包下的所有类,如果类上有 @Component, 就会被注入到 IoC 中
System.out.println(context.getBean(DataConfig.class)); // 通过类型获取

依赖注入

A 中有 B,创建 A对象 和 B对象,自动的将 B 装入 A 中

@Data
@Component
public class GlobalConfig {

  @Value("8080")
  private String port;
  @Value("/")
  private String path;
  
  @Autowired // 自动装配,通过类型注入
  private DataConfig dataConfig;
}
ApplicationContext context = new AnnotationConfigApplicationContext("com.mohanqing.ioc");
System.out.println(context.getBean(GlobalConfig.class)); // 通过类型获取

@Autowired 通过类型进行注入,如果需要通过名称取值,则需要通过 @Qualifier 完成名称的映射

@Data
@Component
public class GlobalConfig {

  @Value("8080")
  private String port;
  @Value("/")
  private String path;
  
  @Autowired
  @Qualifier("config")
  private DataConfig config;
}
@Data
@Component("config") // 指定名称
public class DataConfig {
  @Value("localhost:3306/mohanqing")
  private String url;
  @Value("Driver")
  private String driverName;
  @Value("root")
  private String username;
  @Value("1234")
  private String password;
}

二、AOP

AOP (Aspect-Oriented Programming) 面向切面编程,一种抽象化的面向对象编程

1. 没有 AOP 时

没有AOP时的程序核心业务逻辑和其它的非业务代码耦合在一起

没有AOP时

public class CalImpl implements Cal {

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

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

问题:每个方法都包含相同的横切关注点代码(权限、日志、事务等)

2. 有 AOP 时

AOP 把切面抽象成对象,做到核心业务和非业务代码的解耦合

有AOP时

AOP 底层使用动态代理机制

AOP底层

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.3.15</version>
</dependency>
// 核心业务类(只关注业务)
public class CalImpl implements Cal {

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

  @Override
  public int sub(int num1, int num2) {
    int result = num1 - num2;
    return result;
  }
}
// 切面类(处理横切关注点)
@Aspect // 声明为切面
@Component // 注入 IOC
public class LoggerAspect {

  @Before("execution(public int com.mohanqing.aop.CalImpl.*(..))") // 目标方法执行之前
  public void before(JoinPoint joinPoint) {

    // 获取方法名
    String name = joinPoint.getSignature().getName();

    // 获取方法的参数
    System.out.println(name + "方法的参数是" + Arrays.toString(joinPoint.getArgs()));
  }

  @AfterReturning(value = "execution(public int com.mohanqing.aop.CalImpl.*(..))", returning = "result") // returning = "result" 绑定返回结果
  public void afterReturn(JoinPoint joinPoint, Object result) {
    String name = joinPoint.getSignature().getName();
    System.out.println(name + "方法的结果是" + result);
  }
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐