【IOC、DI、AOP】 水深无声 2023-09-26 19:38 116阅读 0赞 ## 1、IoC ## IoC(Inversion of Control,控制反转)是一种软件设计思想,它的核心思想是将对象之间的依赖关系交给容器来管理,从而降低对象之间的耦合度,提高代码的灵活性和可维护性。 在传统的编程模式中,一个对象需要依赖另一个对象时,通常是通过直接创建和管理这个对象来实现的。但是,随着软件系统的不断变化和扩展,这种硬编码方式会使得系统变得难以维护和扩展,因为对象之间的耦合度太高了。而 IoC 的思想则是将这种对象之间的依赖关系反转过来,由容器来管理和注入依赖关系,从而实现对象之间的解耦。 在实现 IoC 的过程中,通常使用依赖注入(Dependency Injection,DI)的方式来管理对象之间的依赖关系。依赖注入有三种实现方式: 构造函数注入:通过构造函数传递依赖对象的实例; Setter 方法注入:通过 Setter 方法传递依赖对象的实例; 接口注入:通过接口实现注入依赖对象的实例。 在 Spring 框架中,依赖注入是通过配置 Bean 的定义和属性来实现的。Spring 容器会自动读取配置文件,将 Bean 定义和属性信息装载到容器中,并根据依赖关系自动注入依赖对象的实例。这样就可以实现对象之间的松耦合,从而提高代码的灵活性和可维护性。 ### 以下是一个简单的代码示例: ### 首先,在 src/main/java 目录下创建一个名为 com.example.demo 的包。在该包下创建一个名为 MyApplication 的主应用程序类: package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class MyApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(MyApplication.class, args); // 从 Spring 容器中获取名为 "myBean" 的 bean MyBean myBean = (MyBean) context.getBean("myBean"); myBean.sayHello(); } } 在该类中,使用了 @SpringBootApplication 注解,它组合了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 注解,用于开启 Spring Boot 应用程序的自动配置和组件扫描功能。 接下来,在同一包下创建一个名为 MyBean 的类: package com.example.demo; import org.springframework.stereotype.Component; @Component("myBean") public class MyBean { public void sayHello() { System.out.println("Hello, World!"); } } 在该类中,我们使用了 @Component 注解,将该类声明为一个组件,并使用 “myBean” 作为该组件在 Spring 容器中的名称。 现在,我们可以运行 MyApplication 类,并从 Spring 容器中获取 MyBean 实例,并调用其 sayHello() 方法输出 “Hello, World!”。 ## 2、DI ## 依赖注入(Dependency Injection)是一种设计模式,它用于解决对象之间的依赖关系,特别是在面向对象编程中。该模式通过将依赖关系的创建和管理从客户端代码中解耦出来,从而使代码更加灵活、可维护和可测试。 在依赖注入模式中,对象不是直接创建或获取它们所依赖的对象,而是将它们的依赖关系交给外部的系统或组件来处理。这些依赖关系通常通过构造函数参数、属性或方法参数来传递,或者通过专门的容器来管理。 依赖注入可以有三种方式实现: 构造函数注入(Constructor Injection):在对象创建时,通过构造函数参数将依赖关系传递给对象。 属性注入(Property Injection):通过公开的属性或方法,将依赖关系注入到对象中。 接口注入(Interface Injection):定义一个接口,通过实现这个接口,来将依赖关系注入到对象中。 依赖注入的好处在于,它可以帮助我们实现代码的解耦和可测试性。通过将对象之间的依赖关系交给外部来管理,我们可以更轻松地对代码进行单元测试,并且在未来对代码进行重构时,也可以更轻松地修改依赖关系。 ### 以下是一个简单的示例代码: ### 假设我们有一个简单的服务类,名为MyService,其中有一个名为doSomething()的方法: public class MyService { public void doSomething() { System.out.println("Doing something..."); } } 现在我们想在另一个类中使用MyService类,我们可以使用Spring Boot的依赖注入功能将其注入到另一个类中。 首先,我们需要在MyService类上添加@Component注解,以将其标记为一个组件,以便Spring Boot可以自动扫描并创建该类的实例: @Component public class MyService { public void doSomething() { System.out.println("Doing something..."); } } 接下来,在我们要使用MyService类的类中,我们可以使用@Autowired注解来注入MyService类的实例: @Service public class MyOtherService { @Autowired private MyService myService; public void doSomethingElse() { // do A myService.doSomething(); // do B } } 在上面的示例中,我们在MyOtherService类中使用了@Autowired注解来注入MyService类的实例。然后,在doSomethingElse()方法中,我们使用myService实例调用了MyService类中的doSomething()方法。 这样,我们就可以使用Spring Boot的依赖注入功能轻松地管理我们的类之间的依赖关系。 ## 3、AOP ## AOP(面向切面编程)是一种面向对象编程的技术,它允许在程序运行时动态地添加横向关注点到应用程序中。如安全性、日志记录和事务处理等,从应用程序的核心业务逻辑中抽象出来,以便在多个应用程序组件中共享和重用。 AOP的核心是切面(Aspect),切面定义了一系列与横向关注点相关的通知(Advice),通知则是要在横向关注点发生的特定点执行的代码。在Spring Boot中,您可以使用注释或XML配置来定义切面和通知,并通过Spring AOP来织入切面和通知。 ### 有五种类型的通知: ### 前置通知(Before advice):在目标方法执行之前执行通知代码。 后置通知(After advice):在目标方法执行之后(无论方法是否发生异常)执行通知代码。 返回通知(After returning advice):在目标方法执行之后(方法正常返回时)执行通知代码。 异常通知(After throwing advice):在目标方法抛出异常时执行通知代码。 环绕通知(Around advice):在目标方法执行前后都可以执行通知代码。 ### 以下是使用Spring Boot AOP的基本步骤: ### 定义切面类,可以使用@Aspect注释。 在切面类中定义通知方法,可以使用@Before、@After或@Around等注释。 在通知方法中执行所需的操作,例如记录日志或验证用户。 在需要应用切面的类中注入切面,可以使用@Autowired注释。 下面是一个简单的示例,展示如何使用Spring Boot AOP记录方法调用时间: 定义一个切面类,可以使用@Aspect注释。 import org.apache.commons.lang3.time.StopWatch; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * 0、务必在 Spring Boot 启动类上面 包扫描此切面 * 1、放具体的接口或者具体的接口实现类都是可以的,效果一样 * execution(* com.lfsun.api.service.hello.HelloService.*(..)) * execution(* com.lfsun.service.main.HelloServiceImpl.*(..)) * * * 2、针对某一包下所有类 * 可以 execution(* com.lfsun.service.main..*(..)) * 我这里以具体的实现类为例,达到的效果都是一样的 * * 3、结果 * 执行 MyAspect 切面的环绕通知(前). * 执行 MyAspect 切面的前置通知 . * 执行 MyAspect 切面的返回通知. 结果是: hello world 666! * 执行 MyAspect 切面的后置通知. * 执行 MyAspect 切面的环绕通知(后). 结果是: hello world 666! * @author Administrator */ @Aspect @Component public class MyAspect { // 前置通知 //@Before("execution(* com.lfsun.service.main.HelloServiceImpl.*(..))") //@Before("execution(* com.lfsun.api.service.hello.HelloService.*(..))") @Before("execution(* com.lfsun.service.main..*(..))") public void beforeAdvice() { System.out.println("执行 MyAspect 切面的前置通知 ."); } // 后置通知 //@After("execution(* com.lfsun.service.main.HelloServiceImpl.*(..))") //@After("execution(* com.lfsun.api.service.hello.HelloService.*(..))") @After("execution(* com.lfsun.service.main..*(..))") public void afterAdvice() { System.out.println("执行 MyAspect 切面的后置通知."); } // 返回通知 //@AfterReturning(pointcut = "execution(* com.lfsun.service.main.HelloServiceImpl.*(..))", returning = "result") //@AfterReturning(pointcut = "execution(* com.lfsun.api.service.hello.HelloService.*(..))", returning = "result") @AfterReturning(pointcut = "execution(* com.lfsun.service.main..*(..))", returning = "result") public void afterReturningAdvice(Object result) { System.out.println("执行 MyAspect 切面的返回通知. 结果是: " + result); } // 异常通知 //@AfterThrowing(pointcut = "execution(* com.lfsun.service.main.HelloServiceImpl.*(..))", throwing = "exception") //@AfterThrowing(pointcut = "execution(* com.lfsun.api.service.hello.HelloService.*(..))", throwing = "exception") @AfterThrowing(pointcut = "execution(* com.lfsun.service.main..*(..))", throwing = "exception") public void afterThrowingAdvice(Exception exception) { System.out.println("执行 MyAspect 切面的抛出异常通知. 异常是: " + exception); } // 环绕通知 //@Around("execution(* com.lfsun.service.main.HelloServiceImpl.*(..))") //@Around("execution(* com.lfsun.api.service.hello.HelloService.*(..))") @Around("execution(* com.lfsun.service.main..*(..))") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("执行 MyAspect 切面的环绕通知(前)."); StopWatch stopWatch = new StopWatch(); stopWatch.start(); Object result = joinPoint.proceed(); System.out.println(String.format("执行 MyAspect 切面的环绕通知(后). 结果是: {%s},执行时间:{%d} ms", result, stopWatch.getTime())); return result; } }
还没有评论,来说两句吧...