Java Web发展史
第一阶段:JavaBean+Servlet+Jsp逐步发展
第二阶段:面对EJB重量级框架带来的种种麻烦
第三阶段:SpringMVC/Struts+Spring+Hibernate/myBatis
第四阶段:享受SpringBoot"约定大于配置"的种种乐趣,很多繁琐的配置都变成了约定
第五阶段:以Dubbo为代表的SOA为服务架构体系(Dubbo是阿里创建的)
第六阶段:比较前沿的技术,SpringCloud 微服务架构技术生态圈(基于SpringBoot,保证了开箱即用,需要什么SpringCloud都有)
IoC 控制反转
不只是Spring的IoC,其他的框架也具有相同的IoC思想及应用:
控制: 控制对象的创建及销毁(生命周期)
反转:将对象的控制权交给IoC容器
IoC容器约定
①所有Bean的生命周期交由IoC容器管理
②所有被依赖的Bean通过构造方法执行注入
③被依赖的Bean需要优先创建
Spring IoC容器使用方法
概述
通过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);
1、通过构造方法实例化Bean
步骤1:创建要实例化的类,并提供无参构造方法。
public class Bean {
public Bean(){
System.out.println("Bean被创建了");
}
步骤2:spring.xml中配置
测试:
ApplicationContext context=new ClassPathXmlApplicationContext(spring.xml的路径);
Bean bean=context.getBean("bean",Bean.class);
System.out.println(bean);
2、通过静态工厂方法实例化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
测试:
ApplicationContext context=new ClassPathXmlApplicationContext("spring-ioc2.xml");
Bean2 bean=context.getBean("bean2factoryId",Bean2.class);
System.out.println(bean);
3、通过实例工厂方法实例化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取别名
有类Bean1,并且通过bean标签实例化Bean1,可以给Bean1的实例取多个名字。
方式一: bean标签里添加name属性
方式二: 添加标签
依赖注入的方式
1.通过构造方法注入Bean
简化写法:
2.通过Set()方法注入Bean
3.集合类型注入Bean
4.注入null空值
5.注入内部bean
Spring中Bean的作用域
1、Singleton作用域(单例模式)
通过Spring容器实现单例模式,具有局限性:保证在一个Spring上下文(ApplicationContext)是单例模式,多个AppliactionContext单例模式就失效了。
2、prototype作用域(多例模式)
如果一个的作用域为prototype,则该会被实例化多次,有多个Bean会被创建(每次向Spring上下文请求该Bean都会new一个新的实例)。
Bean1依赖Bean2,总结起来:
对于蓝框中的情况,有时候我们需要单个Bean1,多个Bean2,就要用到方法注入.
方法注入
Bean2代码修改:
Spring.xml修改:
这样即使Bean1是单例模式,每次拿到Bean1都会是不同的Bean2实例.
3、Web环境作用域
request作用域、session作用域、application作用域、websocket作用域(使用较少).....
request:每次浏览器刷新都会实例化一个新的bean;
session:浏览器刷新不会实例化新的bean,更换浏览器则会实例化新的bean;
application:bean实例化之后就不会改变,更换浏览器也不会。
SpringWeb上下文环境
在web.xml中定义:
不使用DispatcherServlet:
以RequestController为例:
在spring.xml中添加:
request:每次浏览器刷新都会实例化一个新的bean;
session:浏览器刷新不会实例化新的bean,更换浏览器则会实例化新的bean;
application:bean实例化之后就不会改变,更换浏览器也不会。
4、自定义作用域
Spring内置的自定义SimpleThreadScope作用域
以自定义双例模式为例:
步骤1:实现Scope接口
(org.springframework.beans.factory.config.Scope)主要关注实现的get方法和remove方法。
get()方法:按照name参数,按照我们自己定义的规则,去返回一个Bean,如果我们定义的规则里,Bean不存在,那么将通过objectFactory去创建一个Bean。
双例模式:一个bean的Id对应两个实例,实现双例模式,需要两个Map集合,Map map1=new HashMap();Map map2=new HashMap();。
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; //说明没拿到任何实例
}
步骤2: spring.xml配置
测试
@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提供的线程内单例作用域:
同一线程中Spring上下文会分配同一实例
不同线程(Thread)中,则获得不同实例。
srping配置:
测试代码:
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();
}
}
}
默认Scope(单例)Bean的懒加载
Spring容器会在创建容器时提前初始化Singleton作用域的bean,但是如果Bean被标注了lazy-init=“true”,则该Bean只有在其被需要的时候才会被初始化。(只对singleton作用域的bean有效)
如
多个bean使用懒加载:
表示spring配置文件里所有bean都是懒加载模式。
使用场景:
如果某个Bean在程序整个运行周期都可能不会被使用,那么可考虑设定该Bean为懒加载。
优点:尽可能的节省了资源。
缺点:可能会导致某个操作相应操作时间增加。
Bean初始化及销毁
以singleton作用域为例,假设有一个javaBean,该Bean的作用是连接数据库,该Bean被创建之后,执行数据库连接操作.
Bean初始化
如果需要在Bean实例化时执行一些逻辑,Spring提供了两种方法:
方法1:bean标签里的init-method属性,该值为Bean的某一方法,Spring实例化时,会调用Bean的该方法。
方法2:让Bean实现InitializingBean接口,Spring在实例化该Bean时,检测到Bean实现了该接口,就会调用该接口所定义的相应方法。
Bean销毁
如果需要在Bean销毁之前执行一些逻辑,有两种方法。
方法1:bean标签里的destory-method属性,该值为Bean的某一方法,Spring销毁该Bean时,会调用Bean的该方法。
方法2:让Bean实现DisposableBean接口,Spring在销毁该Bean时,检测到Bean实现了该接口,就会调用该接口所定义的相应方法。
案例1:
通过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配置:
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中定义:
default-destory-method="onDestory"/>
案例2:
实现相应接口,并实现相应方法
代码:
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属性继承
应用场景
场景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":设置标签只是定义性的,不会对它进行实例化操作。
parent属性:在Bean的子类bean标签添加,值为父类的Id。
场景1:
spring.xml配置:
场景2:
类1和类2不继承某一父类,只是存在相同属性。
spring.xml配置: