Spring知识点总结3 Spring框架

什么是 Spring 框架?

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inverse of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。
 
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
 

Spring,SpringMVC,SpringBoot,SpringCloud有什么区别?

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。Spring使你能够编写更干净、更可管理、并且更易于测试的代码。
Spring MVC是Spring的一个模块,一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。主要针对的是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。
Spring配置复杂,繁琐,所以推出了Spring boot,约定优于配置,简化了spring的配置流程。
Spring Cloud构建于Spring Boot之上,是一个关注全局的服务治理框架。
 
Spring VS SpringMVC:
Spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案;
SpringMVC是Spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于Spring框架中WEB层开发的一部分;
 
 
SpringMVC VS SpringBoot:
SpringMVC属于一个企业WEB开发的MVC框架,涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等,XML、config等配置相对比较繁琐复杂;
SpringBoot框架相对于SpringMVC框架来说,更专注于开发微服务后台接口,不开发前端视图;
 
SpringBoot和SpringCloud:
SpringBoot使用了默认大于配置的理念,集成了快速开发的Spring多个插件,同时自动过滤不需要配置的多余的插件,简化了项目的开发配置流程,一定程度上取消xml配置,是一套快速配置开发的脚手架,能快速开发单个微服务;
SpringCloud大部分的功能插件都是基于SpringBoot去实现的,SpringCloud关注于全局的微服务整合和管理,将多个SpringBoot单体微服务进行整合以及管理;SpringCloud依赖于SpringBoot开发,而SpringBoot可以独立开发;
 
 
Spring是核心,提供了基础功能;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是为简化Spring配置的快速开发整合包;
Spring Cloud是构建在Spring Boot之上的服务治理框架。
 
 
 
 

Spring中注入依赖的方式

  • 基于 field 注入(属性注入)注解
  • 基于 setter 注入
  • 基于 constructor 注入(构造器注入)

 

基于 field 注入

所谓基于 field 注入,就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是我平常开发中看的最多也是最熟悉的一种方式,同时,也正是 Spring 团队所不推荐的方式。比如:
@Autowired private SCL scl;
基于 field 注入的好处
正如你所见,这种方式非常的简洁,代码看起来很简单,通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了,不需要特殊的构造器或者set方法,依赖注入容器会提供你所需的依赖。
基于 field 注入的坏处
基于 field 注入虽然简单,但是却会引发很多的问题。这些问题在我平常开发阅读项目代码的时候就经常遇见。
容易违背了单一职责原则
使用这种基于 field 注入的方式,添加依赖是很简单的,就算你的类中有十几个依赖你可能都觉得没有什么问题,普通的开发者很可能会无意识地给一个类添加很多的依赖。
但是当使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则
 
依赖注入与容器本身耦合
依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说,这个类应该是一个简单的POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。
这个问题具体可以表现在:
  • 你的类和依赖容器强耦合,不能在(ioc)容器外使用
  • 你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化,这更像是集成测试
不能使用属性注入的方式构建不可变对象(final 修饰的变量)
 

构造器注入

(Constructor Injection)
 
@Service public class UserServiceImpl implements UserService 
{ private final UserMapper userMapper;
 @Autowired
 public UserServiceImpl(UserMapper userMapper) 
 { 
     this.userMapper = userMapper;
 
 } }
优点:
显式注明必须强制注入,通过强制指明依赖注入来保证这个类的运行,防止NullPointerException;
注入对象可以使用final修饰;
非IOC容器环境也可使用new实例化该类的对象;
避免循环依赖,如果存在循环依赖,spring项目启动的时候就会报错;
弊端:
当你有十几个甚至更多对象需要注入时,构造函数的代码臃肿,看起来不太舒服;
 
 

 

setter方法注入

(Setter Injection)
@Service
public class UserServiceImpl implements UserService 
{ 
    private UserMapper userMapper;
    @Autowired 
    public void setUserMapper(UserMapper userMapper) 
    { 
        this.userMapper = userMapper; 
    
    } }
优点:
依赖注入中使用的依赖是可选的,选择依赖的意思是注入的依赖是可以为 NULL;
允许在类构造完成后重新注入;
弊端:
  • 注入对象不能使用final修饰;
  • 如果注入的属性是必选的属性,则通过构造器注入;
  • 如果注入的属性是可选的属性,则通过setter方法注入;
  • 至于field注入,不建议使用;
 
spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 
service),在被调用时可以保证它们都完全准备好了
 
 
 
 

 

什么是循环依赖?

类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。
0
 
@Component 
public class A { 
    // A中注入了B
    @Autowired 
    private B b;
} 
@Component
public class B { // B中也注入了A
    @Autowired 
    private A a; }
自己依赖自己
// 自己依赖自己 
@Component
public class A { 
    // A中注入了A
    @Autowired 
    private A a; }
 
 
 

什么情况下循环依赖可以被处理?

在回答这个问题之前首先要明确一点,Spring解决循环依赖是有前置条件的
  1. 出现循环依赖的Bean必须要是单例
  2. 依赖注入的方式不能全是构造器注入的方式


