今日分享开始啦,请大家多多指教~
Spring概述
概念
Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略。
优缺点
优点
缺点
Spring依赖反射,反射影响性能
使用门槛升高,入门Spring需要较长时间
Spring的模块
Spring框架中都用到了那些设计模式
Spring IOC
IOC是什么?
IOC(Inversion of Control)即控制反转,简单说就是把原来代码需要实现的对象创建、依赖反转给容器来帮忙实现,需要创建一个容器并且需要一种描述让容器指导要创建对象之间的关系,在Spring中管理对象及其依赖关系是通过Spring的IOC容器实现的。
IOC的实现方式有依赖注入和依赖查找是,由于依赖查找用的很少,因此IOC也叫做依赖注入。依赖注入指对象被动地接受依赖类而不用自己主动去找,对象不是从容器中查找它依赖的类,而是在容器实例化对象时,主动将它所依赖的类注入给它。
依赖注入更加准确地描述了IOC的设计理念,所谓依赖注入(Dependency Injection),即组件之间的依赖关系有容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用程序中的各个关联的组件之中,组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。
举例:一个Car类需要一个Engine的对象,那么一般需要手动new一个Engine,利用IOC就只需要定义一个私有的Engine类型的成员变量,容器会在运行时创建一个Engine的实例对象并将引用自动注入给成员变量。
控制反转是:关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。
IOC容器初始化的过程
基于xml的容器初始化
当创建一个
ClassPathXmlApplicationContext时,构造方法做了两件事
ClassPathXmlApplicationContext通过调用父类
AbstractApplicationContext的refresh方法启动整个IOC容器对Bean定义的载入过程,refresh是一个模板方法,规定了IOC容器的启动流程,在创建IOC容器前如果已有容器存在,需要把已有的容器销毁,保证在refresh方法后使用的是新创建的IOC容器
容器创建后通过loadBeanDefinitions方法加载Bean配置资源,该方法做两件事:
调用资源加载器的方法获取需要加载的资源;
真正执行加载功能,由子类XmlBeanDefinitionReader实现。加载资源时:
Spring IOC容器中注解解析的Bean信息放在一个HashMap集合中,key是字符串,值是BeanDefinition,注册过程中需要synchronized保证线程安全,当配置文件的Bean被解析且被注册到IOC容器中后,初始化就算真正完成了,Bean 定义信息已经可以使用且可被检索。Spring IoC 容器的作用就是对这些注册的 Bean 定义信息进行处理和维护,注册的 Bean 定义信息是控制反转和依赖注入的基础。
测试类
基于注解的容器初始化
分为两种:
或者
在xml中开启注解装配
<context:annotation-config></context:annotation-config>
然后使用
ClassPathXmlApplicationContext加载xml配置文件
依赖注入的实现方法
构造方法注入
IOC Service Provider会检查被注入对象的构造方法取得它所需要的依赖对象列表,进而为其注入相应的对象。这种方法的优点是在对象构造完成后就处于就绪状态,可以马上使用。缺点是当依赖对象较多时,构造方法的参数列表会比较长,构造方法无法被继承,无法设置默认值。对于非必需的依赖处理可能需要引入多个构造方法,参数数量的变动可能会造成维护的困难。
setter方法注入
当前对象要只需要为其依赖对象对应的属性添加setter方法,就可以通过setter方法将依赖对象注入中。setter 方法注入在描述性上要比构造方法注入强,并且可以被继承,允许设置默认值。缺点是无法在对象构造完成后马上进入就绪状态。
构造器依赖注入和Setter方法注入的区别
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
接口注入
必须实现某个接口,接口提供方法来为其注入依赖对象。使用少,因为它强制要求被注入对象实现不必要接口,侵入性强,已在Spring4中废除。
依赖注入的过程?
getBean方法获取Bean实例,该方法调用doGetBean,doGetBean真正实现从IOC容器获取Bean的功能,也是触发依赖注入的地方。
具体创建Bean对象的过程是由ObjectFactory的createBean完成的,该方法主要通过createBeanInstance方法生成Bean包含的Java对象实例和populateBean方法对Bean属性的依赖注入进行处理。
在populateBean方法中,注入过程主要分为两种情况:
1.属性值类型不需要强制转换时,不需要强制类型转换时,不需要解析属性值,直接进行依赖注入。
2.属性类型需要强制转换时。
BeanWrapperImpl类负责对完成初始化的Bean对象进行依赖注入
当容器对Bean的定位、载入、解析和依赖注入完成后就不再需要手动创建对象,IOC容器会自动为我们创建对象并且依赖注入。
Bean的生命周期
此时Bean就已经准备就绪了,可以被应用程序使用了,他们一直驻留在容器中,直到容器被销毁。
自己定制初始化和注销方法
XML 方式通过配置 bean 标签中的 init-Method 和 destory-Method 指定自定义初始化和销毁方法。
注解方式通过 @PreConstruct 和 @PostConstruct 注解指定自定义初始化和销毁方法。
Bean的作用范围
通过scope属性指定bean的作用范围,包括:
session:和request类似,确保每个session中有一个Bean实例,session过期后Bean会随之失效
Bean线程安全性
Spring的单例Bean不是线程安全的
Spring中的Bean默认是单例模式,Spring框架中没有对单例Bean进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
Spring如何处理线程并发问题
只有无状态的Bean才会在多线程下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据访问的冲突,因为每一个线程都拥有自己的变量副本,从而就没有必要对该变量进行同步,ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
扩展问题
创建Bean的方式
xml
注解
@Component 把当前类对象存入 Spring 容器中,相当于在 xml 中配置一个 bean 标签。value 属性指定 bean 的 id,默认使用当前类的首字母小写的类名。
@Controller,@Service,@Repository 三个注解都是 @Component 的衍生注解,作用及属性都是一模一样的。只是提供了更加明确语义,@Controller 用于表现层,@Service用于业务层,@Repository用于持久层。如果注解中有且只有一个 value 属性要赋值时可以省略 value。
如果想将第三方的类变成组件又没有源代码,也就没办法使用 @Component 进行自动配置,这种时候就要使用 @Bean 注解。被 @Bean 注解的方法返回值是一个对象,将会实例化,配置和初始化一个新对象并返回,这个对象由 Spring 的 IoC 容器管理。
name 属性用于给当前 @Bean 注解方法创建的对象指定一个名称,即 bean 的 id。当使用注解配置方法时,如果方法有参数,Spring 会去容器查找是否有可用 bean对象,查找方式和 @Autowired 一样。
BeanFactory、FactoryBean和ApplicationContext的区别?
BeanFactory(”低级容器”)
BeanFactory是一个Bean工厂,使用简单工厂模式,是Spring IOC容器的顶级接口,可以理解为含有Bean集合的工厂,作用是管理Bean,包括实例化、定位、配置对象及建立这些对象间的依赖。BeanFactory 实例化后并不会自动实例化 Bean,只有当 Bean 被使用时才实例化与装配依赖关系,属于延迟加载,适合多例模式。
FactoryBean
FactoryBean是一个工厂Bean,使用了工厂方法模式,作用是生产其他Bean实例,可以通过实现该接口,提供一个工厂方法来自定义实例化Bean的逻辑,FactoryBean接口有BeanFactory中的配置实现,,这些对象本身就是用于创建对象的工厂,如果一个 Bean 实现了这个接口,那么它就是创建对象的工厂 Bean,而不是 Bean 实例本身。
ApplicationContext(”高级容器“)
ApplicationContext是BeanFactory的子接口,扩展了BeanFactory的功能,提供了支持国际化的文本消息,统一的资源文件读取方式,事件传播以及应用层的特别配置等。该接口定义了一个refresh方法,用于刷新整个容器,即重新加载/刷新所有的bean。容器会在初始化时对配置的 Bean 进行预实例化,Bean 的依赖注入在容器初始化时就已经完成,属于立即加载,适合单例模式,一般推荐使用。
实现类:
Spring AOP
AOP是什么?
AOP(Aspect-Oriented Programming)即面向切面编程,简单地说就是将代码中重复的部分抽取出来,在需要执行的时候使用动态代理技术,在不修改源码的基础上对方法进行增强,减少了系统的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
常见的场景包括:权限认证、自动缓存、错误处理、日志、调试和事务。
动态代理
JDK
Spring根据类是否实现接口来判断动态代理的方式,如果实现接口会使用JDK动态代理,核心是InvocationHandler接口和Proxy类,JDK动态代理主要通过重组字节码实现,首先获得被代理对象的引用和所有接口,生成新的类必须实现代理类的所有接口,动态生成Java代码后编译生成新的.class文件并重新加载到JVM运行。JDK代理直接写Class字节码。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入口, 在方法反射调用时使用。
CGLIB
AOP相关术语
Aspect:切面是通知和切点的结合。通知和切点共同定义了切点的全部内容,在Spring AOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@AspectJ注解来实现。
Joinpoint:指连接点,在SpringAOP中,一个连接点总是代表一个方法的执行。应用可能有数以千计应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
Advice:通知,指切面对于某个连接点所产生的动作,包括:
Pointcut:切入点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
Proxy:代理,Spring AOP 中有 JDK 动态代理和 CGLib 代理,目标对象实现了接口时采用 JDK 动态代理,反之采用 CGLib 代理。
Target:代理的目标对象,指一个或多切面所通知的对象。
Weaving :织入,指把增强应用到目标对象来创建代理对象的过程。
AOP的过程
SpringAOP由BeanPostProcessor后置处理器开始,这个后置处理器是一个监听器,可以监听容器触发的Bean生命周期事件,向容器注册后置处理器以后,容器中管理的Bean就具备了接收IOC容器回调事件的能力。BeanPostProcessor 的调用发生在 Spring IoC 容器完成 Bean 实例对象的创建和属性的依赖注入后,为 Bean 对象添加后置处理器的入口是 initializeBean 方法。
AOP工作中心在于如何将增强编织到目标对象的连接点上,这里包含两个动作
扩展问题
AOP有哪些实现方式
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表是AspectJ,动态代理则以SpringAOP为代表。
SpringAOP 和 AspectJ AOP有什么区别
Spring注解
基于Spring注解配置
基于Java的配置,允许在少量Java注解的帮助下,进行大部分Spring的配置而非通过xml文件。
value属性用于指定配置类的字节码文件。
对象注解
@Component:将java标记为Bean,它是任何Spring管理组件的通用构造型,Spring的组件扫描机制可以将其拾取并拉入容器中。
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中。
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
依赖注入(DI)的相关注解
@Required(已过时):这个注解表明Bean的属性,必须在配置的时候设置,通过一个Bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出
BeanInitializationException。
@Autowired:自动按类型注入,如果有多个匹配则按照指定 Bean 的 id 查找,查找不到会报错,默认情况下,要求对象必须存在(可以设置required为false),可用于构造函数、成员变量、Setter方法。
@Qualifier:在自动按照类型注入的基础上再按照 Bean 的 id 注入,给变量注入时必须搭配,当创建多个相同类型的Bean,并希望仅使用属性装配其中一个 Bean时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 Bean来消除歧义。
@Resource :直接按照 Bean 的 id 注入,当找不到与名称匹配的Bean才会按照类型来装配注入,只能注入 Bean 类型。
@Value :用于注入基本数据类型和 String 类型。
@AutoWired注解自动装配的过程
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />
在启动容器时,容器自动装载了一个
AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowired、@Resource时就会在IOC容器自动查找需要的Bean,并装配给该对象属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
自动装配有哪些局限性?
自动装配的局限性是:
AOP的相关注解有哪些?
@Aspect:声明被注解的类是一个切面Bean;
@Before:前置通知,指定某个连接点之前执行的通知;
@After:后置通知,指定某个连接点退出是执行的通知(不论正常返回还是退出);
@AfterReturning:返回后通知,指某连接点正常完成之后的通知,返回值使用returning属性接收;
@AfterThrowing:异常通知,指方法抛出异常导致退出是执行的通知,和@AfterReturning只会有一个执行,异常使用throwing属性接受。
Spring事务
Spring事务实现的方式
Spring事务的本质其实就是数据库对事务的支持,,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring事务隔离级别
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致
Spring事务传播行为
当多个事务同时存在的时候,Spring如何处理这些事务的行为称为事务的传播行为。
事务管理类型的选择
大多数Spring框架用户选择声明式事务管理,因为它对应用代码影响很小,因此更符合一个无侵入的轻量级容器的思想。。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
今日份分享已结束,请大家多多包涵和指点!
来源:阿木自动化软件测试
声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!