基本概念
IOC:Inversion of Control —— 控制反转
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
DI:Dependency Injection —— 依赖注入
在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(依赖注入)来实现的。
比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。
AOP:Aspect Oriented Programming —— 面向切面编程
AOP(Aspect Oriented Programming)是一种编程范式,用于将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。通过这种方式,可以将诸如日志记录、事务管理、安全检查等功能模块化,从而提高代码的可维护性和可重用性。
核心概念
切面(Aspect):一个关注点的模块化,如日志记录、事务管理等。
连接点(Join Point):程序执行过程中的某个特定点,如方法调用、异常抛出等。
通知(Advice):在特定的连接点上执行的动作,如前置通知、后置通知等。
切入点(Pointcut):匹配连接点的表达式,用于指定哪些连接点应该应用通知。
织入(Weaving):将切面代码插入到普通业务代码中的过程,可以在编译时或运行时完成。
IOC和DI的关系
IOC和DI的关系
IoC 是一种设计思想,强调的是控制权的转移,即从传统的在代码中直接操控对象转为由外部容器来管理对象及其生命周期。
DI 是实现IoC的一种方式,通过依赖注入机制,让对象在运行时自动获得所需的依赖,从而降低对象间的耦合度。
实现步骤
DI的实现步骤:
在Java中,Spring框架提供了多种实现依赖注入的方式,包括构造器注入、设值注入(setter injection)以及字段注入(field injection)。下面以Spring Boot项目为例,展示如何使用构造器注入来实现依赖注入。
1.定义Service层接口和实现
public interface MyService {
String provideInfo();
}
@Service
public class MyServiceImpl implements MyService {
@Override
public String provideInfo() {
return "Hello from Service";
}
}
2.构造器注入(Construtor injection)——(官方推荐)
@Controller
public class MyController {
private final MyService myService;
// 使用构造器注入MyService实例
public MyController(MyService myService) {
this.myService = myService;
}
@GetMapping("/info")
public String getInfo() {
return myService.provideInfo();
}
}
3. 字段注入(Field Injection)——(最简单的方式)
@Service
public class MyService {
private String info;
public String provideInfo() {
return info;
}
}
@Controller
public class MyController {
// 使用字段注入MyService实例
@Autowired
private MyService myService;
@GetMapping("/info")
public String getInfo() {
return myService.provideInfo();
}
}
4. 设值注入(Setter Injection)
@Service
public class MyService {
private String info;
public String provideInfo() {
return info;
}
// Setter方法用于注入依赖
@Autowired
public void setInfo(String info) {
this.info = info;
}
}
@Controller
public class MyController {
private MyService myService;
// 使用setter注入MyService实例
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
@GetMapping("/info")
public String getInfo() {
return myService.provideInfo();
}
}
5.三种依赖注入方式比较
注入方式 | 可靠性 | 可维护性 | 可测试性 | 灵活性 | 循环关系的检测 | 性能影响 |
---|---|---|---|---|---|---|
Field | 不可靠 | 低 | 差 | 很灵活 | 不检测 | 启动快 |
Constructor | 可靠 | 高 | 好 | 不灵活 | 自动检测 | 启动慢 |
Setter | 不可靠 | 低 | 好 | 很灵活 | 不检测 | 启动快 |
AOP的实现步骤
AOP的实现方式
- 前置通知(Before Advice)
在方法执行前执行的通知。- 后置通知(After Returning Advice)
在方法成功返回后执行的通知。- 异常通知(After Throwing Advice)
在方法抛出异常后执行的通知。- 最终通知(After (Finally) Advice)
无论方法是否正常返回或抛出异常,都会执行的通知。- 环绕通知(Around Advice)
在方法执行前后都执行的通知,可以拦截方法调用。
示例代码
假设我们需要在所有方法调用前后添加日志记录功能,可以使用Spring AOP来实现。
1. 定义切面类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 定义环绕通知
@Around("execution(* com.example.demo.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("Method " + joinPoint.getSignature().getName() + " started");
try {
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("Method " + joinPoint.getSignature().getName() + " ended in " + (end - start) + "ms");
return result;
} catch (Throwable e) {
System.out.println("Exception in method " + joinPoint.getSignature().getName() + ": " + e.getMessage());
throw e;
}
}
}
2.定义目标类
import org.springframework.stereotype.Service;
@Service
public class MyService {
public String provideInfo() {
return "Hello from Service";
}
}
3.定义控制器
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
@GetMapping("/info")
public String getInfo() {
return myService.provideInfo();
}
}
4.AOP 的优势
- 模块化横切关注点:将日志记录、事务管理等通用功能模块化,提高代码的可维护性和可重用性。
减少重复代码:避免在多个地方重复编写相同的代码。
易于扩展:可以通过修改切面来轻松扩展功能,而无需修改业务逻辑代码。
5.总结
- AOP 是一种强大的编程范式,用于将横切关注点从业务逻辑中分离出来。通过定义切面、连接点、通知和切入点等概念,可以将日志记录、事务管理等功能模块化,从而提高代码的可维护性和可重用性。Spring AOP 提供了一套完整的工具和API来实现这一目标。
关注我!带你学习更多的编程技巧!