spring常见面试题

一、*介绍一下spring框架?

Spring框架是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,旨在简化企业级应用程序的开发。通过IoC和DI,Spring降低了组件之间的耦合度,使得应用程序更加灵活和易于维护。面向切面编程(AOP)使得日志、事务管理等横切关注点可以从业务逻辑中分离出来,提高了代码的可读性和可维护性。

Spring框架的主要模块

Spring框架由多个模块组成,每个模块都针对软件开发的不同方面提供了解决方案。以下是一些核心的Spring模块:

  1. Spring Core

:提供了框架的基本组成部分,包括IoC(控制反转)和DI(依赖注入)功能。IoC和DI是Spring框架的核心特性,它们允许你将对象之间的依赖关系从代码中解耦出来,并通过配置文件或注解进行管理。

  1. Spring Beans

:在Spring Core的基础上,提供了更高级的企业级服务,如Bean的创建、配置和管理。

  1. Spring Context

:建立在Spring Core和Spring Beans的基础上,提供了一个应用程序上下文(ApplicationContext),这是一个配置文件信息的抽象表示,允许你在整个应用程序中共享配置信息。

  1. Spring Expression Language (SpEL)

:一个强大的表达式语言,用于在运行时查询和操作对象图。它可以被用于Bean定义中以及XML和注解驱动的配置中。

  1. Spring AOP(面向切面编程)

:提供了一种将横切关注点(如日志、事务管理等)与业务逻辑分离的方法,从而使代码更加清晰和模块化。

  1. Spring JDBC

:提供了JDBC(Java数据库连接)的抽象层,简化了数据库访问代码的编写,同时支持声明式事务管理。

  1. Spring ORM

:为使用ORM(对象关系映射)技术如Hibernate、JPA等提供了集成层,简化了数据持久化操作。

  1. Spring Web

:提供了基础的Web开发支持,包括文件上传、多部分请求处理等。

  1. Spring MVC

:Spring的Web MVC框架,它构建在Spring Web模块之上,提供了一种分离关注点的方式,使得开发Web应用程序变得更加简单。

  1. Spring Test

:提供了对Spring应用程序进行测试的支持,包括单元测试和集成测试。

二、*Spring AOP和AspectJ AOP有什么区别?

2.1、Spring AOP

Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。

Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现方式,但它们在实现方式、灵活性、依赖性和应用场景等方面存在一些显著的区别。以下是详细的对比分析:

2.2、实现方式

  • Spring AOP:
    • 基于代理:Spring AOP使用动态代理技术(包括JDK动态代理和CGLIB)在运行时生成代理对象,并将切面逻辑织入到代理对象中。
    • 代理机制:对于基于接口的目标类,Spring使用JDK动态代理;对于没有实现接口的目标类,Spring使用CGLIB生成子类来实现代理。
  • AspectJ AOP:
    • 基于字节码增强:AspectJ AOP在编译期或类加载期通过在目标类中直接插入字节码来实现切面逻辑的织入,不依赖于代理。
    • 灵活性:AspectJ提供了更强大和灵活的AOP功能,允许进行更细粒度的控制和切入。
  • 2.3、灵活性和依赖性

  • Spring AOP:
    • 依赖Spring:Spring AOP对Spring管理的Bean生效,基于代理的方式,支持更灵活的切点表达式。但由于是基于代理,它只能作用于Spring Bean的方法调用,无法应用于非Spring Bean的方法。
    • 集成性:与Spring框架有较强的集成,依赖于Spring的容器和代理机制,在Spring项目中更容易集成和使用。
  • AspectJ AOP:
    • 不依赖Spring:AspectJ AOP作为独立的框架,不依赖于Spring,可以在任何Java项目中使用。
    • 广泛应用:它可以对任何Java对象生效,不仅限于Spring管理的Bean,可以作用于所有的对象。
    • 额外配置:需要在编译时或类加载时进行织入,可能需要额外的配置和依赖。

2.4、性能

  • Spring AOP:
    • 动态代理的性能:由于采用动态代理,对目标类的影响相对较小,因此性能相对较高。但对于大规模或频繁的切面逻辑,可能会有一些性能损耗。
  • AspectJ AOP:
    • 字节码增强的性能:通过编译时或类加载时织入,虽然对字节码进行了修改和增强,可能对目标类的影响更大,但在某些场景下可能性能更高。然而,这也取决于具体的实现和配置。

