目录
1.使用依赖注入的好处
- 解耦。我们之前使用new来创建对象,是硬编码。(硬编码比较死板,不利于修改和扩展)当使用配置方式后,就不用我们手动new对象,创建对象的时机就交给Spring框架来完成。
- 灵活。(修改和扩展是可以直接在配置文件中进行操作)
2.控制反转
控制反转(Inversion of Control,Ioc),它是spring框架中的核心之一,指的是对象成员变量的赋值控制权由原来我们自己创建,变为交给Spring来创建。
3.常用注解
3.1 @Configuration
它是标注在类上的,被它标注的类就是一个配置类,它的使用就相当于applicationContext.xml文件的作用。
3.2 @Component
这个注解是让Spring容器在加载时自动会添加到容器里面的注解。它也是标注在类上的。它在Spring容器中就相当于一个Bean标签。
public @interface Component {
/**
* 该值可能指示对逻辑组件名称的建议,
* 在自动检测组件的情况下被转换为SpringBean.
* 返回建议的组件名称(如果有的话)(否则返回空字符串)
*/
String value() default "";
}
3.3 @Reponstory
这个注解的作用与@Component注解的作用和功能一样。但从语义层面来说,这个注解用于数据访问层,而@Component常用于层次不太明确或没有层次的地方。
public @interface Repository {
/**
* 该值可能指示对逻辑组件名称的建议,
* 在自动检测组件的情况下被转换为SpringBean.
* 返回建议的组件名称(如果有的话)(否则返回空字符串)
*/
// Component的别名
@AliasFor(annotation = Component.class)
String value() default "";
}
3.4 @Service
这个注解的作用也与@Component注解的作用和功能一样。但从语义层面来说,这个注解用于业务逻辑层。
public @interface Service {
/**
* 该值可能指示对逻辑组件名称的建议,
* 在自动检测组件的情况下被转换为SpringBean.
* 返回建议的组件名称(如果有的话)(否则返回空字符串)
*/
// Component的别名
@AliasFor(annotation = Component.class)
String value() default "";
}
3.5 @Controller
这个注解的作用与@Component注解的作用和功能一样。但从语义层面来说,这个注解用于控制访问层。
public @interface Controller {
/**
* 该值可能指示对逻辑组件名称的建议,
* 在自动检测组件的情况下被转换为SpringBean.
* 返回建议的组件名称(如果有的话)(否则返回空字符串)
*/
// Component的别名
@AliasFor(annotation = Component.class)
String value() default "";
}
3.6 @Bean
它是标注在一个方法上的,这个注解可以给它一个name/value属性,用于给这个Bean对象取一个唯一的名称,便于调用。如果没有给定name/value属性,那么被这个注解所标注的方法所对应的名称就是方法名称。
3.7 @Autowired
这个注解可以添加到成员属性、方法参数、构造方法上。它的作用是把标注的对象进行自动注入(装配)。被它注解的对象默认是按照类型来自动装配。如果希望按照名称来进行自动装配,那么要结合@Qualifier注解一起使用。
3.8 @Qualifier
它的作用是配合@Autowired注解来按名称进行装配,它不能独立使用。如果希望能够独立使用,则只能放到参数列表中。
3.9 @Value
这个注解的作用是对基本数据类型和字符串类型进行注入值。
3.10 @ComponentScan
这个注解是用于类上的,它的作用是对指定名路径进行扫描。
3.11 @PropertySource
这个注解的作用是加载.properties文件中的配置。
3.12 @Import
这个注解是用于标注在类上的,作用是导入其它的配置类,它是可以一次导入多个配置类。
@Import{(xxx.class,xxxx.class)}
4.依赖注入发生时间
当Spring IoC容器完成了Bean定义资源的定位、载入和解析注册后,IoC容器就可以管理Bean定义的相关数据。但是,此时IoC容器还没有对所管理的Bean进行依赖注入,依赖注入是在以下两种情况下产生的:
- 当用户第一次调用getBean()方法时,IoC容器触发依赖注入。
- 当用户在配置文件中将<Bean>元素配置了lazy-init=false属性时,IoC容器才回进行预实例化,触发依赖注入。
5. BeanFactory和FactoryBean的区别
区别:BeanFactory是管理Bean的工厂,而Factory是创建对象的工厂Bean
5.1 BeanFactory
BeanFactory是IoC容器的核心接口,可以进行实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。它定义了IoC的容器的最基本形式,并不是IoC的具体实现。但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
// 返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
Object getBean(String name) throws BeansException;
// 返回以给定名称注册的bean实例,并转换为给定class类型
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
// 判断工厂中是否包含给定名称的bean定义,若有则返回true
boolean containsBean(String name);
// 判断给定名称的bean定义是否为单例模式
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 返回给定名称的bean的Class,如果没有找到指定的bean实例,则抛出NoSuchBeanDefinitionException异常
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 返回给定bean名称的所有别名
String[] getAliases(String name);
}
5.2 FactoryBean
一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。
从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
// 获取当前对象
@Nullable
T getObject() throws Exception;
// 获取当前对象的类型
@Nullable
Class<?> getObjectType();
// 默认值为true 是单例对象 false 不是单例对象
default boolean isSingleton() {
return true;
}
}
6.Bean的生命周期
在Spring中Bean的生命周期可以分为4类:
- Bean 自身方法
- Bean 生命周期接口方法
- 容器级别生命周期接口方法
- 工厂后处理器接口方法
Spring Bean 生命周期 | 相关接口及方法 |
Bean 自身方法 | Bean本身业务的方法 配置文件中 init-method 和 destroy-method 指定的方法 |
Bean 生命周期接口方法 | InitializingBean 接口 DisposableBean 接口 BeanNameAware 接口 ApplicationContextAware 接口 BeanFactoryAware 接口 其它 |
容器级别生命周期接口方法 一般称为后处理器 | InstantitationAwareBeanPostProcessor 接口实现 BeanPostProcesso r接口实现 |
工厂后处理器接口方法 也可以归为容器级别 | AspectJWeavingEnabler ConfigurationClassPostProcessor CustomAutowireConfigure r等 |
一个 Bean 的生命周期需要经历以下时序:
- init-method:指定某个方法在 Bean 实例化完成,依赖关系设置结束后执行
- detroy-method:指定某个方法在 Bean 销毁之前被执行
- InitializingBean 接口:在 Bean 实例化完成,依赖关系设置结束后执行,在 init-method 之前 执行
- DisposableBean 接口:在指定 Bean Bean 销毁之前被执行,它是在 detroy-method 之前执行
- ApplicationContextAware 接口:在实例化 Bean 时,为 Bean 注入 ApplicationContext7
7.为什么要使用三级缓存来解决循环依赖
7.1 什么是循环依赖
循环依赖就是 A 的创建依赖于 B 的创建,B 的创建又依赖于 C 的创建,而 C 的创建又依赖于 A 的创建。
如上图的所示就形成了循环依赖。
7.2 在 Spring 中使用循环依赖
7.2.1 实体类
@Component
public class AService {
@Autowired
private BService bService;
public void show() {
System.out.println(bService);
}
}
@Component
public class BService {
@Autowired
private AService aService;
}
7.2.2 配置
<bean id="aService" class="com.funny.service.AService">
<property name="BService" ref="bService"/>
</bean>
<bean id="bService" class="com.funny.service.BService">
<property name="AService" ref="aService"/>
</bean>
7.2.3 配置类
@Configuration
@ComponentScan("com.funny")
public class AppConfig {
}
7.2.4 测试类
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
AService aService = (AService) context.getBean("AService");
aService.show();
}
通过测试发现,在Spring中已经帮我们解决一些循环依赖的问题。
7.3 分析解决循环依赖的问题
在 Spring 中,创建一个对象需要经过以下步骤:
1)利用构造器创建一个普通对象(也叫原始对象)
2)给对象的属性赋值,也就填充对象的属性值
3)填充其他属性
4)初始化前、初始化完成,以及其它操作
5)初始化后
6)把创建并初始化的对象放到单例池中(singletoObjects)里面。
在 Spring 中,通过以下三个 Map 对象来解决循环依赖问题:
1、一级缓存:singletonObjects,在这级缓存中已经经历完整的生命周期的 Bean 对象
2、二级缓存:earlySingletonObjects,比一级缓存多了一个 early,表示缓存的是早期的 Bean 对 象,也就是 Bean 的生命周期还没有走完就把这个 Bean 放到了二级缓存中。
3、三级缓存:singletonFactories,缓存的是 ObjectFactory ,表示对象工厂,用来创建某个对 象。 实际上,大多数据开发部下使用一级缓存就可以解决循环依赖问题,如果在循环依赖中存在一些 循环的属性需要注入,比如 AOP 相关的数据需要注入,这时就需要使用 三级缓存来解决循环依赖问题。
从上图中可以发生,Spring 在处理循环依赖问题时,使用二级缓存就已经解决,但它不能解决复 杂的循环依赖问题。要想解决复杂的循环依赖问题,就需要用到三级缓存。
8.AOP
AOP(Aspect Oriented Programming)面向切面编程,它是面向对象编程的一种补充。
在 Spring 中,AOP 的实现有两种方式:
- 使用 JDK 动态代理,要求被增强(代理)对象必须实现接口
- 使用 CGLib 动态代理
在 Spring 中两种方式都有,如果被代理对象实现了接口就使用 JDK 动态代理,否则就使用 CGLib 动态代理。默认使用的是 JDK 动态代理。我们可以设置使用何种代理方式。
8.1 横切关注点
简单的说,横切关注点就是可以对某些方法进行拦截,拦截后对原方法进行增强的处理。
8.2 切面
切面就是对横切关注点的抽象,这个关注点可能会横切多个对象。简单的说就是用于增强的类。
8.3 连接点
连接点是在程序执行过程中某个特定的点,如某个方法调用时或者处理异常的时。
8.4 切入点
在切面上拦截到某个特定的连接点之后执行的动作。
9.事务管理
数据库的事务是指将一系列的数据库操作当作一个逻辑单元,这个单元中的数据操作要么完全成 功,要么完全失败。
9.1 事务的特点
一个事务处理逻辑单元要成为事务,必须满足ACID属性:
- 原子性(Atomicity):一个事务内的操作不可再分,要么全部成功,要么全部失败。
- 一致性(Conisitency):事务执行后,数据库的状态与其他业务逻辑保持一致。
- 隔离性(Isolation):每个事务运行都是相互独立的,互不干绕。
- 持久性(Druability):事务一旦提交,就会永久保存下来。
9.2 事务隔离级别
事务隔离级别有4个:
- READ_UNCOMMITED:读未提交,会产生“脏读”数据,必须要避免的。
- READ_COMMITED:读已提交事务,会产生“幻读”数据。
- REPEATABLE_READ:可重复读,会产生“幻读”数据。
- SERIALIZABLE:它是一个串行读。
事务隔离级别是从上往下的安全性增加,但是性能降低。
9.3 事务传播行为
事务传播行为有7个:
事务传播行为类型 | 说 明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务。 如果已经存在一个事务中,加入到这就个事务中 |
PROPAGATION_SUPPORTS | 支持当前事务。如果当前没有事务,就以非事务方式执 行 |
PROPAGATION_MANDATORY | 使用当前的事务。如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务。如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作。如果当前存在事务,就把当前 务挂起 |
PROPAGATION_NEVER | 以非事务方式执行。如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。 如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。 与 PROPAGATION_REQUIRES_NEW 的差别是 PROPAGATION_REQUIRES_NEW 另起一个事务,将会与 其父事务相互独立。 PROPAGATION_NESTED 事务和其父事务是相依的,其要 等父事务一起提交。 |