IOC
控制反转,将创建对象的权利交给容器概念类的回答就不提了,本专栏里的其他文章有讲到。
好处:
- 自由度会更大,IOC在创建对象的过程中提供了各种扩展点,更好的扩展性。
- 由容器统一的为我们设计一个单例工厂,所有的类不让程序员自己去new,如果我们使用new的方式去创建实例,每次使用一个对象就去new,特别在高并发场景下,就会导致内存中产生大量的bean,会产生更多的GC,让垃圾回收产生更大的压力,更有可能导致OOM。有了IOC容器之后,所有的创建过程全部在启动时期完成,同时内存中保存的对象都是单例状态,每次使用直接从内存中获取,这样内存的抖动不会过于严重。
- 在容器启动的时候帮我们去创建bean,这样如果一些bean的创建过程或者bean和bean之间依赖存在问题,将异常提前到启动的时候,提前发现问题。
- 最大的好处还是解耦,容器中存在大量的bean 这些bean不需要我们去new,以后的依赖注入,组装等一系列的操作全部由容器来完成,达到解耦的目的。
- 高层模块不直接依赖于低层模块,而是依赖于抽象,低层模块实现抽象,实现高层和低层模块的解耦。例如:controller类是高层,service类是低层,controller依赖的是service的接口,service类实现了service接口;service类是高层那么DAO类就是低层,service依赖的是DAO接口,DAO类实现了DAO接口。在容器里面我们需要将具体的实现类注入到容器,由容器来帮我们依赖注入即可,controller类中不会去new任何一个实现类,这样实现了controller和service的解耦。
- 很多时候我们只需要依赖接口去编程,甚至无需关心实现,而容器里面拥有这个接口所对应的实现类对象,那么具体实现的方法由容器调用。
- 在Spring的IOC(控制反转)容器中,我们可以通过依赖注入的方式将接口的实现类对象注入到需要使用的地方。这意味着我们只需关注接口的定义和使用,而不需要显式地创建实现类的对象。
- 当Spring容器启动时,它会扫描应用程序中的组件(使用注解或配置方式进行声明),并创建相应的对象。当遇到一个需要依赖注入的接口时,容器会查找并实例化对应的实现类,并将实例注入到需要的地方。
AOP
面向切面编程,对目标方法做增强,为原有的代码提供一些附加的能力。
- 在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。
- 而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
- AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用 CGLIB 方式实现动态代理。
SpringBoot中的运用AOP:
切面配置类:(建立配置包:config)
/**
* 自定义切面配置类
*/
@Configuration //代表当前这个类是一个spring的配置类
@Aspect //代表这个类是一个切面配置类
public class MyAspectConfig {
//切面Aspect = Advice 附加操作 + Pointcut切入点
//切入点表达式:
// 1.execution 方法级别切入点表达式 save update 运行效率越低
// 2.within:类级别切入点表达式: 控制越粗 运行效率
// 3.基于注解的切入点表达式 @annotation(com.baizhi.annotations.xxx)
下一个~~~~java解释了基于注解的切入点表达式
//@Around("execution(* com.baizhi.service.*.*(..))")
//@Around("within(com.baizhi.service.*)") 表示com.baizhi.service包下的所有类的所有方法
@Around("@annotation(com.baizhi.annotations.MyAdvice)")
//注意: 使用@Around注解 在方法定义时声明一个参数: ProceedingJoinPoint 处理过程中连接点
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("==========进入环绕的前置操作===========");
System.out.println("当前执行类: "+proceedingJoinPoint.getTarget());
System.out.println("执行的方法名: "+proceedingJoinPoint.getSignature().getName());
//放目标方法执行
Object proceed = proceedingJoinPoint.proceed();//继续处理 业务逻辑方法执行
System.out.println("==========进入环绕的后置操作===========");
return proceed;
}// 返回值作用: 将业务方法的核心业务逻辑的返回结果返回给调用者
-----------------------------------------------------------------
@Before("execution(* com.baizhi.service.*.*(..))")
//@Before 代表这是一个核心业务逻辑执行之前附加操作 value:用来书写切入点表达式 配置附加操作在哪里生效
public void before(JoinPoint joinPoint){
System.out.println("=========前置附加操作=========");
//注意: 使用@Before 和 @After注解声明方法上加入一个参数 定义一个参数 JointPoint 连接点
System.out.println("获取当前执行目标类: "+ joinPoint.getTarget());
System.out.println("获取当前执行目标类中方法: "+ joinPoint.getSignature().getName());
}
-----------------------------------------------------------------
//@After("execution(* com.baizhi.service.*.*(..))")
public void after(JoinPoint joinPoint){
System.out.println("========后置附加操作==========");
System.out.println("当前执行目标类: "+ joinPoint.getTarget());
System.out.println("当前执行目标类中方法: "+ joinPoint.getSignature().getName());
}
}
-----------------------------------
测试类:
public class UserServiceTests extends BasicTests{
@Autowired
private UserService userService;
@Test
public void testSave(){
userService.save("小陈");
}
基于注解的切入点表达式:
建立注解包annotations:放自定义注解
//指定运行时生效
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAdvice { //自定义一个注解 其中不写任何东西 将这个注解放在哪个方法上 哪个方法就是切入点
}
然后在自定义切面配置类上指定 刚刚自定义的注解:
@Around("@annotation(com.baizhi.annotations.MyAdvice)")
public void before(JoinPoint joinPoint){
System.out.println("=========前置附加操作=========");
}
依赖注入(DI)
spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。