2.5、应用场景

  • Spring AOP:
    • 轻量级AOP需求:适用于轻量级的AOP需求,如日志记录、事务管理等,对Spring框架集成友好。
  • AspectJ AOP:
    • 复杂AOP需求:适用于复杂的AOP需求,如系统架构中需要对各个层次的组件进行切面,或者对非Spring Bean的对象进行切面等。

综上所述,选择Spring AOP还是AspectJ AOP取决于具体的项目需求和场景。如果需要更强大和灵活的AOP功能,并且可以接受一些额外的配置和依赖,那么AspectJ AOP可能更为适合。而对于大多数Spring项目来说,Spring AOP是一个常用且足够的选择。

三、*如何正解Spring IOC

对象的作用域scope

lazy_init默认值是false,在spring容器启动时创建对象,并且只在scope=singleteon时有效;

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

四、***Spring中的bean的作用域有哪些?Spring中的单例bean的线程安全吗?

4.1、spring的bean作用域

singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。Spring默认bean的模式是单例模式,@Controller也是bean的一种,也是单例模式;

  • 为什么spring bean要默认是单例呢?

1、减少对象创建开销,节省资源,提高性能。

2、简化依赖关系:在Spring框架中,Bean之间可能存在复杂的依赖关系。如果每次都创建新的Bean实例,可能会产生相互依赖的Bean获取不同实例的问题,从而破坏依赖关系。单例模式可以确保依赖关系的稳定性和一致性。

3、保证全局状态一致性,易于维护和测试:单例模式可以确保全局共享状态的一致性,避免了资源竞争和线程安全性问题。这有助于简化应用的维护和测试工作。

单例模式在多线程环境下存在线程安全问题!!!

prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

容器在接到该类型对象的请求的时候,会每次都重新生成一个新的实例对象给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继的声明周期的管理工作,例如该对象的销毁。也就是说,容器每次返回给请求方一个新的实例对象后,就任由这个对象“自生自灭”了。

对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己专有的一个对象实例。通常,声明为prototype的对象都是一些有状态的,比如保存每个顾客信息的对象。

prototype多例模式时

1、容器启动时并没有初始化Controller变量,而是在每次调用时初始化一个新的Controller实例

2、多例模式下,静态变量多实例共享,非静态变量不共享。(本质上还是不同的对象)

注:Java中多个实例的static变量会共享同一块内存区域,也就是多个对象共享一个类的同一个静态成员变量。应为static是在类加载的时候初始化到方法区的,所有的实例会共享这块区域。

====下面是在web项目下才用到的===

request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听。

session:每次会话,同上。

global session:全局的web域,类似于servlet中的application

好了,上面都说了spring的controller默认是单例,那很自然就是singleton了。

4.2、单例的Controller,类中的非静态变量如何保证是线程安全的

@RestController@Scope(value = "prototype") @RequestMapping("/test/") public class TestController { private int num = 0; @GetMapping("/show") public Integer show() { // 添加@Scope(value = "prototype")每次打印结果都会加1 // 不添加@Scope(value = "prototype")每次打印结果都加1 System.out.println(num); return num; } @GetMapping("/add") public Integer add() { num ++; return show(); } }

最佳实践:

1、不要在controller中定义成员变量。使用局部变量,因为局部变量在每个线程中都有各自的实例,从而保证线程安全。

2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式!或者使用ThreadLocal 保存每个线程自已的变量副本

4.3、单例模式如何保证并发

Spring 中controller、service、dao都为单例模式(注:默认状态)。既然是单例,那么在并发的情况下,会发生什么?

1)使用ThreadLocal保存每个线程自己的变量;

2)加锁;

==================================================

java 里,每个线程都有自己独享的空间,也就是栈内存。线程在调用方法的时候,会创建一个栈帧。也就是说调用一个方法的时候,也就是一个栈帧的入栈过程,该方法执行完毕,栈帧也就出栈了。换句话讲,成员方法对于每个线程事实上是私有的,而不是你表面看上去的那样是 "共享" 的。

那么为什么成员变量会出问题呢?

==================================================

