概述
在Java中,AOP(面向切面编程)和IoC(控制反转)是两种常见的设计模式,它们在现代Java应用程序中,尤其是在Spring框架中扮演着重要的角色,在软件工程中广泛应用的设计原则,特别是在企业级应用程序开发中。它们都旨在提高代码的模块化、可维护性和可扩展性。
一、Ioc(控制反转)
控制反转(Inversion of Control)是一种设计原则,用来减少计算机程序中的依赖关系,它允许程序在运行时动态地适应和配置对象之间的关系。在Java中,IoC通常通过依赖注入(DI)实现,其实就减少我们在业务层通过new 来创建dao层对象。
IoC的主要特点:
- 依赖注入(DI):组件之间的依赖关系由容器在运行时注入,而不是在代码中硬编码。
- 容器管理:容器负责创建对象、管理它们的生命周期和依赖关系。
- 松耦合:由于依赖关系是在运行时注入的,因此组件之间的耦合度降低。
Spring框架中的IoC:
- Spring使用IoC容器来管理应用程序中的对象,这些对象被称为Beans。
- 通过
@Autowired
注解,Spring可以自动装配Bean的依赖关系。- 可以在XML配置文件或使用注解(如
@Component
、@Service
、@Repository
)来配置Bean。
二、Aop(面向切面编程)
面向切面编程(Aspect Orient Programming)
是一种编程范式,它允许程序员将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。
Aop的主要特点:
- 切面:切面是封装横切关注点的模块,可以包含前置、后置、环绕和异常处理等通知。
- 连接点:程序执行过程中的某些点(如方法调用),可以在这些点上应用切面。
- 切点:定义了哪些连接点会被切面所通知。
- 织入:将切面应用到目标对象上并创建新的代理对象的过程。
Spring框架中的Aop:
- Spring AOP使用代理机制来实现AOP,可以在不修改目标对象的情况下增加额外的行为。
- 支持使用XML或注解(如
@Aspect
、@Before
、@After
、@Around
)来配置切面。- Spring AOP主要支持方法级别的切点。
Aop常用注解:
- @Aspect: 将一个类标识为一个切面(Aspect),这意味着这个类包含切面逻辑。
- @Before:前置通知,在目标方法执行之前执行。
- @After:后置通知,在目标方法执行之后执行,无论目标方法是否抛出异常。
- @AfterReturning:返回通知,在目标方法成功执行后执行,即目标方法返回结果后。
- @AfterThrowing:异常通知,在目标方法抛出异常后执行。
- @Around:环绕通知,可以包裹目标方法的执行,拥有最大的控制权,可以在目标方法执行前后执行自定义逻辑。
三、Aop和Ioc的关系
在Spring框架中,AOP和IoC是紧密集成的:
- IoC容器提供基础:Spring的IoC容器负责管理Bean的生命周期和依赖关系,为AOP提供运行时的对象。
- AOP在IoC基础上工作:Spring AOP使用IoC容器中的对象来创建代理,并将切面织入到这些对象中。
- 无缝集成:Spring允许你在IoC容器中声明Bean的同时声明它们为切面,并配置它们的织入点。
四、代码示例
Ioc代码示例
1. 创建接口
package com.demo.aop.ioc;
/**
* 文件名:DemoService
* 创建者:宁贝贝
* 创建时间:2024-08-20
* 描述:
*/
public interface DemoService {
void display();
}
2. 实现接口
package com.demo.aop.ioc;
/**
* 文件名:DemoServiceImpl
* 创建者:
* 创建时间:2024-08-20
* 描述:
*/
public class DemoServiceImpl implements DemoService{
@Override
public void display() {
System.out.println("此方法被调用。。");
}
}
3. 通过配置类将类加载至容器
package com.demo.aop.ioc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 文件名:DemoConfig
* 创建者:
* 创建时间:2024-08-20
* 描述:通过这个配置类创建一个DemoService对象
*/
@Configuration
@ComponentScan(basePackages = "com.demo.aop.ioc")
public class DemoConfig {
@Bean(value = "demoService") //这个注解主要是设置容器中对象的名称,(如果不设置默认的是首字母小写类名称)
public DemoService DemoService() {
return new DemoServiceImpl();
}
}
4. 测试类
package com.demo.aop.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 文件名:IocDemo
* 创建者:
* 创建时间:2024-08-20
* 描述:
*/
public class IocDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取demoService的Bean
DemoService demoService = context.getBean("demoService", DemoService.class);
demoService.display();
}
}
Aop代码示例
1. 创建业务接口
package com.demo.aop;
/**
* 文件名:MathService
* 创建者:
* 创建时间:2024-08-20
* 描述: 业务接口
*/
public interface MathService {
int add(int a, int b);
}
2. 业务实现类
package com.demo.aop;
/**
* 文件名:MathServiceImpl
* 创建者:
* 创建时间:2024-08-20
* 描述:
*/
public class MathServiceImpl implements MathService {
@Override
public int add(int a, int b) {
int sum = a+b;
System.out.println("执行计算方法 "+ sum);
return sum;
}
}
3. 创建配置类将bean交给容器管理
package com.demo.aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 文件名:AopConfig
* 创建者:
* 创建时间:2024-08-20
* 描述: EnableAspectJAutoProxy 开启aop
*/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.demo.aop")
public class AopConfig {
@Bean(value = "mathService") //这个注解主要是设置容器中对象的名称,(如果不设置默认的是首字母小写类名称)
public MathService BusServiceImpl() {
return new MathServiceImpl();
}
}
4. 创建切面类
package com.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 文件名:LoggingAspect
* 创建者:宁贝贝
* 创建时间:2024-08-20
* 描述:
*/
@Aspect
@Component
public class LoggingAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* @Pointcut注解定义了一个切点,用于匹配com.example.service包下所有类的所有方法
*/
@Pointcut("execution(* com.demo.aop.MathService.add(..))")
public void logOnMathServiceAdd() {}
/**
* @Before 注解的关键属性:
* value 或 pointcut:指定切点表达式,用于匹配哪些方法的执行将触发通知。如果不指定 value,则通知将应用于所有方法。
* joinPoint:方法参数,表示当前的连接点(例如,被通知的方法),可以用于获取方法签名、参数等信息。
* 其他可选参数包括 argNames(指定方法参数的名称以匹配特定的参数值)等。
* @param joinPoint
*/
@Before("logOnMathServiceAdd()")
public void logBeforeAddition(JoinPoint joinPoint) {
logger.info("前置方法 {},{}", joinPoint.getArgs()[0],joinPoint.getArgs()[1]);
}
/**
* @AfterReturning注解的关键属性:
* value 或 pointcut:指定切点表达式,用于匹配哪些方法的执行将触发通知。如果不指定value,则通知将应用于所有方法。
* returning:用于指定方法返回值的参数名称。在通知方法中,可以使用此参数名称来引用方法的返回值。
* @param joinPoint
* @param result
*/
@AfterReturning(pointcut = "logOnMathServiceAdd()", returning = "result")
public void logAfterAdditionReturning(JoinPoint joinPoint, Object result) {
logger.info("后置方法{},{},{}", joinPoint.getArgs()[0], joinPoint.getArgs()[1], result);
}
}
5. 创建测试类
package com.demo.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 文件名:AopDemo
* 创建者:
* 创建时间:2024-08-20
* 描述:
*/
public class AopDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
// 获取MathService的Bean
MathService mathService = context.getBean("mathService", MathService.class);
mathService.add(2, 3);
}
}
6. 执行结果
五、总结
通过结合使用AOP和IoC,Spring框架提供了一种强大的方式来构建松耦合、易于测试和维护的应用程序。AOP允许开发者将业务逻辑与横切关注点分离,而IoC则简化了组件之间的依赖管理和配置。