IOC(控制反转)
所谓IOC(Inversion of Controller),其实就是一种设计原则,主要思想就是把对象的创建和对象间的依赖关系的管理从我们的代码中抽离出来,由外部容器进行管理
在Spring框架中,Spring IOC容器便是这个外部容器,作为开发者, 我们只需要告诉Spring IOC我们需要的类和类之间的依赖关系,它就能自动帮我们实例化和配置这些类的实例了
IOC在Spring中的实现是由依赖注入(Dependence Injection,DI) 来完成的,具体的,可以由构造器的参数、工厂方法的参数或对象实例变量设置三种方式进行注入
构造器的参数
// 依赖类
class Service {
public void execute() {
System.out.println("Service executed.");
}
}
// 依赖注入目标类
class Client {
private final Service service;
// 通过构造器注入依赖
public Client(Service service) {
this.service = service;
}
public void doSomething() {
service.execute();
}
}
// 使用构造器注入
public class Main {
public static void main(String[] args) {
Service service = new Service();
Client client = new Client(service);
client.doSomething();
}
}
工厂方法的参数
// 依赖类
class Service {
public void execute() {
System.out.println("Service executed.");
}
}
// 依赖注入目标类
class Client {
private Service service;
private Client(Service service) {
this.service = service;
}
public void doSomething() {
service.execute();
}
// 工厂方法
public static Client createClient(Service service) {
return new Client(service);
}
}
// 使用工厂方法注入
public class Main {
public static void main(String[] args) {
Service service = new Service();
Client client = Client.createClient(service);
client.doSomething();
}
}
对象实例变量设置(即Setter方法)
// 依赖类
class Service {
public void execute() {
System.out.println("Service executed.");
}
}
// 依赖注入目标类
class Client {
private Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomething() {
service.execute();
}
}
// 使用setter方法注入
public class Main {
public static void main(String[] args) {
Service service = new Service();
Client client = new Client();
client.setService(service);
client.doSomething();
}
}
AOP
所谓AOP(Aspect-Oriented Programming)就是面向切面编程,主要思想是将横切关注点(其实就是多个类中重复出现的功能或逻辑,通常是日志、事务等)从业务中抽离出来,从而达到减少重复的代码,让逻辑更清晰的目的
对于Spring 来说,Spring AOP是通过动态代理来实现的,如果目标对象实现了接口,就用jdk动态代理,如果未实现接口就用cglib动态代理。
这里还涉及到多个概念:切面、通知、连接点、切点
切面:即通知和切点的组合
通知:定义切面的工作什么,什么时候去做
切点:定义了我们要在哪些连接点应用切面
连接点:在应用执行过程中能够插入切面(Aspect)的一个点,可以是被代理对象的某个动作,连接点有四种:
1.方法执行:在方法开始执行和结束执行的位置
2.构造函数调用:在构造函数调用前后
3.字段访问:在字段读取或写入时
4.异常抛出:在方法抛出异常时
举例说明:
将被代理的类:
package com.example.demo;
import org.springframework.stereotype.Component;
@Component
public class BankAccount {
private double balance;
public BankAccount() {
this.balance = 0;
}
public void deposit(double amount) {
// 连接点:方法执行前后插入日志
balance += amount;
System.out.println("Deposit successful. New balance: " + balance);
}
public void withdraw(double amount) {
// 连接点:方法执行前后插入日志
if (amount <= balance) {
balance -= amount;
System.out.println("Withdrawal successful. New balance: " + balance);
} else {
System.out.println("Insufficient balance for withdrawal.");
}
}
public double getBalance() {
return balance;
}
}
切面类:
package com.example.demo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 切面
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.demo.BankAccount.*(..))") // 切点
public void allMethods() {}
@Before("allMethods()") // 通知
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before executing: " + joinPoint.getSignature().getName());
}
@After("allMethods()") // 通知
public void logAfter(JoinPoint joinPoint) {
System.out.println("After executing: " + joinPoint.getSignature().getName());
}
}
如上代码,分别展示了切面、通知、连接点、切点在Spring应用中的展现