回顾:
面试官关于spring最喜欢、也是概率最大的提问
谈谈你对spring的理解
spring的官方定义:
Spring 是一个轻量级的控制反转 (IOC) 和面向切面 (AOP) 的开源容器框架
spring有一些优点:
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容器提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控,系统日志等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,mybatis,Struts2等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制(依赖注入)降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring
的部分或全部。
可以说spring是程序员的春天。
可以从IOC和AOP的理解入手说明
谈谈你对IOC的理解
这是一个衍生问题:
什么是IOC?--> 为什么要IOC-->控制反转 是控制了谁对谁的反转?-->IOC容器完成了哪些事情-->做的这些事情与工厂模式有什么异同。
什么是IOC
IoC(Inversion of Control)控制反转,它是类与类依赖关系交给容器处理。
有个网友对IOC形容得很贴切:https://www.zhihu.com/question/48427693?sort=created
为什么要用IOC
- 减少了对象的创建和管理 ,使代码层次更加清晰。
- Spring 的IOC容器是一个轻量级的容器 ,没有侵入性(不依赖容器的API) ,不需要实现一些特殊接口。
- 鼓励我们面向接口编程。
- 减少了代码的耦合,将耦合的部分推到了配置文件中 ,如果他们的关系发生了改变,只需要修改配置文件。
控制反转 是控制了谁对谁的反转
- 控制指的是:当前对象对内部成员的控制权。
- 反转指的是:这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;
IOC容器完成了哪些事情
IOC容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系
- 通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
- 再通过配置文件(xml)或者注解来描述类与类之间的关系
- 我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了
IOC做的这些事情与工厂模式有什么异同
相同点:把主动变被动:产品的具体实现(生产)交给工厂处理,而业务逻辑(客户代码)只需要向工厂索要产品即可,他并不关心,工厂如何生产,并且生产什么。只要符合我们之前约定的产品本质即可
不同的地方:IOC不再需要程序主动去new对象,你看不到new操作,也看不到工厂模式的代码了。用户只要关注业务代码就可以。
谈谈你对AOP的理解
这也是一个衍生问题:什么是AOP?--> 能做什么--> 一个简单的AOP打算怎么实现?
什么是AOP
面向切面(方面)编程
aop能做什么
可以把业务逻辑和系统级的服务进行隔离(系统级的服务像系统的日志,事务,权限验证等)
一个简单的AOP打算怎么实现
实现一个业务系统日志切面
1、定义注解log
@Target({ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
/**91:平台统计**/
int OPERA_TYPE_STATISTICS = 91;
public int operation() ;//操作码
public String moudle() ;//模块名
public String subMoudle();//子模块名
public String title();//操作说明
}
2、在controller层,对业务方法进行织入
@Controller
@RequestMapping("/v1.0/basicstatistics")
public class StatisticsController {
@Autowired
private StatisticsService statisticsService;
@Log(operation=Log.OPERA_TYPE_STATISTICS,title="查看统计详情", moudle = "平台", subMoudle = "统计")
@RequestMapping("/inquireStatisticsDetailGlobal.json")
@ResponseBody
public ServerResponse<InquireStatisticsDetailGlobalOutput> inquireStatisticsDetailGlobal(){
return statisticsService.inquireStatisticsDetail();
}
}
对请求路径进行拦截处理--操作日志记录拦截
public class LogHandlerInterceptor implements HandlerInterceptor {
// 请求处理之前进行调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
// 请求处理之后,DispatcherServlet进行视图返回渲染之前进行调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerMethod handler2=(HandlerMethod) handler;
Log log = handler2.getMethod().getAnnotation(Log.class);//可以得到log对象数据
System.out.println("处理log日志");
}
// 在DispatcherServlet 渲染了对应的视图之后执行。用于进行资源清理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long payTime = System.currentTimeMillis()-(long) request.getAttribute("startTime");//业务处理花费时间
System.out.println("afterCompletion 业务处理好,资源清理完成,返回前端");
}
}
解释Spring Bean的生命周期
Spring Bean的生命周期简单易懂。在一个bean实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个bean不在被调用时需要进行相关的析构操作,并从bean容器中移除。
Spring bean factory 负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。
- 初始化之后调用的回调方法。
- 销毁之前调用的回调方法。
Spring框架提供了以下四种方式来管理bean的生命周期事件:
- InitializingBean和DisposableBean回调接口
- 针对特殊行为的其他Aware接口
- Bean配置文件中的Custom init()方法和destroy()方法
- @PostConstruct和@PreDestroy注解方式
使用
customInit()
和customDestroy()
方法管理
bean
生命周期的代码样例如下:
-
<beans> <bean id="demoBean" class="com.somnus.task.DemoBean" init-method="customInit" destroy-method="customDestroy"></bean> </beans>
-
Spring Bean的作用域
Spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下:
- singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
- prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
- request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
全局作用域与Servlet中的session作用域效果相同。使用3,4,5作用域的,需要手动设置代理
BeanFactory和ApplicationContext有什么区别
BeanFactory 可以理解为含有bean集合的工厂类。BeanFactory 包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。
application context是beanFactory的子接口,拥有BeanFactory的所有功能,但applicationcontext在此基础上还提供了其他的功能。
- 提供了支持国际化的文本消息
- 统一的资源文件读取方式
- 已在监听器中注册的bean的事件
且beanFactory是延迟加载,需要类的时候才创建类的实例,而ApplicationContext在初始化时就加载完成了所有的单例bean
以下是三种较常见的 ApplicationContext 实现方式:
1、ClassPathXmlApplicationContext:从classpath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
2、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
3、XmlWebApplicationContext:由Web应用的XML文件读取上下文。
Spring框架中的单例Beans是线程安全的吗
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
构造方法注入和属性注入有什么区别
- 在属性注入方法支持大部分的依赖注入,如果我们仅需要注入int、string和long型的变量,我们不要用设值的方法注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值。在构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则的话为报错。
- 属性注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方法注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法尽在对象被创建时调用。
- 在使用属性注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可能是不完整的。而在另一种情况下,构造器注入则不允许生成依赖关系不完整的对象。
- 在属性注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出s
ObjectCurrentlyInCreationException异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然。所以Spring用设值注入的方法解决了循环依赖的问题,因对象的设值方法是在对象被创建之前被调用的。
Spring 框架中都用到了哪些设计模式
Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:
- 代理模式—在AOP和remoting中被用的比较多。
- 单例模式—在spring配置文件中定义的bean默认为单例模式。
- 模板方法—用来解决代码重复的问题。比如. RestTemplate,
JmsTemplate
,JpaTemplate。
- 前端控制器—Spring提供了
DispatcherServlet
来对请求进行分发。
- 视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
- 依赖注入—贯穿于
BeanFactory
/ApplicationContext
接口的核心理念。
- 工厂模式—BeanFactory用来创建对象的实例。
后续会不定期更新关于spring面试题目和答案。。再见