spring七大模块
- Spring Core: Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
- Spring Context: 构建于Core封装包基础上的 Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。
- Spring DAO: DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。
- Spring ORM: ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。
- Spring AOP: Spring的 AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。
- Spring Web:Spring中的 Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。
- Spring Web MVC: Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。
spring5的新特性
加入了lamde表达式。
对于耗时的操作用lambda表达式的for循环,如数据库的IO操作,多线程充分利用CPU资源;对于不太耗时的操作使用普通for循环,比如纯CPU计算类型的操作,单线程性能更高,减少上下文切换的开销。
Spring的两种IOC容器
1,BeanFactory
2,ApplicationContext(是BeanFactory的扩展)
实现CommandLineRunner或ApplicationRunner可以在程序启动的时候执行某些方法,按一定顺序执行的话,可以在实现类上加上@Order注解。@Order(value=整数值)。
BeanFactory和ApplicationContext的异同点
相同点:两者都是通过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩展功能。
不同点:BeanFactory是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext
注册bean
SpringContext利用无参的构造函数创建一个对象,然后利用setter方法赋值。所以如果无参构造函数不存在,=Spring上下文创建对象的时候便会报错。
将bean放入IOC容器的方式:
1,<bean id="" class="" />
2,在@Configuration中利用@Bean创建bean
3,加上@Compent注解标识为一个组件,然后利用@CompentScan扫描组件
4,@import(value ={Cemb.class,CembSelector.class,CembImportBeanDefinitionRegistrar.class})
参数有三种方式:
1,Cemb.class:直接注册Cemb这个bean
2,CembSelector.class:CembSelector这个对象是实现了Importselector,可以指定一些注册bean的逻辑,在重写方法的时候需要注意不要返回null,否则会报空指针。
3,CembImportBeanDefinitionRegistrar.class:CembImportBeanDefinitionRegistrar这个类实现了ImportBeanDefinitionRegistrar,重写registerBeanDefinitions方法,可以指定一些注册bean的逻辑,但是必须要将bean封装成RootBeanDefinition对象然后registry.registerBeanDefinition("cemb", beanDefinition);这样注册。
5,使用FactoryBean进行注册。需要注册的bean需要实现FactoryBean,然后重写其三个方法。
注意:
当@Bean和扫描方式对同一个单例bean创建的时候,不会报错,优先级是扫描>@Bean
bean的创建默认是单例
多例:在调用bean的时候才会被创建
单例:在创建IOC容器的时候就会创建bean,创建bean是先从spring的缓存中去判断是否存在此bean,如果不存在再去创建和初始化。
bean的生命周期
bean的生命周期:创建(通过反射创建),初始化(初始化之前会先属性赋值,并且初始化的前后可能会进行后置增强),销毁
创建:根据无参构造器,通过反射创建,创建后的bean会包装成BeanWrapper这种类型。
初始化:
1,自定义初始化方法,然后通过initMethod配置
2,@PostConstruct实现(JSR250规范,并不是spring的注解,是jdk的),属性赋值完成之后执行
3,实现InitializingBean接口的afterPropertiesSet方法来完成,dubbo中的中转对象的初始化这里就有使用到。
注意:底层在初始化的过程中,在调用初始化方法执行的前后,会通过后置处理器来调用一些自定义的方法来对初始化过程进行增强,AOP就是这个原理。
销毁:
1,自定义销毁方法,然后通过destoryMethod配置
2,@PreDestroy实现(JSR250规范,并不是spring的注解,是jdk的)可以在bean被销毁之前执行
4,实现的InitializingBean接口的destroy方法。
注意:销毁,其实就是调用IOC容器beanFactory实例然后将缓存map和IOC容器这个map都clear清空。
Spring的几种注入bean的方式。
1,Autowired(可以搭配@Qualifier和@Primary(@Primary指定bean注入的优先级)使用,按照名称注入),根据类型注入
2,Resource(JSR-250),根据名称注入
3,Inject(JSR-330的标准),根据类型注入。当不是spring框架的时候注入可以使用这种
@Autowired和@Resource的区别:
1,@Autowired是根据对象类型来自动装配的,是属于spring的注解,默认对象必须存在,如果对象不存在,则需要设置它的required属性为false
2,@Autowired(required=false)也可以使用根据名称来装配,需要加上@Qualifier注解,并指定属性名。
3,@Resource是根据对象名称来装配的,是属于jdk1.6的注解。
直白的说,就是@Autowired自动注解一个接口,俩个实现类,Autowired就不知道注入哪一个实现类,而@Resource有name属性,可以区分。
注意(注入原理可以根据):
1,构造器注入
2,属性注入
3,setter注入
BeanPostProcessor后置处理器
BeanPostProcessor是一个后置处理器,作用是在bean创建之后的初始化过程执行之前和之后,进行前置处理和后置处理进行对bean的初始化的增强。所有的bean在初始化的前后都会去执行一次前置和后置的处理,在具体的处理里面,会去循环遍历拿到bean对应的后置处理器,然后根据对应的后置处理器去执行一些增强方法,比如bean实现了ApplicationContextAware接口的话,后置处理器会去判断是否实现了这个接口,实现了就将ApplicationContext这个IOC容器作为属性设置进这个bean中,相当于让这个bean拥有了可以调用IOC容器的功能,其他的功能类似,所以后置处理器是spring的核心。
例如:
@Autowired是通过AutowiredAnnotationBeanPostProcessor这个后置处理器来实现的。
实现方式:
实现BeanPostProcessor接口,重写postProcessBeforeInitialization和postProcessAfterInitialization即可。
aop面向切面编程
aop通过动态代理实现的,其中@Around的执行顺序是要高于@Before,@After等。
动态代理的实现方式有两种:
1,Cglib
2,jdk的动态代理(spring默认使用)
可以通过proxy-target-class属性来指定,false表示使用jdk方式。
自动创建代理类方式:
1,AnnotationAwareAspectJAutoProxyCreator,根据Bean中的AspectJ注解自动创建代理,所使用的后置处理器是SmartInstantiationAwareBeanPostProcessor。
注意:
1,AnnotationAwareAspectJAutoProxyCreator这个bean的创建是通过AspectJAutoProxyRegistrar实现ImportBeanDefinitionRegistrar,将AnnotationAwareAspectJAutoProxyCreator通过id为internalAutoProxyCreator,key为这个类封装为RootBeanDefinition,在spring中定义,之后再创建和初始化。
2,然后目标类是先等AnnotationAwareAspectJAutoProxyCreator创建完成之后,再创建,创建的时候会通过动态代理创建一个代理对象,其中代理对象中有对应的后置处理器,在前置处理的时候会去获取对应的通知方法的定义,后置处理才会去创建这个对象,并且创建通知方法,根据通知方法做增强处理。
3,其中通知方法会转成一个拦截对象MethodInterceptor放入一个拦截链中(List中),放入顺序和执行顺序是相反的,然后根据动态代理的方式执行,其中每个通知方法都有对应的处理方法(invoke)去处理,处理方法都不一样。这里的执行算法是通过递归的压栈和弹栈来实现的,保证了最终弹栈的顺序是按照正确的通知方法的执行顺序来执行的。
4,切面方法之所以能按照顺序执行,是因为AnnotationAwareAspectJAutoProxyCreator也实现了Ordered接口,指定了执行顺序
2,BeanNameAutoProxyCreator,匹配Bean的名称自动创建匹配到的Bean的代理
3,根据Advisor的匹配机制自动创建代理,会对容器中所有的Advisor进行扫描,自动将这些切面应用到匹配的Bean中,实现类DefaultAdvisorAutoProxyCreator
Spring事务在controller层不起作用的原因
因为spring和springmvc的IOC是一个父子容器,先加载spring容器,所以要在controller层添加事务,需要:
1,只能使用注解事务方式。
2,在spring中加载除了controller之外的所有的bean,在springmvc中只加载controller注解的bean,只扫描controller包。
同时开启事务:
1,开启事务管理器,@EnableTransactionManagement
2,注册事务管理器的bean,并且绑定数据源
核心类InfrastructureAdvisorAutoProxyCreator,执行流程:
1,注册 2,利用后置处理器机制在创建以后,包装对象,返回一个代理对象(增强),代理对象执行方法时,利用拦截器键进行调用
当执行目标方法时:
执行拦截器链:
执行其中的事务拦截器:
1.先获取事务相关属性
2.获取PlatformTransactionManager事务管理器,直接到容器中获取platformTransactionManager bean实例
执行目标方法:
如果异常:completeTransactionAfterThrowing,利用事务管理回滚操作
如果正常:利用 事务管理器,提交事务。
AnnotationTransactionAttributeSource:封装了事务增强器要用事务注解的信息,使用这个类解析事务注解
TransactionInterceptor:保存了事务属性信息,事务管理器,实现了MethodInterceptor。
注意:当异常捕获了,事务不会生效。
BeanFactory和FactoryBean区别
BeanFactory:BeanFactory接口的类表明此类事一个工厂,是一个IOC容器,作用就是配置、新建、管理各种容器中的Bean。
FactoryBean:实现FactoryBean的类表明此类也是一个Bean,类型为工厂Bean(Spring中共有两种bean,一种为普通bean,另一种则为工厂bean)。作用是用来向容器中注册和初始化bean的。
注意:实现了FactoryBean的类例如叫CembFactoryBean,其中getObject方法是注册bean,在bean在容器中注册之后,通过id为cembFactoryBean去拿是直接拿到的getObject注册的bean,而不能拿到其本身,如果想要拿到其本身必要在id前加一个&符号。
BeanFactoryPostProcessor和BeanPostProcessor的区别?
BeanPostProcessor:Bean的后置处理器(处理的对象是Bean);可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。
BeanFactoryPostProcessor:BeanFactory的后置处理器(处理的对象是BeanFactory),是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的,可以从BeanFactory中获取到bean创建之前的定义信息。
BeanDefinitionRegistryPostProcessor:实现这个接口,可以在BeanFactoryPostProcessor之前执行,bean初始化之前做一些bean定义的处理。
BeanDefinitionRegistryPostProcessor继承BeanFactoryPostProcessor,底层是先执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法之后,再执行实现BeanFactoryPostProcessor的postProcessBeanFactory方法。
spring的aware接口系列
每个aware接口都有set方法,实现对应的aware可以获取相应的在spring中的属性。这些属性的设置是在bean初始化的前后,通过后置处理器去完成的。
spring的设计模式
单例,工厂,代理,适配器,策略,观察者,装饰,模板等。
拦截器和过滤器的区别
过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,是spring的组件,过滤器依赖与servlet容器,不是spring的组件。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。但过滤器只能在servlet前后起作用,拦截器可以
深入到方法前后,异常前后。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
IOC源码主要流程
分析AnnotationConfigApplicationContext的refresh方法。
synchronized (this.startupShutdownMonitor) {
// 刷新前的预处理,比如初始化一些属性设置,校验属性的合法性等。
prepareRefresh();
// 获取BeanFactory(Bean工厂,IOC容器,内部创建是在构造函数中new出来的)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对BeanFactory进行一些预处理,比如设置一些属性值等
prepareBeanFactory(beanFactory);
try {
// 对BeanFactory设置一些后置处理,由子类去实现,可以自定义一些后置处理
postProcessBeanFactory(beanFactory);
// 执行BeanFactoryPostProcessors后置处理,对BeanFactory的创建之后进行一些后置处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor(Bean的后置处理器),同时对AOP中的bean的增强处理和bean的创建初始化也是在这里进行的
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
initMessageSource();
// 初始化事件派发器
initApplicationEventMulticaster();
// 子类重写这个方法,在容器刷新的时候可以自定义逻辑;
onRefresh();
// 检查容器中将所有项目里面的ApplicationListener监听器,并注册进来
registerListeners();
// 创建,并初始化单例模式的bean,然后并且会在bean初始化方法执行前后进行后置处理
finishBeanFactoryInitialization(beanFactory);
// 完成BeanFactory的初始化创建工作;IOC容器就创建完成,并且刷新容器
finishRefresh();
}
}
bean的懒加载
Spring 可以是懒加载的,主要针对单实例bean,就是当真正使用到 Bean 的时候才实例化 Bean。使用@Lazy实现。
bean相互依赖注入问题
Spring的单例对象的初始化主要分为三步:
(1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
(3)initializeBean:调用spring xml中的init 方法。
从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二部。也就是构造器循环依赖和field循环依赖。
那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
这三级缓存分别指:
singletonFactories : 单例对象工厂的cache
earlySingletonObjects :提前暴光的单例对象的Cache
singletonObjects:单例对象的cache
Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则:从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。
从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于
singletonFactories这个三级cache。这个cache的类型是ObjectFactory。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。
知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
为什么要使用spring
spring是一个开源框架,spring可以有效的控制J2EE各个应用层之间的对象,不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,
都可在Spring的管理下有机地协调、运行。Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现。
Service对象无须关心持久层对象的具体实现,各层对象的调用完全面向接口。当系统需要重构时,代码的改写量将大大减少。
因为平时我们的开发很复杂,每个对象之间的互相调用关系错综复杂,对象多了之后管理起来就会非常麻烦,spring就为我们解决了这方面的问题。
spring的IOC即是控制反转,为我们管理了所有的bean,本来以前A对象想要调用B对象的时候,我们需要new一个B对象的实例,由我们自己去控制依赖关系,现在变成了由spring的IOC容器控制,帮助我们创建B实例,并且实现依赖关系,这种设计思想就是控制反转,同时依赖反转又称为DI(依赖注入),自动的帮助我们把被依赖的bean注入到依赖的bean里面去。这样的好处就是减少了耦合性,使得bean的管理更加方便快捷高效。
同时,IOC是采用反射机制实现的,反射对于工厂模式的区别就是,反射对于对象的编译是动态的,能够更加灵活,缺点是效率低。工厂模式缺点就是不灵活,当工厂对象需要发生变动的时候,或者需要增加工厂对象的时候,就需要修改工厂方法,或者增加工厂的实现类,这样就不利于解耦。
同时IOC的低侵入性也很好,不需要像Struts2,EJB这样的框架一样,使用需要继承或者实现,IOC的bean都不需要继承或者实现,侵入性很低,可移植性很高。
springmvc
1,父子容器,继承IOC的容器作为子容器,用来加载controller,modelandview等bean,service,dao等bean用IOC加载。springmvc的IOC可以访问spring的IOC,但是spring的IOC不可以访问springmvc的IOC。
2,安全机制?
在容器启动完成之后,不允许再往容器中创建和初始化bean。
3,WEB-INF下的文件受保护机制。
通常把那些限制访问的资源(比如说jsp源代码)放到Web应用的WEB-INF目录下,对于/web-INF/及其子目录,不允许直接的公共访问,
所以就可以起到保护这些代码未经授权的访问和窥视,更好的保护了源代码。为了减少风险,可以把这些页面文件移到WEB-INF
目录下。基于Servlet的声明,WEB-INF不作为Web应用的公共文档树的一部分。因此,WEB-INF
目录下的资源不是为客户直接服务的。我们仍然可以使用WEB-INF目录下的JSP页面来提供视图给客户,客户却不能直接请求访问JSP。
4,同步和异步请求。
tomcat9以下的通信机制中是bio,之上是nio,netty中是nio。
异步处理:请求之后,主线程会马上将连接释放(tomcat中的线程连接数就不会那么有压力),将真正执行的业务部分放入到异步线程中去执行,
当异步执行完成之后再返回响应。采用异步,是两个线程(主从),会触发两次拦截器或者过滤器。
异步相对同步的优点:
很多个线程同时请求,同步会是这些线程依次排队等待上一个线程处理完,这样最后一个线程是花最多时间拿到结果的
如果是异步的话,这很多个线程都是和第一个线程一样的相同时间拿到结果
springmvc的工作原理
springmvc最新版本是基于servlet3.0开发的。
入口是springmvc-web这个包下的META-INF/services目录下的javax.servlet.ServletContainerInitializer中配置的是
SpringServletContainerInitializer这个类,这个类实现ServletContainerInitializer接口,加载WebApplicationInitializer这个类的所有子类实现,并初始化DispatcherServlet和springmvc的IOC容器(Servlet容器,spring的是Root容器),即是WebApplicationContext(WebApplicationContext继承了ApplicationContext)。
流程:
1,springmvc的核心控件是DispatchServlet,DispatchServlet是一个中央控制器,用户从客户端向服务器发送http请求,请求首先发送到DispatchServlet,再由DispatchServlet将请求分发给HandlerMapping(处理器映射器)。
2,HandlerMapping(处理器映射器)根据请求url寻找到对应的处理器(controller),并且寻找是否存在在处理器执行之前的处理器拦截器(HandlerInterceptor),返回给DispatchServlet。
3,HandelAdapter(处理器适配器)再根据请求,在处理器映射器返回的处理器中寻找具体的执行方法,去执行具体的处理器。
4,执行具体的方法中之后会返回一个ModelAndView对象给DispatchServlet。
5,DispatchServlet收到ModelAndView这个对象,但是这是一个逻辑试图,需要用试图解析器进行解析。
6,ViewResolver(试图解析器)将ModelAndView进行解析,将解析后的结果返回给DispatchServlet。
7,DispatchServlet将解析之后的视图再返回给客户端。
springmvc和Struts2的区别
区别1:
Struts2的核心是基于一个Filter即StrutsPreparedAndExcuteFilter
SpringMvc的核心是基于一个Servlet即DispatcherServlet(前端控制器)
区别2:
Struts2是基于类开发的,传递的参数是通过类的属性传递(属性驱动和模型驱动),所以只能设计成多例prototype
SpringMvc是基于类中的方法开发的,也就是一个url对应一个方法,传递参数是传到方法的形参上面,所以既可以是单例模式也可以是多例模式singiton,controller是单例的。
区别3:
Struts2采用的是值栈存储请求以及响应数据,OGNL存取数据
SpringMvc采用request来解析请求内容,然后由其内部的getParameter给方法中形参赋值,再把后台处理过的数据通过ModelAndView对象存储,Model存储数据,
View存储返回的页面,再把对象通过request传输到页面去。