SSM框架学习之Spring浅谈(二)

Spring常用注解

  • @Controller : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。

@Component和@Bean的区别是什么?

  • @Component 注解作用于类,而@Bean注解作用于方法。
  • @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到Spring的bean容器中)@Bean注解通常是我们在标有该注解的方法中定义产生这个 bean, @Bean告诉了Spring这是某个类的实例,当我需要用它的时候还给我。
  • @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过@Bean注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

@Autowired和@Resource的区别

  • @Autowired是Spring提供的注解,@Resource是JDK提供的注解。
  • @Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找,@Resource 是先根据名称(byName)查找,如果(根据名称)查找不到,再根据类型(byType)进行查找。
  • 当一个接口存在多个实现类的情况下,@Autowired和@Resource都需要通过名称才能正确匹配到对应的 Bean。@Autowired可以通过@Qualifier 注解来显示指定名称,@Resource可以通过‘name’属性来显示指定名称。

Bean的作用域(Bean代指的就是那些被IOC容器所管理的对象。)

  • singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续getBean()两次,得到的是不同的Bean实例。
  • request(仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该bean仅在当前HTTP request内有效。
  • session(仅 Web 应用可用): 每一次来自新session的HTTP请求都会产生一个新的bean(会话 bean),该bean仅在当前HTTP session内有效。
  • application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

如何配置bean的作用域

xml方式:

<bean id="..." class="..." scope="singleton"></bean>

注解方式:

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person person() {
      return new Person();
}

Bean的生命周期

1、Spring对bean进行实例化;(反射、IOC)
2、(循环依赖)Spring将值和bean的引用注入到bean对应的属性中;
3、(完成beanName、BeanFactory、applicationContext的属性注入)如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法;
4、如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
5、如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
6、(调用BeanPostProcessor前置方法,进行 bean 实例属性的填充)如果bean实现了BeanPostProcessor接口,Spring将调用它们的afterProcessBeforeInitialization()方法;
7、(调用init方法初始话对象)如果bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用;
8、(调用后置方法实现AOP,代理封装)如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法;
9、(可以使用)此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
10、如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

单例Bean的是线程安全的吗?

Spring默认的bean是单例的,它并没有进行封装处理而且是共享的,所以不是线程安全的。

但是,共享不一定会有线程安全问题。
如果某个bean我们定义了共享数据,且可以对共享数据进行修改,这样才会造成线程安全问题。比如我们在线程中定义一个count++,那么这个数就是不安全的,每次线程到来都会被执行一次,count值加一。

如果,我们必须要在bean中定义有状态的实例或变量,那么我们可以有以下几种办法,保证安全。
1.使用ThreadLocal,把变量变成线程私有的(ThreadLocal使用不当会造成内存泄漏);
2.synchronized、lock、CAS,保证互斥访问临界区;
3.把bean从单例(singleton)设置成原型(protopyte);

BeanFactory和FactoryBean的区别

BeanFactory是个Factory,也就是IOC容器或对象工厂,而FactoryBean就是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,FactoryBean是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

Spring依赖注入的几种方式

构造函数注入

IOC Service Provider会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期,应该是由IOC Service Provider来管理的。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。

setter 注入

对于JavaBean对象来说,通常会通过setXxx ( )和getXxx()方法来访问对应属性。这些setxxx()方法统称为setter方法,getXxx ()当然就称为getter方法。通过setter方法,可以更改相应的对象属性,通过getter方法,可以获得相应属性的状态。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。

接口注入

相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC ServiceProvider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。

三种注入方式的比较

接口注入:从注入方式的使用上来说,接口注入是现在不甚提倡的种方式,基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter方法注入则不需要如此。

构造方法注入:这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。

setter方法注入:因为方法可以命名,所以etter方法注入在描述性上要比构造方法注入好一些。另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。

综上所述,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式;而接口注入因为侵入性较强,近年来已经逐渐被淘汰了。

Spring如何解决循环依赖

三级缓存:

singletonObjects,一级缓存,存储的是所有创建好了的单例Bean(完整,可以使用的bean)
earlySingletonObjects,二级缓存,完成实例化,但是还未进行属性注入及初始化的对象(半成品)
singletonFactories,三级缓存,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象(实例化bean然后放到二级缓存)

1、首先尝试从一级缓存中获取serviceA实例,发现不存在并且serviceA不在创建过程中;
2、serviceA完成了初始化的第一步(实例化:调用createBeanInstance方法,即调用默认构造方法实例化);
3、将自己(serviceA)提前曝光到singletonFactories中;
4、此时进行初始化的第二步(注入属性serviceB),发现自己依赖对象serviceB,此时就尝试去get(B),发现B还没有被实create,所以走create流程;
5、serviceB完成了初始化的第一步(实例化:调用createBeanInstance方法,即调用默认构造方法实例化);
6、将自己(serviceB)提前曝光到singletonFactories中;
7、此时进行初始化的第二步(注入属性serviceA);
8、于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象,并把A放到二级缓存(虽然A还没有初始化完全,但是总比没有好呀);
9、B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中;
10、此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中;

二级缓存已经基本可以解决循环依赖了为什么还要用三级缓存?

如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

Spring中事务的传播

Spring事务的传播行为
1.Propagation.REQUIRED(默认)∶如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
2.Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
3.Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
4.Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
5.Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
6.Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
7.Propagation.NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。

Spring中事务管理过程

1、先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开启新事务。
2、当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务。
3、执行具体的sql逻辑操作。
4、在操作的过程中,如果执行失败了,那么会通过‘completeTransactionAfterThrowing’来完成事务的回滚操作,回滚具体的逻辑是通过‘doRollBack’方法来实现的,实现的时候也是先获取连接对象,通过连接对象来回滚。
5、如果执行过程中,没有发生任何异常,那么会通过‘commitTransactionAfterReturning’来完成事务的提交操作,提交的具体逻辑是通过‘doCommit’方法来实现的,实现的时候也是先获取连接对象,通过连接对象提交。
6、当事务执行完毕之后需要清除相关的事务信息‘cleanupTransactionInfo’

关键:TransactionInterceptor 

 Spring用到了哪些设计模式

工厂模式:Spring通过BeanFactory(Bean工厂)和ApplicationContext来创建对象。
单例模式:Spring中的Bean默认为单例模式
代理模式:Spring的AOP功能和注解的底层就是动态代理
模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate。
适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller。
策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略。
观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。
桥接模式:可以根据客户的需求能够动态切换不同的数据源。例如项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库。

 

posted @ 2023-06-14 09:48  请别耽误我写BUG  阅读(13)  评论(0编辑  收藏  举报