Spring理解,重要概念及图解,2023秋招spring常见八股文

按照自己的需求,找到自己不会的地方去解决

1.Spring的核心

1)Spring的两大核心:IoC和AOP

Spring框架包含众多模块,如Core、Testing、Data Access、Web Servlet等,其中Core是整个Spring框架的核心模块。Core模块提供了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能,而这些功能以及其他模块的功能都是建立在IoC和AOP之上的,所以IoC和AOP是Spring框架的核心。

IoC(Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。

说到IoC就不得不说DI(Dependency Injection),DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观,所以很多时候我们就用DI来代替它,在很多时候我们简单地将IoC和DI划等号,这是一种习惯。而实现依赖注入的关键是IoC容器,它的本质就是一个工厂。

AOP(Aspect Oriented Programing)是面向切面编程思想,这种思想是对OOP的补充,它可以在OOP的基础上进一步提高编程的效率。简单来说,它可以统一解决一批组件的共性需求(如权限检查、记录日志、事务管理等)。在AOP思想下,我们可以将解决共性需求的代码独立出来,然后通过配置的方式,声明这些代码在什么地方、什么时机调用。当满足调用条件时,AOP会将该业务代码织入到我们指定的位置,从而统一解决了问题,又不需要修改这一批组件的代码。

2)IOC容器:BeanFactory和ApplicationContext

1.两种工厂的创建方式:

2.两种工厂的关系(区别)

底层图解:

总结

3)DI依赖注入

依赖注入有三种常见的注入方式:构造函数注入、Setter方法注入和接口注入。下面分别展示这三种注入方式的代码体现。

  1. 构造函数注入:

public class MyApplication {
    private MessageService messageService;
​
    // 通过构造函数注入依赖
    public MyApplication(MessageService messageService) {
        this.messageService = messageService;
    }
​
    public void processMessage(String message) {
        // 使用依赖对象
        messageService.sendMessage(message);
    }
}
  1. Setter方法注入:

public class MyApplication {
    private MessageService messageService;
​
    // 通过Setter方法注入依赖
    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }
​
    public void processMessage(String message) {
        // 使用依赖对象
        messageService.sendMessage(message);
    }
}
  1. 接口注入:

public interface MessageServiceInjector {
    MyApplication getApplication();
}
​
public class EmailServiceInjector implements MessageServiceInjector {
    public MyApplication getApplication() {
        // 创建依赖对象
        MessageService messageService = new EmailService();
​
        // 创建应用程序并注入依赖
        MyApplication application = new MyApplication(messageService);
​
        return application;
    }
}

在接口注入中,我们定义了一个MessageServiceInjector接口,它包含一个getApplication方法,用于获取MyApplication对象。然后,我们创建了一个EmailServiceInjector类,实现了MessageServiceInjector接口,并在getApplication方法中创建了依赖对象messageService,并将其注入到MyApplication对象中。

通过以上三种方式,我们可以将依赖对象注入到目标类中,实现依赖的解耦和灵活替换。具体选择哪种注入方式,取决于具体的需求和设计。

2.AOP和动态代理关系

动态代理于AOP基本逻辑关系:

OOP编程的垂直性造成代码冗余,不方便管理;

解决代码冗余这类问题的思维是AOP面向切面编程思想;

实现这一思想,就要用动态代理技术;代理技术直接使用很麻烦,所以不会直接用,而是使用spring Aop技术框架来解决该类问题,aop框架底层就用到动态代理技术。

通过AOP例子理解AOP

下面是一个简单的Java AOP代码示例:

  1. 定义一个切面类,实现切面逻辑:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
​
@Aspect
@Component
public class LoggingAspect {
​
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("Before executing the method");
    }
}
  1. 创建一个服务类,该类中的方法将被切面所拦截:

import org.springframework.stereotype.Service;
​
@Service
public class UserService {
​
    public void addUser(String username) {
        System.out.println("Adding user: " + username);
    }
}
  1. 创建一个配置类,启用AOP并扫描切面和服务类:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
​
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {
​
}
  1. 创建一个主类,加载配置类并获取服务类的实例:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
​
public class MainApp {
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
​
        userService.addUser("John");
​
        context.close();
    }
}

在上述示例中,切面类 LoggingAspect 使用 @Aspect 注解标记为切面,并使用 @Before 注解定义了一个前置通知,该通知会在执行 com.example.service 包下的任何方法之前执行。服务类 UserService 中的 addUser 方法会被切面所拦截,在方法执行之前会打印一条日志。配置类 AppConfig 使用 @EnableAspectJAutoProxy 注解启用了AOP,并通过 @ComponentScan 注解扫描了切面和服务类。主类 MainApp 加载配置类,并获取服务类的实例,然后调用 addUser 方法。

