学习目标
第1~4章中我们学习了MyBatis框架,这也是同学们接触的第一个框架。在学习和练习的过程中可以深刻体会到框架的强大之处及给开发人员带来的便利。(如果没有了解可以去我主页看看 第一至四章 的内容来学习)。从本章开始,将学习另一个非常重要的框架——Spring框架,这也是目前Java语言中最流行的框架。
5.1 Sping框架的历史由来
Spring框架是一个轻量级的企业级应用框架,兴起于2003年。当时流行的传统Java EE框架均为过于脆肿的"重量级"架构体系,其开发效率、开发难度和实现的性能都不能满足人们的需求。
Spring框架已经发展为一个功能丰富且易用的集成框架,其核心是一个完整的基于控制反转的轻量级容器,用户可以使用它建立自己的应用程序。在此容器之上,Spring框架提供了大量实用的服务,并将很多高质量的开源项目集成到统一的框架之上。
本章将带领同学们初步接触Spring框架的两个核心概念——IOC和AOP,感受Spring框架的神奇魅力。
5.2 Spring IOC
IOC的全称为Inversion of Control,即控制反转,意为把对资源的控制权反转过来。IOC不是一项开发技术,也不是具体功能,而是面向对象编程中的一种设计理念。
5.2.1 IOC和依赖注入
在Java中,IoC(控制反转)和依赖注入(Dependency Injection, DI)是紧密相关的概念,它们通常通过框架(如Spring)来实现。IoC是一种设计原则,用于减少代码间的耦合度,而依赖注入是实现IoC的一种具体方式。
下面我将用简单的Java代码示例来说明依赖注入的概念,但请注意,在纯Java中手动实现依赖注入可能会比较复杂且不易维护,因此通常建议使用像Spring这样的框架来自动处理依赖注入。不过,为了教学目的,我们可以手动模拟依赖注入的过程。
示例:手动实现依赖注入
假设我们有两个类,Car 和 Engine。Car 类依赖于 Engine 类。
Engine.java
public class Engine {
public void start() {
System.out.println("Engine started");
}
}
Car.java
在这个例子中,我们将通过构造函数来注入 Engine 依赖。
public class Car {
private Engine engine;
// 通过构造函数注入Engine依赖
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
System.out.println("Car started");
}
}
Main.java
在 Main 类中,我们手动创建 Engine 和 Car 的实例,并将 Engine 实例注入到 Car 实例中。
public class Main {
public static void main(String[] args) {
// 创建Engine实例
Engine engine = new Engine();
// 创建Car实例,并将Engine实例注入到Car中
Car car = new Car(engine);
// 使用Car
car.start();
}
}
使用Spring框架实现依赖注入
在Spring框架中,依赖注入是通过配置文件(XML或注解)自动完成的,无需手动编写注入代码。
Car.java(使用Spring注解)
import org.springframework.beans.factory.annotation.Autowired;
public class Car {
private Engine engine;
// 使用@Autowired注解自动注入Engine依赖
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
System.out.println("Car started");
}
}
然后,你需要在Spring的配置文件中声明 Engine 和 Car 的bean,或者使用Java配置类来配置它们。但是,由于这里只是代码示例,所以我没有展示完整的Spring配置。
请注意,Spring还支持其他类型的依赖注入,如通过setter方法注入、字段注入等,但构造函数注入是推荐的方式之一,因为它可以确保对象在被使用之前就已经被正确初始化。
5.2.2 第一个Spring程序
编写第一个Spring程序通常涉及几个基本步骤,包括设置Spring环境、创建一个简单的Spring Bean,以及编写一个主程序来启动Spring应用。下面是一个简单的例子,展示如何使用Spring Boot来快速搭建一个Spring程序。Spring Boot是Spring的一个子项目,它大大简化了基于Spring的应用开发。
1. 创建一个Maven项目
首先,你需要一个Java开发环境(如JDK)和Maven。然后,你可以使用任何IDE(如IntelliJ IDEA、Eclipse等)来创建一个新的Maven项目。
2. 添加Spring Boot依赖
在你的pom.xml文件中,添加Spring Boot的起步依赖(starter)。这里我们使用Spring Web Starter来创建一个简单的Web应用。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.5.2</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3. 创建一个简单的Spring Boot应用
创建一个Java类,作为你的Spring Boot应用的主入口。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
4. 创建一个Controller
为了演示Spring的Web功能,我们可以添加一个简单的REST Controller。
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Hello, Spring Boot!";
}
}
5. 运行你的应用
现在,你可以运行DemoApplication类中的main方法来启动你的Spring Boot应用。默认情况下,应用将运行在localhost:8080。打开浏览器,访问http://localhost:8080/,你应该会看到“Hello, Spring Boot!”的响应。
以上步骤展示了如何使用Spring Boot快速搭建一个基本的Spring Web应用。Spring Boot通过自动配置和依赖注入等特性,极大地简化了Spring应用的开发和部署。
5.3 Spring AOP
Spring AOP(面向切面编程)允许你在不修改源代码的情况下,为应用程序添加额外的行为(如日志、事务管理等)。在Spring中,AOP通常与AspectJ注解一起使用来实现。
下面是一个简单的Spring AOP示例,我们将创建一个日志切面(Aspect),该切面会在调用特定方法之前和之后执行日志记录。
首先,确保你的Spring项目中包含了AOP相关的依赖。如果你使用的是Maven,可以在pom.xml中添加如下依赖(注意版本号可能会更新):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注意:这里以Spring Boot为例,但Spring AOP同样适用于传统的Spring项目。
1. 定义一个切面(Aspect)
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 在任何目标方法执行之前执行
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
// 在任何目标方法执行之后执行
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
// 你可以根据需要添加更多通知(如@Around, @AfterReturning, @AfterThrowing等)
}
2. 创建一个服务类
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class MyService {
public void doSomething() {
System.out.println("Doing something...");
}
public void doAnotherThing() {
System.out.println("Doing another thing...");
}
}
3. 配置Spring以启用AOP
如果你使用的是Spring Boot,并且已经添加了spring-boot-starter-aop依赖,那么Spring Boot会自动配置AOP。否则,你可能需要在你的配置类中添加@EnableAspectJAutoProxy注解来启用AspectJ自动代理。
4. 测试
你可以通过编写一个单元测试或直接在Spring Boot应用中调用MyService的方法来测试AOP。当你调用MyService中的方法时,你应该会在控制台看到由LoggingAspect打印的日志。
注意
@Aspect注解标记的类是一个切面。
@Component注解使切面成为Spring容器中的一个Bean。
@Before、@After等注解定义了切面的通知(Advice),它们指定了在何时(如方法执行之前或之后)以及哪些方法(通过切点表达式指定)上执行附加行为。
切点表达式(如execution(* com.example.service..(…)))用于指定哪些连接点(JoinPoint)应该被拦截。在这个例子中,它匹配com.example.service包下所有类的所有方法。
5.3.1 什么是AOP
AOP(Aspect-Oriented Programming,面向切面编程)在Java中是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从主业务逻辑中分离出来,实现代码的模块化和重用。这种技术是对OOP(Object-Oriented Programming,面向对象编程)的补充和完善。在Java中,AOP主要通过动态代理或静态织入(如AspectJ框架)来实现。
AOP的基本概念
切面(Aspect):
切面是横切关注点的模块化,例如日志、事务、安全等。切面封装了跨多个类或对象的行为。
连接点(JoinPoint):
连接点是应用执行过程中可以插入切面的点,如方法调用、字段访问、异常处理等。在Spring AOP中,通常只支持方法级的连接点。
切点(Pointcut):
切点是对连接点的过滤,它指定了切面应该作用于哪些连接点。切点表达式用于定义这些连接点的集合。
通知(Advice):
通知是切面在特定连接点执行的动作,它定义了切面在何时以及如何应用其行为。通知类型包括前置通知(Before advice)、后置通知(After advice)、环绕通知(Around advice)等。
目标对象(Target Object):
目标对象是被一个或多个切面通知的对象。在AOP中,目标对象的执行会被切面所增强。
织入(Weaving):
织入是将切面应用到目标对象并导致代理对象创建的过程。织入可以在编译时、加载时或运行时进行。
AOP在Java中的实现
在Java中,AOP可以通过多种方式实现,包括使用Spring AOP框架、AspectJ等。
Spring AOP:
Spring AOP是基于动态代理的,它提供了对AOP的支持,但仅限于方法拦截。Spring AOP通过配置(XML或注解)来定义切面、切点和通知,并在运行时通过动态代理技术将切面织入到目标对象中。
AspectJ:
AspectJ是Java社区中最完整、最流行的AOP框架之一。它提供了丰富的AOP特性,包括编译时和加载时织入,以及完整的切点表达式语言。AspectJ既可以作为独立的AOP框架使用,也可以与Spring框架集成。
示例代码
以下是一个使用Spring AOP的简单示例,展示了如何定义一个切面来记录方法调用的日志。
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}
@Service
public class MyService {
public void doSomething() {
System.out.println("Doing something...");
}
}
在这个示例中,LoggingAspect是一个切面,它定义了两个通知:logBefore和logAfter。这两个通知分别在MyService类中的方法执行前后执行,并打印出方法名。
5.3.2 Spring AOP 在项目中运用
Spring AOP(面向切面编程)在Java项目中的应用广泛,主要用于处理跨多个类或方法的横切关注点,如日志记录、事务管理、安全控制等。下面我将通过一个简单的Spring Boot项目示例来展示如何在Java中使用Spring AOP。
1. 添加依赖
首先,确保你的Spring Boot项目中包含了Spring AOP的依赖。如果你使用的是Maven,可以在pom.xml文件中添加如下依赖:
<dependencies>
<!-- Spring Boot Starter AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 其他依赖... -->
</dependencies>
2. 创建一个切面
接下来,创建一个切面类,并使用AspectJ注解来定义切点(Pointcut)和通知(Advice)。
package com.example.demo.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 定义切点,匹配com.example.demo.service包下所有类的所有方法
@Before("execution(* com.example.demo.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
// 在目标方法执行前执行的代码
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
// 你还可以添加其他类型的通知,如@After, @AfterReturning, @AfterThrowing, @Around等
}
3. 创建一个服务类
然后,创建一个服务类,该类中的方法将被切面增强。
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class MyService {
public void doSomething() {
// 执行业务逻辑
System.out.println("Doing something...");
}
// 可以添加更多方法...
}
4. 调用服务类
最后,在你的Spring Boot应用中(例如通过控制器或测试类)调用MyService类的方法,并观察AOP的效果。
package com.example.demo;
import com.example.demo.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private MyService myService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
myService.doSomething(); // 调用服务方法,观察AOP日志
}
}
5. 运行和测试
启动Spring Boot应用,并观察控制台输出。你应该会看到在doSomething方法执行前后打印的日志,这证明了AOP切面已经成功应用于该方法。
标签:Spring,Sping,2023,IDEA,切面,AOP,org,import,public From: https://blog.csdn.net/2301_78884095/article/details/139998753注意事项
- 确保你的切面类被Spring容器管理,即使用了@Component或类似的注解。
- 切点表达式定义了哪些方法会被切面增强,确保它正确匹配了你想要增强的方法。
- Spring AOP默认只支持方法级别的增强,不支持字段或构造函数的增强。
- 对于更复杂的AOP需求,可以考虑使用AspectJ编译器进行编译时织入,但这需要额外的配置和依赖。
通过上述步骤,你可以在Java项目中有效地运用Spring AOP来处理横切关注点,提高代码的模块化和可维护性。