@Component public class A 
{ // 
    @Autowired //
    private B b; public A(B b) { } }
@Component public class B { // 
    @Autowired // 
    private A a;
    public B(A a){ 
    } }
在上面的例子中,A中注入B的方式是通过构造器,B中注入A的方式也是通过构造器,这个时候循环依赖是无法被解决,如果你的项目中有两个这样相互依赖的Bean,在启动时就会报出以下错误:
 
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
 
测试循环依赖的解决情况跟注入方式的关系,做如下四种情况的测试
依赖情况
依赖注入方式
循环依赖是否被解决
AB相互依赖(循环依赖)
均采用setter方法注入
AB相互依赖(循环依赖)
均采用构造器注入
AB相互依赖(循环依赖)
A中注入B的方式为setter方法,B中注入A的方式为构造器
AB相互依赖(循环依赖)
B中注入A的方式为setter方法,A中注入B的方式为构造器
 
 

Spring是如何解决的循环依赖?

关于循环依赖的解决方式应该要分两种情况来讨论
  1. 简单的循环依赖(没有AOP)
  2. 结合了AOP的循环依赖
 
@Component
public class A { 
    // A中注入了B
    @Autowired private B b; }
@Component
public class B
{ // B中也注入了A
    @Autowired private A a;
}
 
Spring通过三级缓存解决了循环依赖,其中
1 singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
2 earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
3 singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程
0
 
假设不使用三级缓存,直接在二级缓存中(没有被Aop)
0
 
上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的
 
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,
第一步,先获取到三级缓存中的工厂;
第二步,调用对象工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。
紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
 
0
 
 
 
 
 

Spring 当中用到了哪些设计模式?

模板方法模式:例如 jdbcTemplate,通过封装固定的数据库访问比如获取 connection、获 取 statement,关闭 connection、关闭 statement 等 然后将特殊的 sql 操作交给用户自己实现。
 
策略模式:Spring 在初始化对象的时候,可以选择单例或者原型模式。
 
简单工厂:Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来 获得 bean 对象。
 
工厂方法模式:一般情况下,应用程序有自己的工厂对象来创建 bean.如果将应用程序自己的工 厂对象交给 Spring 管理, 那么 Spring 管理的就不是普通的 bean,而是工厂 Bean。
 
单例模式:保证全局只有唯一一个对象。
 
适配器模式:SpringAOP 的 Advice 有如下:BeforeAdvice、AfterAdvice、AfterAdvice, 而需要将这些增强转为 aop 框架所需的 对应的拦截器 MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、 ThrowsAdviceInterceptor。
 
代理模式:Spring 的 Proxy 模式在 aop 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。
 
装饰者模式:如 HttpServletRequestWrapper,自定义请求包装器包装请求,将字符编码转 换的工作添加到 getParameter()方法中。
 
观察者模式:如启动初始化 Spring 时的 ApplicationListener 监听器。
 
 
 
 

 

Spring 中的bean 是线程安全的吗?

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

Spring Bean作用域

Spring 的 bean 作用域(scope)类型有5种:
1、singleton:单例,默认作用域。
2、prototype:原型,每次创建一个新对象。
3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
5、global-session:全局会话,所有会话共享一个实例。
线程安全这个问题,要从单例与原型Bean分别进行说明。
「原型Bean」对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
「单例Bean」对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行「查询」以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
spring单例,为什么controller、service和dao确能保证线程安全?
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。实际上大部分时间Bean是无状态的(比如Dao) 所以说在某种程度上来说Bean其实是安全的。
但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 singleton 改为 protopyte, 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。
有状态就是有数据存储功能无状态就是不会保存数据
controller、service和dao层本身并不是线程安全的,只是如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。
  1. 在 @Controller/@Service 等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
  2. 尽量不要在@Controller/@Service 等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
  3. 默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
  4. 一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的。
 
 
 
 
 

Spring事务管理的方式有几种?

1.编程式事务:在代码中硬编码(不推荐使用)。
2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声 明式事务。
 

编程式事务管理

通过 TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。
使用TransactionTemplate 进行编程式事务管理的示例代码如下:
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}
复制代码
 
 
使用 TransactionManager 进行编程式事务管理的示例代码如下:
@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}
 

声明式事务管理

推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)。
使用 @Transactional注解进行事务管理的示例代码如下:
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}
 
比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后ATM出1000元钱。
这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;
如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。
所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。
 
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:
public class PlatformTractionManager implements PlatformTransactionManager {
    @Override
    public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        return null;
    }

    @Override
    public void commit(TransactionStatus status) throws TransactionException {

    }

    @Override
    public void rollback(TransactionStatus status) throws TransactionException {

    }
}
 

 什么是事务传播行为?

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
 
public void methodA(){
    methodB();
    //doSomething
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    //doSomething
 }
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。

Spring中七种事务传播行为

事务传播行为类型
说明
PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
 