运行主类 MainApp,将会输出以下内容:

Before executing the method
Adding user: John

这表明切面逻辑在方法执行之前被调用,成功拦截了服务类的方法。

3.spring中bean的安全问题

补充:成员方法才存在线程安全问题

  • spring中的bean如果是无状态的(一般情况是),则是线程安全的;

  • spring中的bean如果是有状态的,则需要标注@Scope("prototype"),意思是允许多个实例

    相反的@Scope("singleton"),意思是IOC容器中只允许创建一个实例

4.事务(来保证操作的原子性)

补充:事务是数据库中的概念,但spring对事务进行了拓展

  • 在Spring框架中,一般需要在以下情况下使用事务

  1. 数据库操作:当需要进行数据库的增删改操作时,可以使用事务来确保操作的一致性和完整性。例如,插入用户信息和同时插入用户订单信息,这两个操作应该放在同一个事务中,要么都成功,要么都失败。

  2. 多个服务方法调用:当一个服务方法中调用了多个其他服务方法,并且这些方法需要保证一致性,可以使用事务来统一管理。例如,用户下单操作需要调用库存服务和支付服务,这些服务操作应该在同一个事务中。

  3. 并发操作:当多个并发请求需要修改同一份数据时,需要使用事务来保证数据的一致性。例如,多个用户同时对同一商品进行抢购,需要使用事务来控制商品库存的减少和订单的生成。

  4. 异常处理:当方法执行过程中发生异常,需要回滚之前的操作,可以使用事务来实现回滚。例如,用户购买商品时,如果支付过程中发生异常,需要回滚订单和库存的操作。

总之,一般需要在涉及到数据库操作、多个服务方法调用、并发操作和异常处理的场景下使用事务。事务可以确保操作的一致性和完整性,提供可靠的数据访问和更新机制。

  • 步骤

    1. 开启事务:在执行一组操作之前,通过调用数据库的事务管理器来开启一个事务。

    2. 执行操作:在事务中执行一组相关的操作,如插入、更新、删除等。

    3. 提交事务:如果所有操作都成功执行,通过调用事务管理器的提交方法来提交事务,将操作的结果永久保存到数据库中。

    4. 回滚事务:如果有任何操作失败或发生异常,通过调用事务管理器的回滚方法来回滚事务,撤销已经执行的操作,保持数据库的一致性。

  • .spring支持编程式事务声明式事务是实现事务管理的两种主要方式。(重点)**

    1. 编程式事务:编程式事务是通过编写代码来管理事务的方式。在编程式事务中,开发人员需要手动编写事务的开启、提交和回滚等逻辑。通常,编程式事务使用的是底层的事务管理API,如JDBC的事务管理API。以下是一个使用编程式事务的示例:

    Connection conn = null;
    try {
        conn = dataSource.getConnection();
        conn.setAutoCommit(false); // 开启事务
        // 执行数据库操作
        // ...
        conn.commit(); // 提交事务
    } catch (SQLException e) {
        if (conn != null) {
            try {
                conn.rollback(); // 回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        e.printStackTrace();
    } finally {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    1. 声明式事务:声明式事务是通过配置的方式来管理事务的方式。在声明式事务中,开发人员只需要在配置文件或注解中声明事务的属性,框架会自动根据配置来管理事务。常见的使用声明式事务的框架有Spring的事务管理机制。以下是一个使用声明式事务的示例:

    @Transactional
    public void doTransaction() {
        // 执行数据库操作
        // ...
    }

    在上述示例中,使用了@Transactional注解来声明事务,并将事务逻辑封装在doTransaction方法中。框架会根据注解的配置来管理事务的开启、提交和回滚等操作。

    总的来说,编程式事务需要手动编写事务管理的代码,灵活性较高,但工作量较大;而声明式事务通过配置的方式来管理事务,简化了开发人员的工作,但灵活性相对较低。选择使用哪种方式取决于具体的需求和项目的特点。

5.Spring事务失效的场景(都是代码执行出现异常,事务却没有回滚)

  • 异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出

  • 抛出检查异常,配置rollbackFor属性为Exception

  • 非public方法导致的事务失效,改为public

详细解释:

第一种:异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出(左图改成右图)

                    事务失效的情况                                                                  改正后

第二种:抛出检查异常,配置rollbackFor属性为Exception

第三种:public方法导致的事务失效,改为public

6.spring中bean的生命周期

7.三级缓存 (具体解决流程看视频解析)

可以解决spring中注入引起循环依赖问题

构造方法中的循环依赖问题需要配合@Lazy懒加载注解解决

8.spring中的常用注解

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值