目录
5. BeanFactory和 ApplicationContext 有什么区别?
6. ApplicationContext通常的实现是什么?
21. Spring AOP和AspectJ AOP有什么区别?
【写在前面】
此文题目和答案都非原创。
是搜集了网络上分享的关于Spring面试常见问题汇总,然后又逐个搜寻了答案,整理于此。
供自学,感谢相关题目和答案的原创分享者。
1. @Component和@Bean的区别是什么?
(1)作用对象不同:@Component 注解作用于类,而@Bean注解作用于方法。
(2)@Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径,从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。
@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean, @Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
(3)@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,只能通过 @Bean来实现。
2. Spring框架中用到了哪些设计模式?
(1)代理模式:在AOP和remoting中被用的比较多。
(2)单例模式:在spring配置文件中定义的bean默认为单例模式。
(3)模板方法模式:解决代码重复问题。父类定义骨架(共同方法的实现,调用哪些方法及顺序),某些特定方法由子类实现(父类是空方法,子类继承后再重写)。
(4)前端控制器模式:spring提供了DispatcherServlet来对请求进行分发。
(5)依赖注入模式:贯穿于BeanFactory和ApplicationContext接口的核心理念。
(6)工厂模式:
------简单工厂:
实现方式:BeanFactory根据传入一个唯一的标识来获得bean对象,由工厂类根据传入的参数动态决定应该创建哪一个产品类。
实现原理:在bean容器的启动阶段,读取bean的xml配置文件,将bean元素分别转换成一个BeanDefinition对象。然后通过BeanDefinitionRegistry将这些bean注册到beanFactory中。
容器中bean的实例化阶段:主要通过反射或者CGLIB对bean进行实例化
------工厂方法:
实现方式:FactoryBean接口
实现原理:实现了FactoryBean接口的bean是一类叫做factory的bean,特点是spring会在使用getBean()调用获得该bean时,自动调用该bean的getObject()方法。返回的是这个bean.getObject()方法的返回值。
典型例子:spring与mybatis的结合:
<bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" ><bean>
//最终返回的是SqlSessionFactoryBean.getObject()的返回值
(7)适配器模式:
实现方式:springmvc中的适配器HandlerAdapter
实现过程:dispatcherServlet根据HandlerMapping返回的handler,向HandlerAdapter发起请求,处理handler。HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向dispatcherServlet返回一个ModelAndView。
(8)装饰器模式:
实现方式:类名中包含Wrapper,或者是Decorator,就是装饰器模式
实质:动态地给一个对象添加一些额外的职责,比生成子类更灵活
(9)观察者模式:
实现方式:spring的事件驱动模型使用的是观察者模式,常用的地方就是listener的实现
具体实现:事件机制的实现包括事件源、事件、事件监听器:
ApplicationEvent抽象类【事件】
ApplicationListener接口【事件监听器】
ApplicationContext接口【事件源】
(10)策略模式:
实现方式:spring框架的资源访问Resource接口,是具体资源访问策略的抽象,也是所有资源访问类所实现的接口
3. Spring MVC的工作原理是什么?
(1)用户发送请求至前端控制器DispatcherServlet。
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3) 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet调用HandlerAdapter处理器适配器。
(5)HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
(6)Controller执行完成返回ModelAndView。
(7)HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
(8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
(9)ViewReslover解析后返回具体View。
(10)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
(11)DispatcherServlet响应用户。
组件说明:
以下组件通常使用框架提供实现:
DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
4. Spring中的bean生命周期?
Bean 的生命周期如上,描述如下:
(1)Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化。
(2)Bean实例化后对将Bean的引入和值注入到Bean的属性中。
(3)如果Bean实现了BeanNameAware接口,Spring将Bean的Id传递给setBeanName()方法。
(4)如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入。
(5)如果Bean实现了ApplicationContextAware接口,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
(6)如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
(7)如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用。
(8)如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
(9)此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
(10)如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
5. BeanFactory和 ApplicationContext 有什么区别?
(1)BeanFactory 可以理解为含有bean集合的工厂类。BeanFactory 包含了各种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。
BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。
基础类型的 IOC 容器,提供完整的 IOC 服务支持。如果没有特殊指定,默认采用延迟初始化策略。相对来说,容器启动初期速度较快,所需资源较少。
(2)从表面上看,ApplicationContext如同BeanFactory 一样具有bean定义、bean关联关系的设置,根据请求分发bean的功能。ApplicationContext 管理的对象,在容器启动后默认全部初始化并且完成绑定。
ApplicationContext提供了一种解决文档信息的方法,一种加载文件资源的方式(如图片),他们可以向监听他们的beans发送消息。另外,容器或者容器中beans的操作,这些必须以bean工厂的编程方式处理的操作可以在应用上下文中以声明的方式处理。应用上下文实现了MessageSource,该接口用于获取本地消息,实际的实现是可选的。
但ApplicationContext在此基础上还提供了其他的功能。
- 提供了支持国际化的文本消息
- 统一的资源文件读取方式
- 已在监听器中注册的bean的事件
以下是三种较常见的 ApplicationContext 实现方式:
- ClassPathXmlApplicationContext:从classpath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
- FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
- XmlWebApplicationContext:由Web应用的XML文件读取上下文。
【补充】
相同点:两者都是通过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩展功能。
不同点:BeanFactory是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。
6. ApplicationContext通常的实现是什么?
(1)FileSystemXmlApplicationContext: 此容器从一个 XML文件中加载beans的定义,XMLBean配置文件的全 路径名必须提供给它的构造函数。
(2)ClassPathXmlApplicationContext: 此容器也从一个 XML文件中加载beans的定义,这里,你需要正确设置 classpath 因为这个容器将在classpath里找bean配置。
(3)WebXmlApplicationContext: 此容器加载一个XML 文件,此文件定义了一个WEB应用的所有bean。
7.Spring AOP 实现原理是什么?
Spring AOP是一种设计思想,称为面向切面编程,利用横切技术剖析对象内部,将业务之间共同调用的逻辑提取并封装为一个可复用的模块,这个模块被命名为切面,该模块减少系统中的重复代码,降低模块间的耦合度,可用于日志,权限认证,事务管理等。
Spring AOP思想的实现一般都是基于代理模式,在Java中采用JDK动态代理模式,但是JDK动态代理模式只能代理接口而不能代理类。因此SpringAOP会在JDK动态代理和CGLIB之间进行切换。
8. 有哪些不同类型的IOC(依赖注入)?
(1)构造器依赖注入(引用方式):构造器依赖注入通过容器触发一个类的构造器来实现 的,该类有一系列参数,每个参数代表一个对其他类的依赖。
(2)Setter 方法注入(传值方式):Setter 方法注入是容器通过调用无参构造器或无参 static 工厂 方法实例化 bean 之后,调用该 bean 的 setter 方法,即实现了基 于 setter 的依赖注入。
9. 解释自动装配的各种模式?
(1)no:这是Spring的默认设置,在该设置下自动装配是关闭的,开发者需要自行在Bean定义中用标签明确地设置依赖关系。
(2)byName:该模式可以根据Bean名称设置依赖关系。当向一个Bean中自动装配一个属性时,容器将根据Bean的名称自动在配置文件中查询一个匹配的Bean。如果找到就装配这个属性,如果没找到就报错。
(3)byType:该模式可以根据Bean类型设置依赖关系。当向一个Bean中自动装配一个属性时,容器将根据Bean的类型自动在配置文件中查询一个匹配的Bean。如果找到就装配这个属性,如果没找到就报错。
(4)constructor:和byType模式类似,但是仅适用于有与构造器相同参数类型的Bean,如果在容器中没有找到与构造器参数类型一致的Bean,那么将会抛出异常。
(5)autodetect:该模式自动探测使用constructor自动装配或者byType自动装配。首先会尝试找合适的带参数的构造器,如果找到就是用构造器自动装配,如果在Bean内部没有找到相应的构造器或者构造器是无参构造器,容器就会自动选择byType模式。
10. Resource 是如何被查找、加载的?
(1)Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。
(2)Spring 为 Resource 接口提供了如下实现类:
UrlResource:访问网络资源的实现类。
ClassPathResource:访问类加载路径里资源的实现类。
FileSystemResource:访问文件系统里资源的实现类。
ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
InputStreamResource:访问输入流资源的实现类。
ByteArrayResource:访问字节数组资源的实现类。
这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。
11.Spring 事务底层原理是什么?
事务都具有以下四个基本特点/属性:
(1)Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败(减款,增款必须一起完成)。
(2)Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。
(3)Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
(4)Durability(持久性):事务完成之后,它对于 系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库
原理:
(1)划分处理单元——IoC
由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首先用spring中的IoC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。
(2)AOP拦截需要进行事务处理的类
Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。
(3)对事务处理实现(事务的生成、提交、回滚、挂起)
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。 为常用数据源支持提供了一系列的TransactionManager。
(4)结合
PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。
12. 什么是事务?
(1)事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。
(2)如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。
(3)当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。
(4)若其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。
13.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 – 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
14.Spring事务中的隔离级别有哪几种?
(1)ISOLATION_DEFAULT
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
另外四个与JDBC的隔离级别相对应;
(2)ISOLATION_READ_UNCOMMITTED
这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。
(3)ISOLATION_READ_COMMITTED
保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
(4)ISOLATION_REPEATABLE_READ
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
(5)ISOLATION_SERIALIZABLE
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
关键词:
1)幻读:事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录;
2)不可重复读取:事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录;
3)脏读:事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。
脏读:指一个事务读取了一个未提交事务的数据。
不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同.一个事务读取到了另一个事务提交后的数据。
虚读(幻读):在一个事务内读取了别的事务插入的数据,导致前后读取不一致(insert)。
15. Spring 事务实现方式
(1)编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
(2)声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。即分为:
基于XML的声明式事务
基于注解的声明式事务
16. Spring框架的事务管理有哪些优点?
它为不同的事务API(如JTA, JDBC, Hibernate, JPA, 和JDO)提供了统一的编程模型。
它为编程式事务管理提供了一个简单的API而非一系列复杂的事务API(如JTA).
它支持声明式事务管理。
它可以和Spring 的多种数据访问技术很好的融合。
17.Spring事务管理的方式有几种?
(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务
18. 将一个类声明为Spring的bean的注解有哪些?
一般使用 @Autowired 注解自动装配 bean,要想把类标识成可用于 @Autowired注解自动装配的 bean 的类,采用以下注解可实现:
@Component :通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用。
@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
@Service :对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
@Controller : 对应 Spring MVC控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
18.Spring中的单例bean的线程安全问题了解吗?
(1)Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
(2)Spring 的 bean 作用域(scope)类型:
singleton:单例,默认作用域。
prototype:原型,每次创建一个新对象。
request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
global-session:全局会话,所有会话共享一个实例。
(3)线程安全这个问题,要从单例与原型Bean分别进行说明。
原型Bean:对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean:对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean, 是一个无状态Bean:也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
(4)对于有状态的bean,Spring官方提供的bean一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。:使用ThreadLocal的好处
使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。
当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。
19. Spring中的bean的作用域有哪些?
Spring Framework支持五种作用域(其中有三种只能用在基于web的Spring ApplicationContext)。
(1)singleton作用域:
Spring的scope的默认值是singleton
Spring 只会为每一个bean创建一个实例,并保持bean的引用.
(2)prototype作用域:
一个bean定义对应多个对象实例。
每一次请求(将其注入到另一个bean中,或执行getBean()方法)都会产生一个新的bean实例,相当于new操作.
Spring一旦将Bean实例交给(注入)调用者,就不再持有这个bean的引用。就无法再执行bean定义的destroy-method.
清除prototype作用域的bean对象并释放资源,是调用者的职责。
(3)request作用域:
HTTP request表示该针对每一次HTTP请求都会产生一个新的bean,仅适用于WebApplicationContext环境。
(4)session作用域:
HTTP session表示该针对每一次HTTP请求都会产生一个新的bean,仅适用于WebApplicationContext环境。
(5)globalSession作用域:
在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。
20. 谈谈自己对于Spring AOP的理解
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。
当我们需要为分散的对象引入公共行为的时候,OOP 则显得无能为力。也就是说,OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。
这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而 AOP 技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。
所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
AOP 代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。
而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。
Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
实现 AOP 的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
21. Spring AOP和AspectJ AOP有什么区别?
(1)AOP是什么?能做什么?
AOP(Aspect Orient Programming),它是面向对象编程的一种补充,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。
AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理和动态代理两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。
面向切面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。AOP提供切面来将跨越对象关注点模块化。
AOP要实现的是在我们写的代码的基础上进行一定的包装,如在方法执行前、或执行后、或是在执行中出现异常后这些地方进行拦截处理或叫做增强处理
Aop的概念
Pointcut:是一个(组)基于正则表达式的表达式,有点绕,就是说他本身是一个表达式,但是他是基于正则语法的。通常一个pointcut,会选取程序中的某些我们感兴趣的执行点,或者说是程序执行点的集合。
JoinPoint:通过pointcut选取出来的集合中的具体的一个执行点,我们就叫JoinPoint.
Advice:在选取出来的JoinPoint上要执行的操作、逻辑。关于5种类型,我不多说,不懂的同学自己补基础。
Aspect:就是我们关注点的模块化。这个关注点可能会横切多个对象和模块,事务管理是横切关注点的很好的例子。它是一个抽象的概念,从软件的角度来说是指在应用程序不同模块中的某一个领域或方面。又pointcut 和advice组成。
Weaving:把切面应用到目标对象来创建新的 advised 对象的过程。
(2)AspectJ是什么?能做什么?
AspectJ是一个易用的功能强大的AOP框架
AspectJ全称是Eclipse AspectJ, 其官网地址是:http://www.eclipse.org/aspectj/ 。
引用官网描述:
- a seamless aspect-oriented extension to the Javatm programming language(一种基于Java平台的面向切面编程的语言)
- Java platform compatible(兼容Java平台,可以无缝扩展)
- easy to learn and use(易学易用)
可以单独使用,也可以整合到其它框架中。
单独使用AspectJ时需要使用专门的编译器ajc。
java的编译器是javac,AspectJ的编译器是ajc,aj是首字母缩写,c即compiler。
(3)AspectJ和Spring AOP的区别?
相信作为Java开发者我们都很熟悉Spring这个框架,在spring框架中有一个主要的功能就是AOP,提到AOP就往往会想到AspectJ,下面我对AspectJ和Spring AOP作一个简单的比较:
Spring AOP
1、基于动态代理来实现,默认如果使用接口的,用JDK提供的动态代理实现,如果是方法则使用CGLIB实现
2、Spring AOP需要依赖IOC容器来管理,并且只能作用于Spring容器,使用纯Java代码实现
3、在性能上,由于Spring AOP是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP的性能不如AspectJ的那么好
AspectJ
- AspectJ来自于Eclipse基金会
- AspectJ属于静态织入,通过修改代码来实现,有如下几个织入的时机:
1、编译期织入(Compile-time weaving): 如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。
2、编译后织入(Post-compile weaving): 也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。
3、类加载后织入(Load-time weaving): 指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法。1、自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了。2、在 JVM 启动的时候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar。
- AspectJ可以做Spring AOP干不了的事情,它是AOP编程的完全解决方案,Spring AOP则致力于解决企业级开发中最普遍的AOP(方法织入)。而不是成为像AspectJ一样的AOP方案
- 因为AspectJ在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的
(4)对比总结
下表总结了 Spring AOP 和 AspectJ 之间的关键区别:
22. 谈谈自己对于Spring IOC的理解
(1)概念:
控制反转:Spring通过一种称作控制反转IoC的技术促进了松耦合。
当应用了IoC, 一个对象依赖的其他对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
(2)IoC容器:
实现了IoC思想的容器。
功能:实例化,初始化组件,装配组件依赖关系,负责组件生命周期管理。
(3)IoC容器特点:
无需主动new对象,而是描述对象应该如何被创建即可。IoC容器会帮忙创建,即被动实例化。
不需要主动装配对象之间的依赖,而是描述需要哪个服务(组件), IoC容器会帮忙装配(即负责将它们关联在一起),被动接受装配。
IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
(4)理解IoC容器的关键问题:
谁控制谁?为什么叫反转?IoC容器控制,而以前是应用程序控制,所以叫反转。
控制什么?控制应用程序所需要的资源(对象,文件...)。
为什么控制?解耦组件之间的关系。
控制的哪些方面被反转了?程序的控制权发生了反转:从应用程序转移到了IoC容器。
23. Spring Boot手动装配有哪几种方式?
(1)使用模式注解 @Component/@Controller/@Component/@Service/@Repository 等
(2)使用配置类 @Configuration 与 @Bean
(3)使用模块装配 @EnableXXX 与 @Import
24. Spring是怎么解决循环依赖的?
(1)回答:循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleA,则它们最终反映为一个环。
(2)Spring如何解决循环依赖?
假设场景如下,A->B->A
1)实例化A,并将未注入属性的A暴露出去,即提前曝光给容器Wrap
2)开始为A注入属性,发现需要B,调用getBean(B)
3)实例化B,并注入属性,发现需要A的时候,从单例缓存中查找,没找到时继而从Wrap中查找,从而完成属性的注入
4)递归完毕之后回到A的实例化过程,A将B注入成功,并注入A的其他属性值,自此即完成了循环依赖的注入
(3)spring中的循环依赖会有3种情况:
1)构造器循环依赖
构造器的循环依赖是不可以解决的,spring容器将每一个正在创建的bean标识符放在一个当前创建bean池中,在创建的过程一直在里面,如果在创建的过程中发现已经存在这个池里面了,这时就会抛出异常表示循环依赖了。
2)setter循环依赖
对于setter的循环依赖是通过spring容器提前暴露刚完成构造器注入,但并未完成其他步骤(如setter注入)的bean来完成的,而且只能决定单例作用域的bean循环依赖,通过提前暴露一个单例工厂方法,从而使其他的bean能引用到该bean.当你依赖到了该Bean而单例缓存里面有没有该Bean的时候就会调用该工厂方法生产Bean,
Spring是先将Bean对象实例化之后再设置对象属性的
Spring先是用构造实例化Bean对象,此时Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法。为什么不把Bean暴露出去,而是暴露个Factory呢?因为有些Bean是需要被代理的。
3)prototype范围的依赖
对于“prototype”作用域bean,Spring容器无法完成依赖注入,因为“prototype”作用域的bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
25. Spring由哪些模块组成?
(1)核心容器 SpringCore
提供Spring框架的基本功能。主要组件是BeanFactory, 是工厂模式的实现。BeanFactory使用控制反转IoC模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
(2)Spring上下文
是一个配置文件,向Spring框架提供上下文信息。包括企业服务。
(3)Spring AOP
通过配置管理特性,AOP模块直接将面向切面的编程功能,集成到Spring框架中。可以很容易的使Spring框架管理任何支持AOP的对象。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。使用Spring AOP,不用依赖组件,就可以将生命性事务管理继承到应用程序中。
(4)Spring DAO
JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。
(5)Spring ORM
Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具。
(6)Spring Web模块
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。
(7)Spring MVC 框架
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架便成为高度可配置的,MVC容纳了大量视图技术。
26. 使用Spring框架的好处是什么?
轻量级:Spring在大小和透明性方面绝对属于轻量级的,基础版本的Spring框架大约只有2MB。
控制反转(IOC):Spring使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。
面向切面编程(AOP): Spring支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。
容器:Spring包含并管理应用程序对象的配置及生命周期。
MVC框架:Spring的web框架是一个设计优良的web MVC框架,很好的取代了一些web框架。
事务管理:Spring对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。
异常处理:Spring提供一个方便的API将特定技术的异常(由JDBC, Hibernate, 或JDO抛出)转化为一致的、Unchecked异常。
27. 什么是spring?
(1)Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。
(2)框架的主要优势之一就是其分层架构,分层架构允许选择使用哪一个组件,同时为了J2EE应用程序开发提供继承的框架。
(3)Spring使用基本的JavaBean来完成以前只能由EJB(EJB是Enterprise Java Beans技术的简称, 又被称为企业Java Beans)完成的事情。
(4)不仅限于服务器端的开发。
(5)Spring的核心是控制反转IoC和面向切面AOP.
(6)Spring是一个分层的JavaSE/EE full-stack一站式轻量级开源框架。