如你所知道的,每个新建对象都存放在堆中,每个持有该对象引用的线程,都可以访问到它(只要你有那个权限)。这也就是说,成员变量对于每个线程,事实上是共享的。

五、***Spring中的bean生命周期?

Spring 中 Bean 的生命周期较复杂,大致可以分为以下 5 个阶段:

  1. Bean 的实例化
  2. Bean 属性赋值
  3. Bean 的初始化
  4. Bean 的使用
  5. Bean 的销毁

Spring 根据 Bean 的作用域来选择 Bean 的管理方式:

  • 对于 singleton 作用域的 Bean 来说,Spring IoC 容器能够精确地控制 Bean 何时被创建、何时初始化完成以及何时被销毁;
  • 对于 prototype 作用域的 Bean 来说,Spring IoC 容器只负责创建,然后就将 Bean 的实例交给客户端代码管理,Spring IoC 容器将不再跟踪其生命周期。

Bean 生命周期的整个执行过程描述如下:

  1. Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。Bean容器利用Java Reflection API创建一个Bean的实例
  2. 对 Bean 进行属性注入。
  3. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
  4. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
  5. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
  6. 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
  7. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
  8. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  9. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
  10. 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
  11. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

5.1、自定义 Bean 的生命周期

我们可以在 Spring Bean 生命周期的某个特定时刻,指定一些生命周期回调方法完成一些自定义的操作,对 Bean 的生命周期进行管理。Bean 的生命周期回调方法主要有两种:

  • 初始化回调方法:在 Spring Bean 被初始化后调用,执行一些自定义的回调操作。
  • 销毁回调方法:在 Spring Bean 被销毁前调用,执行一些自定义的回调操作。我们可以通过以下 3 种方式自定义 Bean 的生命周期回调方法:
  1. 通过接口实现

通过实现 InitializingBean 和 DisposableBean 接口,指定 Bean 的生命周期回调方法

  1. 使用注解实现

我们还可以通过 JSR-250 的 @PostConstruct 和 @PreDestroy 注解,指定 Bean 的生命周期回调方法。

5.2、Spring后置处理器(BeanPostProcessor)

通过该接口可以自定义调用初始化前后执行的操作方法。

该接口中包含了两个方法:

  • postProcessBeforeInitialization() 方法:在 Bean 实例化、属性注入后,初始化前调用。
  • postProcessAfterInitialization() 方法:在 Bean 实例化、属性注入、初始化都完成后调用。

当需要添加多个后置处理器实现类时,默认情况下 Spring 容器会根据后置处理器的定义顺序来依次调用。也可以通过实现 Ordered 接口的 getOrder 方法指定后置处理器的执行顺序。该方法返回值为整数,默认值为 0,取值越大优先级越低。

六、Spring 自动装配

Spring 共提供了 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应,具体说明如下表。

七、Spring框架中用到了哪些设计模式

1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。

2.代理设计模式:Spring AOP功能的实现。

3.单例设计模式:Spring中的bean默认都是单例的。

4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。

5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。

7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

八、@Component和@Bean的区别是什么

@Component通常用于类级别的注解,通过@Component,Spring会自动创建这些类的实例对象,并将其注入到Spring容器中。

@Bean主要用于方法级别的注解,它允许开发者自定义Bean的创建和初始化过程,包括指定Bean的名称、作用域、依赖关系等。

@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。

九、将一个类声明为Spring的bean的注解有哪些?

9.1、@Autowired与@Resources的区别?

@Autowired(required = true):byType

@Resource(name = "",type = "")

9.3、Spring的bean的注解

我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现:

1.@Component注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。

2.@Repository注解。对应持久层,即Dao层,主要用于数据库相关操作。

3.@Service注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。

4.@Controller注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。

十、***Spring事务

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

1.编程式事务:在代码中硬编码(不推荐使用)。

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

10.2、Spring事务中的隔离级别有哪几种?

在TransactionDefinition接口中定义了五个表示隔离级别的常量:

ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。

ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

如果数据库的配置与spring中配置的隔离级别不一致时,以spring的配置为准!

10.3、Spring事务中有哪几种事务传播行为?

在TransactionDefinition接口中定义了七个表示事务传播行为的常量。

支持当前事务的情况:

PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。Support a current transaction, create a new one if none exists.This is the default setting of a transaction annotation。

PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。Support a current transaction, execute non-transactionally if none exists.

PROPAGATION_MANDATORY(强制性的): 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。Support a current transaction, throw an exception if none exists.

不支持当前事务的情况:

PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。Create a new transaction, and suspend the current transaction if one exists.

PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。Execute non-transactionally, suspend the current transaction if one exists.

PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。Execute non-transactionally, throw an exception if a transaction exists.

其他情况:

PROPAGATION_NESTED(嵌套): 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.

10.4、spring事务失效的几种情况

1、方法自调用

声明式事务基于Spring AOP技术,Spring AOP的实现是靠(jdk实现接口/cglib继承类)动态代理的方式。

所以如果一个类的methodA调用本类中另一个方法methodB,实际上是这个类对自己的调用,并没有通过Spring容器去请求代理类,因此Spring AOP不起作用。

2、数据库不支持事务

如果使用mysql,而恰巧又使用MyISAM作为数据库Engine。则无论是编程式事务还是声明式事务都不会生效。因此,一定要确保数据库Engine支持事务。

3、事务传播机制配置错吴;

4、方法的访问修饰符不是public

5、异步方法

6、异常没有抛出或抛出的异常与捕获的异常类型不一致

@Transaction注解中的属性rollback定义了出现什么异常时,事务回滚。但是如果没有将异常抛出,做了try/catch处理,则Spring AOP无法感知,此时事务会失效。

@Transaction注解默认的rollback属性为RuntimeException.class,如果业务流程中定义的异常类型不是RuntimeException或者其子类,则事务也不会生效。

因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用rollbackFor属性明确指定异常。

7、bean没有被spring容器管理。

十一、***Spring是如何解决解决循环依赖的问题

spring循环依赖及解决方法_spring循环依赖及解决方式-优快云博客

1、什么是循环依赖

1)A -> B,B -> A;:互相依赖

2)A -> B - > C -> A :三者依赖

3)A -> A :自我依赖

2、spring的解决方案

答:spring利用三级缓存机制来解决循环依赖问题;Spring内部维护了三个Map,也就是我们通常说的三级缓存。

在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

singletonObjects (一级缓存)它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。

earlySingletonObjects(二级缓存)映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

singletonFactories(三级缓存) 映射创建Bean的原始工厂

getBean(beanName)时,spring先从一级缓存中找,若一级缓存中没有;则从二级缓存中找;若一二级缓存中都没有,则证明该 bean还没有实例化。

1)一级缓存:存放所有成熟的bean;

2)二级缓存:存放所有早期bean,

3)三缓缓存:存储代理bean

后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。那么Spring 是如何通过上面介绍的三级缓存来解决循环依赖的呢?这里只用 A,B 形成的循环依赖来举例:

实例化 A,此时 A 还未完成属性填充和初始化方法(@PostConstruct)的执行,A 只是一个半成品。

为 A 创建一个 Bean工厂,并放入到 singletonFactories 中。

发现 A 需要注入 B 对象,但是一级、二级、三级缓存均为发现对象 B。

实例化 B,此时 B 还未完成属性填充和初始化方法(@PostConstruct)的执行,B 只是一个半成品。

为 B 创建一个 Bean工厂,并放入到 singletonFactories 中。

发现 B 需要注入 A 对象,此时在一级、二级未发现对象

A,但是在三级缓存中发现了对象 A,从三级缓存中得到对象 A,并将对象 A 放入二级缓存中,同时删除三级缓存中的对象 A。(注意,此时的 A

还是一个半成品,并没有完成属性填充和执行初始化方法)

将对象 A 注入到对象 B 中。

对象 B 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 B。(此时对象 B 已经是一个成品)

对象 A 得到对象B,将对象 B 注入到对象 A 中。(对象 A 得到的是一个完整的对象 B)

对象 A完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A。

3.无法解决的循环依赖问题

Spring 无法解决的循环依赖场景通常发生在以下几种情况:

  1. 构造器注入造成的循环依赖:Spring 无法解决构造器注入导致的循环依赖,因为对象创建过程中需要先知道依赖,而依赖又依赖于当前创建的对象。
  2. 方法注入造成的循环依赖:即使是方法注入(也就是常说的setter注入),如果注入的方法在构造对象的过程中被调用,也会导致循环依赖。
  3. 原型模式(Prototype Scope)下的循环依赖:Spring 默认情况下不支持原型模式下的循环依赖解决,因为它会在初次创建实例后缓存它,后续请求都返回这个实例,导致无法每次都返回一个新的对象。

解决方法:

  1. 将相互依赖的Bean中的某一个或几个Bean的作用域改为prototype,这样每次请求该Bean时,Spring都会创建一个新的实例。
  2. 使用@Lazy注解延迟加载,在需要的时候才创建对象。
  3. 使用@Autowired注解的required属性设置为false,允许不立即注入依赖。
  4. 使用@Bean注解的dependsOn属性,明确指定初始化顺序,避免循环依赖。
  5. 重构代码,尽量减少不必要的循环依赖。

更新实战知识分享:

spring 的优点? 1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入式设计,代码的污染极低 8.独立于各种应用服务器 9.spring的DI机制降低了业务对象替换的复杂性 10.Spring的高度开放性,并不强制应用完全依赖于Spring开发者可以自由选择spring的部分或全部 什么是DI机制? 依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色 需要另外一个角色协助的时候,在传统的程序设计过程中,通由调用者来创建被调用者的实例。但在spring中 创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者 因此也称为依赖注入。 spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。 设置注入的优点:直观,自然 构造注入的优点:可以在构造器中决定依赖关系的顺序。 什么是AOP? 面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 1.面向切面编程提供声明式事务管理 2.spring支持用户自定义的切面 面向切面编程(aop)是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象, 是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。 aop框架具有的两个特征: 1.各个步骤之间的良好隔离性 2.源代码无关性 Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3.打开Sesssion 4.创建事务Transation 5.持久化操作 6.提交事务 7.关闭Session 8.关闭SesstionFactory 为什么要用: 1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。 2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作 3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。 4. hibernate的性能非好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。 2. Hibernate是如何延迟加载? 1. Hibernate2延迟加载实现:a)实体对象 b)集合(Collection) 2. Hibernate3 提供了属性的延迟加载功能 当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。 3.Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系) 类与类之间的关系主要体现在表与表之间的关系进行操作,它们都市对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many、 4. 说下Hibernate的缓存机制 1. 内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存 2. 二级缓存: a) 应用及缓存 b) 分布式缓存 条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非 关键数据 c) 第三方缓存的实现 5. Hibernate的查询方式 Sql、Criteria,object comptosition Hql: 1、 属性查询 2、 参数查询、命名参数查询 3、 关联查询 4、 分页查询 5、 统计函数 6. 如何优化Hibernate? 1.使用双向一对多关联,不使用单向一对多 2.灵活使用单向一对多关联 3.不用一对一,用多对一取代 4.配置对象缓存,不使用集合缓存 5.一对多集合使用Bag,多对多集合使用Set 6. 继承类使用显式多态 7. 表字段要少,表关联不要怕多,有二级缓存撑腰 7. Struts工作机制?为什么要使用Struts? 工作机制: Struts的工作流程: 在web应用启动时就会加载初始化ActionServlet,ActionServlet从 struts-config.xml文件中读取配置信息,把它们存放到各种配置对象 当ActionServlet接收到一个客户请求时,将执行如下流程. -(1)检索和用户请求匹配的ActionMapping实例,如果不存在,就返回请求路径无效信息; -(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中; -(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法; -(4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActuibErrors对象, 就表示表单验证成功; -(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的 Action实例不存在,就先创建这个实例,然后调用Action的execute()方法; -(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给 ActionForward对象指向的JSP组件; -(7)ActionForward对象指向JSP组件生成动态网页,返回给客户; 为什么要用: JSP、Servlet、JavaBean技术的出现给我们构建强大的企业应用系统提供了可能。但用这些技术构建的系统非的繁乱,所以在此之上,我们需要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。 基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件 8. Struts的validate框架是如何验证的? 在struts配置文件中配置具体的错误提示,再在FormBean中的validate()方法具体调用。 9. 说下Struts的设计模式 MVC模式: web应用程序启动时就会加载并初始化ActionServler。用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的 Validate()验证后选择将请求发送到哪个Action,如果Action不存在,ActionServlet会先创建这个对象,然后调用 Action的execute()方法。Execute()从ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。 10. spring工作机制及为什么要用? 1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller. 3.DispatcherServlet请请求提交到目标Controller 4.Controller进行业务逻辑处理后,会返回一个ModelAndView 5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象 6.视图对象负责渲染返回给客户端。 为什么用: {AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务 (比如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。 IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反 过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。 Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。} Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。 组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下: ☆ 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 ☆ Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 ☆ Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。 ☆ Spring DAO:JDBC DAO 抽象层提供了有意义的异层次结构,可用该结构来管理异处理和不同数据库供应商抛出的错误消息。异层次结构简化了错误处理,并且极大地降低了需要编写的异代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异遵从通用的 DAO 异层次结构。 ☆ Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异层次结构。 ☆ Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 ☆ Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。 Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。 IOC 和 AOP 控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。 在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了 IOC 的一个实现模式。 Spring 框架的 IOC 容器采用类型 2 和类型3 实现。 面向方面的编程 面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。 AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。 AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。 IOC 容器 Spring 设计的核心是 org.springframework.beans 包,它的设计目标是与 JavaBean 组件一起使用。这个包通不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。 BeanFactory 支持两个对象模型。 □ 单态 模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最用的对象模型。对于无状态服务对象很理想。 □ 原型 模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。 bean 工厂的概念是 Spring 作为 IOC 容器的基础。IOC 将处理事情的责任从应用程序代码转移到框架。正如我将在下一个示例中演示的那样,Spring 框架使用 JavaBean 属性和配置数据来指出必须设置的依赖关系。 BeanFactory 接口 因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最用的 BeanFactory 定义是 XmlBeanFactory,它根据 XML 文件中的定义装入 bean,如清单 1 所示。 清单 1. XmlBeanFactory BeanFactory factory = new XMLBeanFactory(new FileInputSteam("mybean.xml")); 在 XML 文件中定义的 Bean 是被消极加载的,这意味在需要 bean 之前,bean 本身不会被初始化。要从 BeanFactory 检索 bean,只需调用 getBean() 方法,传入将要检索的 bean 的名称即可,如清单 2 所示。 清单 2. getBean() MyBean mybean = (MyBean) factory.getBean("mybean"); 每个 bean 的定义都可以是 POJO (用类名和 JavaBean 初始化属性定义) 或 FactoryBean。FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。 IOC 示例 理解控制反转最简单的方式就是看它的实际应用。在对由三部分组成的 Spring 系列 的第 1 部分进行总结时,我使用了一个示例,演示了如何通过 Spring IOC 容器注入应用程序的依赖关系(而不是将它们构建进来)。 我用开启在线信用帐户的用例作为起点。对于该实现,开启信用帐户要求用户与以下服务进行交互: ☆ 信用级别评定服务,查询用户的信用历史信息。 ☆ 远程信息链接服务,插入客户信息,将客户信息与信用卡和银行信息连接起来,以进行自动借记(如果需要的话)。 ☆ 电子邮件服务,向用户发送有关信用卡状态的电子邮件。 三个接口 对于这个示例,我假设服务已经存在,理想的情况是用松散耦合的方式把它们集成在一起。以下清单显示了三个服务的应用程序接口。 清单 3. CreditRatingInterface public interface CreditRatingInterface { public boolean getUserCreditHistoryInformation(ICustomer iCustomer); } 清单 3 所示的信用级别评定接口提供了信用历史信息。它需要一个包含客户信息的 Customer 对象。该接口的实现是由 CreditRating 类提供的。 清单 4. CreditLinkingInterface public interface CreditLinkingInterface { public String getUrl(); public void setUrl(String url); public void linkCreditBankAccount() throws Exception ; } 信用链接接口将信用历史信息与银行信息(如果需要的话)连接在一起,并插入用户的信用卡信息。信用链接接口是一个远程服务,它的查询是通过 getUrl() 方法进行的。URL 由 Spring 框架的 bean 配置机制设置,我稍后会讨论它。该接口的实现是由 CreditLinking 类提供的。 清单 5. EmailInterface public interface EmailInterface { public void sendEmail(ICustomer iCustomer); public String getFromEmail(); public void setFromEmail(String fromEmail) ; public String getPassword(); public void setPassword(String password) ; public String getSmtpHost() ; public void setSmtpHost(String smtpHost); public String getUserId() ; public void setUserId(String userId);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值