隔离级别
事务的第二个维度就是隔离级别。隔离级别定义了一个事务可能受其他并发事务影响的程度。
(1)并发事务引起的问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。
脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
 
 
不可重复读与幻读的区别
不可重复读的重点是修改:
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
例如:在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
con1 = getConnection(); select salary from employee empId ="Mary";
 
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
con2 = getConnection(); update employee set salary = 2000; con2.commit();
 
在事务1中,Mary 再次读取自己的工资时,工资变为了2000
//con1 select salary from employee empId ="Mary";
 
在一个事务中前后两次读取的结果并不一致,导致了不可重复读。
幻读的重点在于新增或者删除:
同样的条件, 第1次和第2次读出来的记录数不一样
例如:目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。
con1 = getConnection(); Select * from employee where salary =1000;
 
共读取10条记录
这时另一个事务向employee表插入了一条员工记录,工资也为1000
con2 = getConnection(); Insert into employee(empId,salary) values("Lili",1000); con2.commit();
 
事务1再次读取所有工资为1000的员工
//con1 select * from employee where salary =1000;
 
共读取到了11条记录,这就产生了幻像读。
从总的结果来看, 似乎不可重复读和幻读都表现为两次读取的结果不一致。但如果你从控制的角度来看, 两者的区别就比较大。
对于前者, 只需要锁住满足条件的记录。
对于后者, 要锁住满足条件及其相近的记录。
 
0
 
 
@Transactional(rollbackFor = Exception.class)注解了解吗?
Exception 分为运行时异常 RuntimeException 和非运行时异常。
事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
当 @Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
在 @Transactional 注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上 rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。
 
 
 

什么是Spring的内部bean?

当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的 基于XML的 配置元数据中,可以在或元素内使用 元素,内部bean通常是匿名的,它们的Scope一般是prototype。
 
 

什么是bean装配?

装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
 
 

Spring中的自动装配

Spring 容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。
Spring提供了4种自动装配策略(加个 注解)
public interface AutowireCapableBeanFactory{ //无需自动装配 int AUTOWIRE_NO = 0; //按名称自动装配bean属性 int AUTOWIRE_BY_NAME = 1; //按类型自动装配bean属性 int AUTOWIRE_BY_TYPE = 2; //按构造器自动装配 int AUTOWIRE_CONSTRUCTOR = 3; //过时方法,Spring3.0之后不再支持 @Deprecated int AUTOWIRE_AUTODETECT = 4; }
  • no:不进行自动装配,手动设置Bean的依赖关系。
  • byName:根据Bean的名字进行自动装配。
  • byType:根据Bean的类型进行自动装配。
  • constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
  • autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
 
 
从Spring2.5开始,开始支持使用注解来自动装配Bean的属性。它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。
Spring支持几种不同的应用于自动装配的注解。
  • Spring自带的@Autowired注解。
  • JSR-330的@Inject注解。
  • JSR-250的@Resource注解。
 
 
 
 

Spring中如何使用注解来配置Bean?有哪些相关的注解?

答:首先需要在Spring配置文件中增加如下配置:
<context:component-scan base-package="org.example"/>
然后可以用@Component、@Controller、@Service、@Repository注解来标注需要由Spring IoC容器进行对象托管的类。这几个注解没有本质区别,只不过@Controller通常用于控制器,@Service通常用于业务逻辑类,@Repository通常用于仓储类(例如我们的DAO实现类),普通的类用@Component来标注。
 
 
 
 

Spring中的自动装配有哪些限制?

 
  • 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系。
  • 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
  • 优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。
 
 
 

@Autowired注解与@Resource注解之间的区别

1.@Autowired是Spring提供的注解,
@Resource是JDK提供的注解;
2.@Autowired注解只能按类型注入,而@Resource注解默认按名称注入,但也支持按类型注入;
3.@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,若想按名字装配依赖对象,可以结合@Qualifier注解结合使用;
@Resource则有两个重要的属性:name和type,name属性指定byName,若没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,若注解标注在属性的setter方法上,则取属性名作为bean的名称寻找依赖对象;
4.@Resource注解的装配顺序:若同时指定name和type属性,则从Spring上下文中找到唯一匹配的bean进行匹配,找不到则抛异常;若只指定name属性,则从Spring上下文中按照名字进行匹配bean进行装配,找不到则抛出异常;
若只指定type属性,则从Spring上下文中按照类型匹配找到唯一bean进行装配,找不到或者找到多个,则抛出异常;
若没有指定name和type属性,自动按照byName进行匹配bean进行装配,若没有匹配,则按照byType进行匹配,若匹配到则进行装配。
 
 

Spring中出现同名bean怎么办?

同一个配置文件内同名的Bean,以最上面定义的为准
不同配置文件中存在同名Bean,后解析的配置文件会覆盖先解析的配置文件
同文件中ComponentScan和@Bean出现同名Bean。同文件下@Bean的会生效,@ComponentScan扫描进来不会生效。通过@ComponentScan扫描进来的优先级是最低的,原因就是它扫描进来的Bean定义是最先被注册的
 
 
posted @ 2022-08-10 21:13  你问人生何  阅读(166)  评论(0编辑  收藏  举报

载入天数...载入时分秒...