IoC(控制反转)和 DI(依赖注入)
IoC 是 Spring 的核心理念之一,也是 Spring 最为著名的特性之一。它将对象的创建、管理和销毁等过程交给 Spring 容器来完成。DI 则是 IoC 的具体实现,它通过注入依赖对象的方式来完成对象之间的解耦。
IoC(控制反转)是一种设计模式,它将对象的创建、管理和销毁等过程交给容器来完成,而不是由代码显式地进行。这样可以降低代码的耦合度和复杂度,并且提高代码的可测试性和可维护性。
DI(依赖注入)是 IoC 的具体实现方式之一,它通过注入依赖对象的方式来完成对象之间的解耦。在 Spring 中,我们可以通过构造方法注入、setter 方法注入和字段注入等方式实现依赖注入。
下面是一些常见的 IoC 和 DI 相关的概念:
Bean’
Bean 是一个由 Spring 容器管理的对象。在 Spring 中,我们可以将 Java 类声明为一个 Bean,并由容器来创建、初始化和销毁它。
ApplicationContext
ApplicationContext 是 Spring 提供的一个接口,它是 IoC 容器的核心接口。ApplicationContext 在初始化时会创建 Bean 实例,并管理它们的生命周期。
BeanFactory
BeanFactory 是 ApplicationContext 的父接口,它也是 Spring 容器的核心接口。BeanFactory 只负责管理 Bean 实例的生命周期,而不会主动地创建 Bean 实例。
配置文件
Spring 中的配置文件通常使用 XML 格式,用于描述应用程序中的 Bean、Bean 之间的依赖关系、以及其他相关的配置信息。
现在我们来看一下如何在 Java 代码中实现 IoC 和 DI。
1. 声明 Bean
我们可以使用 @Component、@Service、@Controller、@Repository 等注解来将一个 Java 类声明为一个 Bean。这些注解都是 @Component 的派生注解,它们的作用是告诉 Spring 容器,这个类需要被管理。
例如:
@Service public class UserService { // ... }
2. 使用 ApplicationContext
下面是一个简单的使用 ApplicationContext 的例子,其中通过 getBean() 方法获得了 UserService 实例:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean(UserService.class);
3. 依赖注入
我们可以使用 @Autowired 注解、构造方法注入或 setter 方法注入等方式来实现依赖注入。
例如:
@Service public class UserService { private UserDao userDao; @Autowired public UserService(UserDao userDao) { this.userDao = userDao; } // 或者使用 setter 方法注入 // @Autowired // public void setUserDao(UserDao userDao) { // this.userDao = userDao; // } }
通过上述代码,Spring 在创建 UserService 对象时会自动注入一个 UserDao 实例,从而实现了对象之间的解耦。
AOP(面向切面编程)
AOP 是 Spring 的另一个核心特性,它可以在不修改原有代码的情况下,通过切入点和通知等机制,动态地添加或移除一些功能模块,比如日志记录、安全检查等。
AOP(面向切面编程)是一种编程范式,它可以在不修改原有代码的情况下,通过动态地横向切入程序执行流程中的某些特定点,往程序中添加或删除业务逻辑。AOP 通过将横切关注点与正常的业务逻辑分离来提高代码的可维护性和可扩展性。
Spring AOP 是基于代理模式实现的,它通过拦截器来实现对方法的增强。在 Spring 中,我们可以通过配置切面、切入点和通知等机制来实现 AOP。
下面是一些常见的 AOP 相关的概念:
Join Point
Join Point 是指在程序执行过程中的某个特定点,比如方法调用、异常处理等。
Pointcut
Pointcut 是指一个表达式,它定义了哪些 Join Point 应该被拦截。比如可以使用表达式 execution(* com.example….(…)) 来匹配 com.example 包及其子包下的所有方法。
Advice
Advice 是指在拦截到 Join Point 后要执行的逻辑,也就是增强的具体实现方式。Spring 提供了四种类型的 Advice,分别是 Before、After、Around 和 AfterReturning。
Aspect
Aspect 是指一个横切关注点,它可以是一个类或者一个注解。Aspect 定义了 Pointcut 和 Advice,用于描述哪些 Join Point 需要被拦截以及拦截后需要执行的逻辑。
现在我们来看一下如何在 Java 代码中实现 AOP。
1. 声明切面
我们可以使用 @Aspect 注解将一个类声明为切面。在切面中,通常会定义多个 Pointcut 和对应的 Advice。
例如:
@Aspect @Component public class LogAspect { // 定义一个 Pointcut,匹配所有 UserService 的方法 @Pointcut("execution(* com.example.service.UserService.*(..))") public void userServicePointcut() {} // Before Advice,在 UserService 方法执行前输出日志 @Before("userServicePointcut()") public void logBefore(JoinPoint joinPoint) { System.out.println("before " + joinPoint.getSignature().getName()); } }
2. 在配置文件中启用 AOP
我们需要在 Spring 配置文件中启用 AOP,并将切面添加到容器中。
例如:
<aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> <bean id="logAspect" class="com.example.aspect.LogAspect"/>
3. 测试
最后,我们可以编写一个测试类来验证 AOP 是否生效。
例如:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:applicationContext.xml"}) public class UserServiceTest { @Autowired private UserService userService; @Test public void test() { userService.addUser(new User()); userService.deleteUser(123); } }
通过上述代码,我们可以看到在 UserService 的方法执行前会输出日志,这就是 AOP 在工作。
以上就是 AOP 的一些基本概念和 Java 代码实现。需要注意的是,Spring 还有很多高级特性和用法,需要深入学习才能掌握。