JavaBean
+ Servlet
+ Jsp
逐步发展 EJB
重量级框架带来的种种麻烦 SpringMVC
/ Struts
+ Spring
+ Hibernate
/ myBatis
SpringBoot
"约定大于配置"的种种乐趣,很多繁琐的配置都变成了约定 Dubbo
为代表的 SOA
为服务架构体系(Dubbo是阿里创建的) SpringCloud
微服务架构技术生态圈(基于SpringBoot,保证了开箱即用,需要什么SpringCloud都有)
不只是 Spring
的 IoC
,其他的框架也具有相同的IoC思想及应用:
控制 反转
①所有 Bean
的生命周期交由 IoC容器
管理
②所有 被依赖的Bean
通过构造方法执行注入
③ 被依赖的Bean
需要优先创建
通过 Spring.xml
文件实例化 Bean
.只要 Spring.xml
中配置了 bean
标签,则就会根据 class
创建这个 bean
的实例.
把一个java bean交给Spring管理步骤:
1、创建一个xml格式的配置文件
2、在xml文件中定义一个bean,给每个bean设定一个id
3、通过ApplicationContext获取Spring上下文
ApplicationContext context = new ClassPathXmlApplicatioinContext("文件名.xml");
4、获取bean
Bean bean = context.getBeal("第一步中给bean的id",Bean.class);
构造方法
实例化Bean 步骤1:创建要实例化的类,并提供无参构造方法。
public class Bean { public Bean(){ System.out.println("Bean被创建了"); }
步骤2: spring.xml
中配置
<bean id="bean的唯一标识" class="要实例化bean的路径"></bean>
测试:
ApplicationContext context=new ClassPathXmlApplicationContext(spring.xml的路径); Bean bean=context.getBean("bean",Bean.class); System.out.println(bean);
静态工厂
方法实例化Bean
通过 Bean2
的工厂的静态方法实例化 Bean
.
步骤1:创建 Bean2
的工厂 Bean2Factory
类以及 Bean2
类,并且提供一个静态、返回值类型为 Bean2
的方法,返回 new Bean2()
。
public class Bean2Factory { public static Bean2 getBean2(){ return new Bean2(); } } public class Bean2 { public Bean2(){ System.out.println("Bean2被创建了。"); }
步骤2:配置 Spring.xml
<bean class="Bean2工厂的路径" factory-method="Bean2工厂的静态方法名" id="Bean2工厂的唯一标识"></bean>
测试:
ApplicationContext context=new ClassPathXmlApplicationContext("spring-ioc2.xml"); Bean2 bean=context.getBean("bean2factoryId",Bean2.class); System.out.println(bean);
实例工厂
方法实例化Bean
通过 Bean3
的工厂的实例方法实例化 Bean
.
步骤1:创建 Bean3
的工厂 Bean3Factory
类以及 Bean3
类,并且提供一个返回值类型为 Bean3
的方法,方法返回 new Bean3()
。
public class Bean3Factory { public Bean3 getBean3(){ return new Bean3(); } } public class Bean3 { public Bean3(){ System.out.println("Bean3被创建了。"); } }
步骤2:配置 Spring.xml
<bean class="main.java.com.imooc2.Bean3Factory" id="bean3Factory"></bean> <bean class="main.java.com.imooc2.Bean3" factory-bean="bean3Factory" factory-method="getBean3" id="bean3"></bean>
有类 Bean1
,并且通过 bean
标签实例化 Bean1
,可以给 Bean1
的实例取多个名字。
方式一: bean标签里添加name属性
<bean id="bean" class="main.java.com.imooc1.Bean" name="bean1_1,bean1_2"></bean>
方式二: 添加<alias>标签
<bean id="bean" class="main.java.com.imooc2.Bean1" name="bean1_1,bean1_2"></bean> <alias name="bean" alias="bean1_3"/><!-- alias只支持一个别名 -->
构造方法
注入Bean
简化写法:
Set()方法
注入Bean
集合类型
注入Bean
null
空值
bean
Singleton
作用域(单例模式)
通过Spring容器实现单例模式,具有局限性:保证在一个Spring上下文( ApplicationContext
)是单例模式,多个 AppliactionContext
单例模式就失效了。
prototype
作用域(多例模式) 如果一个<bean>的作用域为prototype,则该<bean>会被实例化多次,有多个Bean会被创建(每次向Spring上下文请求该Bean都会new一个新的实例)。
Bean1依赖Bean2,总结起来:
对于蓝框中的情况,有时候我们需要单个Bean1,多个Bean2,就要用到方法注入.
Bean2代码修改:
Spring.xml修改:
这样即使Bean1是单例模式,每次拿到Bean1都会是不同的Bean2实例.
Web环境
作用域
request
作用域、 session
作用域、 application
作用域、 websocket
作用域(使用较少).....
request:每次浏览器刷新都会实例化一个新的bean;
session:浏览器刷新不会实例化新的bean,更换浏览器则会实例化新的bean;
application:bean实例化之后就不会改变,更换浏览器也不会。
在web.xml中定义:
不使用DispatcherServlet:
以RequestController为例:
在spring.xml中添加:
自定义
作用域 SimpleThreadScope
作用域 双例模式
为例:
(org.springframework.beans.factory.config.Scope)主要关注实现的get方法和remove方法。
get()
方法:按照name参数,按照我们自己定义的规则,去返回一个Bean,如果我们定义的规则里,Bean不存在,那么将通过objectFactory去创建一个Bean。
双例模式:一个bean的Id对应两个实例,实现双例模式,需要两个Map集合,Map<String,Object> map1=new HashMap<String,Object>();Map<String,Object> map2=new HashMap<String,Object>();。
get()方法:
public Object get(String name, ObjectFactory<?> objectFactory) { if(!map1.containsKey(name)){ //判断map1是否包含名为name的Bean实例 Object o=objectFactory.getObject();//如果不存在则创建一个ObjectFactory规则Bean map1.put(name, o); //并把这个Bean放入集合,并命名为name return o; } if(!map2.containsKey(name)){ //map2同map1操作 Object o=objectFactory.getObject(); map2.put(name, o); return o; } //如果map1和map2都包含这个Bean,也就是说Spring上下文通过Id获取有两个实例,则返回一个0或1 int i=new Random().nextInt(2); if(i==0){ return map1.get(name);//如果是0,则返回对象1 }else{ return map2.get(name);//如果是0,则返回对象2 } }
remove()
方法:和get方法相反,按照name参数,去移除一个Bean。
public Object remove(String name) { if(map1.containsKey(name)){ Object o=map1.get(name); map1.remove(name); return o; } if(map2.containsKey(name)){ Object o=map2.get(name); map2.remove(name); return o; } return null; //说明没拿到任何实例 }
<bean id="myScope" class="main.java.com.自定义作用域.MyScope"></bean> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="myscope" value-ref="myScope"></entry> </map> </property> </bean>
@Test public void testBean(){ ApplicationContext ac=new ClassPathXmlApplicationContext("spring-zidingyi.xml"); for(int i=0;i<10;i++){ Bean bean=ac.getBean(“myScope”", Bean.class); System.out.println(bean); } }
SimpleThreadScope
Spring提供的 线程内单例
作用域:
srping配置:
<bean id="myScope" class="main.java.com.自定义作用域.MyScope" ></bean> <bean id="simpleThreadScope" class="org.springframework.context.support.SimpleThreadScope"></bean> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="myscope" value-ref="myScope"></entry> <entry key="simpleThreadScope" value-ref="simpleThreadScope"></entry> </map> </property> </bean> <bean id="bean11" class="main.java.com.自定义作用域.Bean" scope="simpleThreadScope"></bean>
测试代码:
public void testBean(){ final ApplicationContext ac=new ClassPathXmlApplicationContext("spring-zidingyi.xml"); for(int i=0;i<10;i++){ new Thread(new Runnable(){ @Override public void run() { Bean bean=ac.getBean("bean11", Bean.class); System.out.println(bean); } }).start(); } } }
Spring容器会在创建容器时提前初始化 Singleton
作用域的bean,但是如果Bean被标注了 lazy-init=“true”
,则该Bean只有在其被需要的时候才会被初始化。(只对singleton作用域的bean有效)
如
<bean id="bean" class="main.java.com.Bean懒加载.Bean" scope="singleton" lazy-init="true"></bean>
多个bean使用懒加载:
<beans default-lazy-init=“true”></beans>
表示spring配置文件里所有bean都是懒加载模式。
使用场景:
如果某个Bean在程序整个运行周期都可能不会被使用,那么可考虑设定该Bean为懒加载。
以singleton作用域为例,假设有一个javaBean,该Bean的作用是连接数据库,该Bean被创建之后,执行数据库连接操作.
如果需要在Bean实例化时执行一些逻辑,Spring提供了两种方法:
init-method InitializingBean
如果需要在Bean销毁之前执行一些逻辑,有两种方法。
destory-method DisposableBean
通过bean标签的init-method和destory-method属性来指定Bean初始化逻辑和销毁逻辑。
代码:
public class Bean { public void onInit(){ System.out.println("Bean的初始化逻辑方法被执行了"); } public void onDestory(){ System.out.println("Bean的销毁逻辑方法被执行了"); } }
spring.xml配置:
<bean class="main.java.com.Bean初始化及销毁.Bean" id="bean" init-method="onInit" destroy-method="onDestory"/>
测试代码:
@Test public void test(){ ApplicationContext ac=new ClassPathXmlApplicationContext("spring-initAnddestory.xml"); Bean bean=ac.getBean("bean", Bean.class); System.out.println(bean); }
结果:bean的销毁方法没执行,因为当前是单例模式,所以Bean的初始化是在Spring上下文实例化完成的,Bean的销毁是在Spring上下文的销毁过程中执行Bean的销毁。(Spring上下文的销毁定义到AbstractApplicationContext中)
改进:
@Test public void test(){ AbstractApplicationContext ac=new ClassPathXmlApplicationContext("spring-initAnddestory.xml"); Bean bean=ac.getBean("bean", Bean.class); System.out.println(bean); ac.close(); }
注意:如果所有的Bean都有相同名称的初始化方法和相同名称的销毁方法,可以在spring.xml中定义:
<beans default-init-method="onInit" default-destory-method="onDestory"/>
实现相应接口,并实现相应方法
代码:
public class Bean implements InitializingBean,DisposableBean{ @Override public void destroy() throws Exception { System.out.println("Bean的销毁逻辑方法被执行了"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Bean的初始化逻辑方法被执行了"); } }
spring.xml配置:
<bean class="main.java.com.Bean初始化及销毁.Bean" id="bean"></bean>
场景1:
ParentClass(属性1,属性2,属性3,get和set方法) 子类1(属性4,属性5) 子类2(属性6,属性7)
场景2:
子类1和子类2没有父类,而是有一些相同的属性。
类1:(属性1,属性2,属性3,属性4,属性5) 类2:(属性1,属性2,属性3,属性6,属性7)
这两个场景下,通过Spring注入类1和类2的所有属性,一般如下:
abstract="true"
:设置 <bean>
标签只是定义性的,不会对它进行实例化操作。 parent属性
:在Bean的子类bean标签添加,值为父类的Id。 场景1:
spring.xml配置:
<bean id="bean" class="main.java.com.Bean继承属性.ParentClass" abstract="true"> <property name="attribute1" value="attribute1"></property> <property name="attribute2" value="attribute2"></property> <property name="attribute3" value="attribute3"></property> </bean> <bean id="bean1" class="main.java.com.Bean继承属性.Class1" parent="bean"> <property name="attribute4" value="attribute4"></property> <property name="attribute5" value="attribute5"></property> </bean> <bean id="bean2" class="main.java.com.Bean继承属性.Class2" parent="bean"> <property name="attribute6" value="attribute6"></property> <property name="attribute7" value="attribute7"></property> </bean>
场景2:
类1和类2不继承某一父类,只是存在相同属性。
spring.xml配置:
<bean id="bean" abstract="true"> <property name="attribute1" value="attribute1"></property> <property name="attribute2" value="attribute2"></property> <property name="attribute3" value="attribute3"></property> </bean> <bean id="bean1" class="main.java.com.Bean继承属性.Class1" parent="bean"> <property name="attribute4" value="attribute4"></property> <property name="attribute5" value="attribute5"></property> </bean> <bean id="bean2" class="main.java.com.Bean继承属性.Class2" parent="bean"> <property name="attribute6" value="attribute6"></property> <property name="attribute7" value="attribute7"></property> </bean>