Spring框架是Java程序员必须要掌握的一个基础框架。
1、什么是控制反转
控制反转是应用于软件工程领域中的,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对
象之间耦合关系在编译时通常是未知的。在传统的编程方式中,业 务逻辑的流程是由应用程序中的
早已被设定好关联关系的对象来决定的。在使用控制反转的情况下,业务逻辑的流程是由对象关系
图来决定的,该对象关系图由装配 器负责实例化,这种实现方式还可以将对象之间的关联关系的定
义抽象化。而绑定的过程是通过“依赖注入”实现的
2、BeanFactory和applicationContext两者有何区别
BeanFactory 可以理解为含有 bean 集合的工厂类。 BeanFactory 包含了种 bean 的定义,以便
在接收到客户端请求时将对应的 bean 实例化。
BeanFactory 还能在实例化对象的时生成协作类之间的关系。此举将 bean 自身与 bean 客户端的
配置中解放出来。 BeanFactory 还包含 了 bean 生命周期的控制,调用客户端的初始化方法
( initialization methods)和销毁方法( destruction methods)。
从表面上看, application context 如同 bean factory 一样具有 bean 定义、 bean 关联关系的设
置,根据请求分发 bean 的功能。但 applicationcontext 在此基础上还提供了其他的功能。
比如:国际化、统一资源文件读取、在监听器中注册bean事件
3、ApplicationContext 实现方式
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
XmlWebApplicationContext
AnnotationConfigApplicationContext
4、基于Java配置和基于xml配置
@Configuration
public class AppConfig{
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
<beans>
<bean id="myService" class="com.somnus.services.MyServiceImpl"/>
</beans>
5、如何开启注解方式
Spring 在 2.5 版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代 XML 方
式的 bean 描述,可以将 bean 描述转移到组件类的 内部,只需要在相关类上、方法上或者字段声
明上使用注解即可。
注解装配在 Spring 中是默认关闭的。所以需要在 Spring 文件中配置一下才能使用基于注解的装配
模式。
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
6、bean的生命周期
在一个 bean 实例被初始化时,需要执行一系列的初始化操作
以达到可用的状态。同样的,当一个 bean 不在被调用时需要进行相关的析构操作,并从 bean 容
器中移除。
Spring bean factory 负责管理在 spring 容器中被创建的 bean 的生命周期。 Bean 的生命周期
由两组回调( call back)方法组成。
初始化之后调用的回调方法。
销毁之前调用的回调方法。
Spring 框架提供了以下四种方式来管理 bean 的生命周期事件:
InitializingBean 和 DisposableBean 回调接口
针对特殊行为的其他 Aware 接口
Bean 配置文件中的 Custom init()方法和 destroy()方法
@PostConstruct 和@PreDestroy 注解方式
例如:
<beans>
<bean id="demoBean" class="com.somnus.task.DemoBean" initmethod="customInit" destroy-method="customDestroy"></bean>
</beans>
7、如何在 Spring 中注入一个 Java Collection?
Spring 提供了以下四种集合类的配置元素:
<list> : 该标签用来装配可重复的 list 值。
<set> : 该标签用来装配没有重复的 set 值。
<map>: 该标签可用来注入键和值可以为任何类型的键值对。
<props> : 该标签支持注入键和值都是字符串类型的键值对。
<beans>
<!-- Definition for javaCollection -->
<bean id="javaCollection" class="com.howtodoinjava.JavaCollection">
<!-- java.util.List -->
<property name="customList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>UK</value>
</list>
</property>
<!-- java.util.Set -->
<property name="customSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
</set>
</property>
<!-- java.util.Map -->
<property name="customMap">
<map>
<entry key="1" value="INDIA"/>
<entry key="2" value="Pakistan"/>
<entry key="3" value="USA"/>
<entry key="4" value="UK"/>
</map>
</property>
<!-- java.util.Properties -->
<property name="customProperies">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</bean>
</beans>
8、bean的自动装配
在 Spring 框架中,在配置文件中设定 bean 的依赖关系是一个很好的机制, Spring 容器还可以自
动装配合作关系 bean 之间的关联关系。这意味着 Spring 可以通过向 Bean Factory 中注入的方
式自动搞定 bean 之间的依赖关系。自动装配可以设置在每个 bean 上,也可以设定在特定的 bean
上。
XML 配置文件表明了如何根据名称将一个 bean 设置为自动装配:
<bean id="employeeDAO" class="com.howtodoinjava.EmployeeDAOImpl"
autowire="byName" />
除了 bean 配置文件中提供的自动装配模式,还可以使用@Autowired 注解来自动装配指定
的 bean。在使用@Autowired 注解之前需要在按照如下的配置方式在 Spring 配
置文件进行配置才可以使用。
<context:annotation-config />
配置好以后就可以使用@Autowired 来标注了。
@Autowired
public EmployeeDAOImpl ( EmployeeManager manager ) {
this.manager = manager;
}
9、构造方法注入和设值注入有什么区别?
1.在设值注入方法支持大部分的依赖注入,如果我们仅需 要注入 int、 string 和 long 型的变量,我
们不要用设值的方法注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值。
在构造方法 注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则
的话为报错。
2. 设值注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方
法注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法尽在对象被创
建时调用。
3. 在使用设值注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可
能是不完整的。而在另一种情况下,构造器注入则不允许生成依赖关系不完整的对象。
4. 在设值注入时如果对象 A 和对象 B 互相依赖,在创建对象 A 时 Spring 会抛出
sObjectCurrentlyInCreationException 异常,因为在 B 对象被创建之前 A 对
象是不能被创建的,反之亦然。所以 Spring 用设值注入的方法解决了循环依赖
的问题,因对象的设值方法是在对象被创建之前被调用的
10、SpringMVC 流程?
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 响应用户。
11、spring源码里用到的设计模式
1、模版设计模式
在spring中大量使用了模版设计模式,也是最多的设计模式。模版设计模式就是在父类中定义流程骨架,在实现类中去对应不同的实现。在spring源码中更多的是在父类中写通用的方法实现,特殊的定义为抽象方法,在具体子类中分别有不同的实现。
public abstract AbsPlay
{
public void 娱乐()
{
看电视();
看电影();
玩游戏();
}
public void 看电视(){
System.out.println("看动画片");
}
public void 看电影(){
System.out.println("看成龙的电影");
}
public abstract void 玩游戏();
}
public class XiaoMing extends AbsPlay
{
@Override
public void 玩游戏(){
System.out.println("玩英雄联盟");
}
}
public class XiaoWang extends AbsPlay
{
@Override
public void 玩游戏(){
System.out.println("玩QQ飞车");
}
}
main(){
AbsPlay xiaoming=new XiaoMing();
xiaoming.娱乐();
AbsPlay xiaowang=new XiaoWang();
xiaowang.娱乐();
}
2、代理模式
小米手机要生产手机,需要骁龙生产芯片
public interface Company
{
void product();
}
public class XiaoLong extends Company
{
@Override
public void product(){
System.out.println("生产骁龙芯片");
}
}
public class XiaoMi extends Company
{
XiaoLong xiaolong=new XiaoLong();
@Override
public void product(){
xiaolong.product();
//其他公司的其他部件
}
}
3、装饰模式
装饰模式有几个元素很重要, 1、 被装饰者。 2、 抽象装饰者。 3、 装饰者对象
public interface MyLOLInfo
{
public void duanwei();
public void myLeague();
}
@Data
public class MyLOLInfo2016 implements MyLOLInfo
{
private List<String> myLeagueList=new ArrayList<>();
@Override
public void duanwei(){
System.out.println("2016倔强青铜5");
}
@Override
public void myLeague(){
myLeagueList.add("寒冰射手");
myLeagueList.add("符文法师");
System.out.println("2016拥有英雄:寒冰射手-符文法师");
}
}
public abstract class Decorator implements MyLOLInfo
{
public MyLOLInfo myLOLInfo;
public Decorator(MyLOLInfo myLOLInfo){
this.myLOLInfo=myLOLInfo;
}
@Override
public void duanwei(){
myLOLInfo.duanwei();
}
@Override
public void myLeague(){
myLOLInfo.myLeague();
}
}
public class Decorator2019 extends Decorator
{
public Decorator2019(MyLOLInfo myLOLInfo){super(myLOLInfo)}
@Override
public void duanwei(){
super.duanwei();
System.out.println("2019升级到华贵铂金1");
}
@Override
public void myLeague(){
super.myLeague();
addLeague();
}
public void addLeague(){
MyLOLInfo2016 myLOLInfo2016=(MyLOLInfo2016)MyLOLInfo;
myLOLInfo2016.getMyLeague().add("提莫");
myLOLInfo2016.getMyLeague().add("加里奥");
System.out.println("2019增加拥有英雄:提莫-加里奥");
}
}
public class Test
{
public static void main(String[] args) {
MyLOLInfo myInfo=new MyLOLInfo2016();
myInfo.duanwei();
myInfo.myLeague();
///2019
MyLOLInfo myInfo2019=new Decorator2019(myInfo);
myInfo2019.duanwei();
myInfo2019.myLeague();
}
}
以上是我手写了的几个设计模式的例子。具体体现在哪里呢?
工厂方法:Spring使用工厂模式可以通过 BeanFactory
或 ApplicationContext
创建 bean 对象。
单例模式:Spring 中 bean 的默认作用域就是 singleton(单例)的。
代理模式:Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,
模版方法:各种Template比如jdbcTemplate、amqpTemlpate
适配器模式:我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter;
在Spring MVC中,DispatcherServlet
根据请求信息调用 HandlerMapping
,解析请求对应的 Handler
。解析到对应的 Handler
(也就是我们平常说的 Controller
控制器)后,开始由HandlerAdapter
适配器处理。HandlerAdapter
作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
装饰者模式:Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下动态切换不同的数据源?这个时候就要用到装饰者模式(这一点我自己还没太理解具体原理)。Spring 中用到的包装器模式在类名上含有 Wrapper
或者 Decorator
。这些类基本上都是动态地给一个对象添加一些额外的职责
观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用。Spring 事件驱动模型非常有用,在很多场景都可以解耦我们的代码。比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题。