欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring架构开荒

程序员文章站 2022-07-02 19:22:13
...

IOC 控制反转

Inversion of Control
控制反转 依赖注入
1 控制什么?

控制对象的创建以及销毁

2 反转什么?

将对象的控制权交给IOC容器

举例说明:

张三要回家
对象:

  • 小明 人
  • 车 奔驰 宝马
    Spring架构开荒
    Audi.java
public class Audi{
	
	public void start(){
		System.out.println("Audi.start");
	}

	public void turnLeft(){
		System.out.println("Audi.start");
	}
	public void trunRight(){
		System.out.println("Audi.start");
	}
	public void stop(){
		System.out.println("Audi.start");
	}
}

传统方式

张三控制车辆的流程:
1 创建一辆车

Audi audi = new Audi();

2 控制车辆的启动 直行 左右转

audi.start();
audi.turnLeft();
audi.trunRight();
turn.stop();

3 销毁车辆

java的jcc组织垃圾回收处理掉

张三的其他需求

比如看电影

public void goMovice(){
		public void start(){
		System.out.println("Audi.start");
	}
	public void stop(){
		System.out.println("Audi.start");
	}
}

现在张三要换一辆Buick车

在Zhangsan.java当就需要全部更改
产生了高耦合性

改进方案 1

把车升级到张三的属性域里面
这样就降低了耦合度

public class ZhangSan {

    Buick car = new Buick();

    public void goHome(){
        car.start();
        car.turnRight();
        car.turnLeft();
        car.stop();
    }
}

改进方案 2

接口Car

public interface Car {

    public void start();
    public void turnLeft();
    public void turnRight();
    public void stop();
}

奔驰车实现这个车的接口

public class Audi implements Car{

    public void start(){
        System.out.println("Audi.start");
    }

在ZhangSan当中用构造方法传进去Car

   private Car car;
    public ZhangSan(Car car){
        this.car = car;
    }
   

到现在Car已经不用ZhangSan来创建了 由IOC来创建了

车辆的控制权完全交给了IOC

实现自己编写一个IOC的容器

Spring架构开荒

约定

  • 所有的Bean的生命周期交由IOC容器管理
  • 所有被依赖的Bean通过构造方法执行注入
  • 被依赖的Bean需要优先创建(创建张三前先创建奥迪)

所有被依赖的Bean通过构造方法执行注入

    public HumanWithCar(Car car) {
        this.car = car;
    }

    private Car car;

Class003Test.java//测试IOC容器

public class Class003Test {

    private IoCConteainer ioCConteainer = new IoCConteainer();

    @Before//创建bean
    public void before(){
        ioCConteainer.setBean(Audi.class, "Audi");
        ioCConteainer.setBean(Buick.class, "Buick");
        ioCConteainer.setBean(ZhangSan.class, "ZhangSan","Audi");
        ioCConteainer.setBean(Lisi.class, "Lisi","Buick");
    }

    @Test//使用bean
    public void test(){
        Human ZhangSan = (Human) ioCConteainer.getBean("ZhangSan");
        ZhangSan.goHome();
        Human Lisi = (Human) ioCConteainer.getBean("Lisi");
        Lisi.goHome();
    }
}

Spring IOC入门

1 创建一个javabean

public class Bean{
	public Bean(){
		System.out.println("Bean.bean");
	}
}

2 在pom中加入spring的依赖

spring-core
spring-context

3 在resources文件夹下建立一个spring.xml文件

//class = 1 中创建的Bean 并给一个唯一标示id
<bena class = "bean" class="com.imooc.spring.ioc.class004.Bean">

//测试


public class Class004Test {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml);
        Bean bean = context.getBean("bean",Bean.class);
        System.out.println("bean = " + bean);
    }
}

Spring架构开荒

实例化Bean方式

  • 构造方法实例化
  • 静态方法
  • 实例方法

构造方法实例化

1 创建Bean.java

public class Bean {
    public Bean(){
        System.out.println("Bean.bean");
    }
}

2 在spring.xml中配置bean

<bean class = "com.spring.ioc.class05.Bean1" id = "bean1"/>

3 在class005Test.java中实例


public class class005Test {
    @Test
    public void test(){
        //取上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        //通过context get一个Bean
        Bean bean = context.getBean("bean", Bean.class);
        System.out.println("bean = " + bean);
    }
}

静态方法实例化

1 创建一个BeanFactory.java
通过BeanFactory的getBean方法实例化Bean


public class Bean2Factory {
    public static Bean2 getBean2(){
        return new Bean2();
    }
}

2 spring.xml中进行配置

<bean class = "com.spring.ioc.Class05.Bean2Factroy" 
	factory-method = "getBean2" id = "bean2"/>

3 Class005Test.java

Bean2 bean2 = context.getBean("bena2", Bean2.class);

实例方法实例化Bean

1 创建一个Bean3Factory.java
通过BeanFactory的getBean方法实例化Bean


public class Bean3Factory {
    public static Bean3 getBean3(){
        return new Bean3();
    }
}

2 spring.xml中进行配置

    <bean class = "com.spring.ioc.Class05.Bean3" factory-bean = "bean3Factory"
                factory-method = "getBean3" id = "bean3"/>

3 Class005Test.java

        Bean3 bean3 = context.getBean("bean3",Bean3.class);
        System.out.println("bean3 = " + bean3);

给Bean取一个别名

1 name属性

<bean class = "myspring.Bean" name  ="bean1_1,bean1_2 " id = "bean1"/>

2 alias属性

    <alias name = "bean1" alias="bean1_3,bean1_4"/>

测试

Bean1 bean1_1 = context.getBean("bean1_1",Bean1.class);
System.out.println("bean1_1 = " + bean1_1);
Bean1 bean1_2 = context.getBean("bean1_2",Bean1.class);
System.out.println("bean1_2 = " + bean1_2);

但是这些名字不同的都是同一个Bean
Spring架构开荒

Spring架构开荒

注入Bean的方式

  • 构造方法注入Bean
  • set方法注入Bean
  • 集合类Bean的型注入
    • List
    • Set
    • Map
    • Propertise
  • null值注入
  • 注入时创建内部Bean

前期准备
AnotherBean.java

public class AnotherBean {
    
}

Bena.java


public class Bean {

    private AnotherBean anotherBean;
    private String string;



    public AnotherBean getAnotherBean() {
        return anotherBean;
    }

    public void setAnotherBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }
}

构造方法注入Bean

在Bean.java中写入构造方法

    public Bean(AnotherBean anotherBean, String string) {
        this.anotherBean = anotherBean;
        this.string = string;
    }

spring.xml

//index:参数是构造方法中的第几个参数
//name:当前参数的参数名字
//type:参数的类型
//value: 给参数赋值 适合简单的数据类型 string integer  double float
//ref:复杂数据类型赋值	对应anotherbean的id
    <bean class = "com.example.controller.Bean" id = "bean">
        <constructor-arg index="0" name = "anotherBean"
                         type="com.example.controller.AnotherBean"
                         ref="anotherBean"/>
        <constructor-arg index="1" name="string"
                         type="java.lang.String"
                         value="aaaaa"/>
    </bean>

Spring架构开荒
简单写法

    <bean class="com.example.controller.AnotherBean" id = "bean"
        c:anotherBean-ref="bean1" c:string="ccccc"
          p:anotherBean1-ref="bean1_1" P:string1="ddddd"
    />

    <util:property name="anotherBean1" ref = "anotherBean"/>
    <util:property name = "String1" value="ddddd"/>

Spring架构开荒

set方法注入Bean

Spring架构开荒

集合类型的Bean的注入

  • list
  • set
  • map
  • propertise

List
spring.xml

    <util:property name = "stringList">
        <list>
            <value>aaaaa</value>
            <value>ddddd</value>
        </list>
    </util:property>
    <util:property>
        <list>
            <ref bean = "anotherBean"/>
            <ref bean = "anotherBean"/>
        </list>
    </util:property>
    <util:propertiy name="properties">
        <prop key="aaaaa">bbbbb</prop>
    </util:propertiy>

class005Test.java

        Bean bean = context.getBean("bean",Bean.class);
        System.out.println("bean.getStringList() = " + bean.getSring());
        System.out.println("bean.getProperties() = " + bean.getProperties());

Spring架构开荒
Spring架构开荒

null注入

Bean.java

    public AnotherBean getAnotherBean2() {
        return anotherBean2;
    }

    public void setAnotherBean2(AnotherBean anotherBean2) {
        this.anotherBean2 = anotherBean2;
    }

    private AnotherBean anotherBean2;

spring.xml

    <util:properties name = "anotherBean2">
        <null/>
    </util:properties>

class005Test.java

System.out.println("bean.getAnotherBean2()" + bean.getAnotherBean2());

Spring架构开荒

注入的时候创建内部Bean

    <util:properties>
        <bean class = "com.example.controller.AnotherBean"/>
    </util:properties>

Spring架构开荒

Bean的作用域的问题

  • Sinleton作用域

  • prototype作用域

  • Web环境作用域

    • requset
    • session
    • application
    • websocket
  • 自定义作用域

    • SimpleThreadScope作用域

Singleton作用域(单例模式)

Spring架构开荒
右侧的Bean会通过Spring注入左侧三个Bean中 只会创建一个Bean

Bean.java

public class Bean {

    public Beanb getBeanb() {
        return beanb;
    }

    public void setBeanb(Beanb beanb) {
        this.beanb = beanb;
    }

    private Beanb beanb;

    @Override
    public String toString() {
        return "Bean{" +
                "beanb=" + beanb +
                '}';
    }
}

spring.xml

    <bean class = "com.example.controller.People.Beanb" id = "bean2"
        scope="prototype"/>

    <bean class = "com.example.controller.People.Bean" id = "bean"
    scope="prototype">
        <property name="beanb" ref="bean2"></property>
    </bean>

public class Class007Test {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    Beanb bean2_1 = context.getBean("Beanb",Beanb.class);
    System.out.println("bean2_1 = " + bean2_1);
    Beanb bean2_2 = context.getBean("bean2_2 = " + bean2_2);
    System.out.println("bean2_2 = " bean2_2);
    bean bean1 = context.getBean("bean",Bean.class);
    System.out.println("bean = " + bean);
}

Spring架构开荒
Spring架构开荒

Prototype作用域(多例模式)

Spring架构开荒

Spring架构开荒

Web相关作用域

  • requset作用域
  • sessiong作用域
  • applicatiton作用域
  • websocker作用域(很少使用)

SpringWeb上下文环境

如果使用DispatcherServlet 不需要增加其他配置

Spring架构开荒

web.xml
Spring架构开荒
RequsetController.java
Spring架构开荒
spring.xml

<bean class="com.spring.ioc.class08.RequestController"/>
<bean class="com.spring.ioc.class08.SessionController"/>
<bean class="com.spring.ioc.class08.ApplicationController"/>

Spring架构开荒
每次刷新@contraller不变

<bean class="com.spring.ioc.class08.RequestController" scope = "requset"/>
<bean class="com.spring.ioc.class08.SessionController" scope = "requset"/>
<bean class="com.spring.ioc.class08.ApplicationController" scope = "requset"/>

每次刷新@contraller改变
回顾

  • request:每个requset请求斗湖创建一个单独的实例
  • session:每个session都会创建一个单独的实例
  • application:每个servletContext都会创建一个单独的实例
  • websocket:每个websocket链接都会创建一个单独实例

自定义作用域

  • SimpleThreadScope作用域

MyScope.java
Spring架构开荒
spring.xml


Bean的懒加载

Spring容器会在创建容器时提前初始化Singleton作用域的bean
但如果Bean被标记了lazy-init = “true”
那么该Bean只有在其被需要的时候才会初始化

只对singleton作用域的bean有效
多例模式不知道bean什么时候被实例化

单一的Bean需要懒加载

    <bean class = "com.example.controller.People.Beanb" id = "bean2"
        scope="singleton" lazy-init="true"/>

所有的bean需要懒加载

<beans default-lazy-init="true">

Spring架构开荒
Spring架构开荒
Spring架构开荒
位所有的bean都设置默认的
Spring架构开荒

Bean初始化及销毁逻辑处理

如果Bean实例化之后执行一些逻辑 有两种方法

  • bean标签里的属性 initmethod
  • 让Bean实现InitializingBean接口

如果Bean在销毁之前需要执行一些逻辑 两种方法

  • destory-method
  • DisposableBean接口

Bean属性继承

场景1
Spring架构开荒
简化代码
Spring架构开荒
场景2
Spring架构开荒
Spring架构开荒

注解

spring2.5之后用注解取代xml

xml 与 annotation对比

Spring架构开荒
MyConfiguration.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration {

    @Bean //这里也可以用(value=“bean1命名”)
    public Bean bean1(){
        return new Bean();
    }
}

Test.java

import com.example.controller.People.MyConfiguration;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Class008Test {
    
    @Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        Bean1 bean1 = context.getBean("bean",Bean.class);
        System.out.println("bean1 = " + bean1);
    }
}

Spring架构开荒

简化代码

conpoent-scan包扫描
Spring架构开荒
@ component注解
Spring架构开荒

给Bean取别名

Spring架构开荒

通过注解注入Bean

Spring架构开荒
构造方法和Set方法注入Bean
Spring架构开荒
通过属性直接注入
Spring架构开荒
实例化和注入时指定Bean的id
Spring架构开荒
List/Set注入
直接注入List实例
Spring架构开荒
将多个泛型实例注入到List
Spring架构开荒
Map注入
Spring架构开荒
多个泛型实例注入Map
Spring架构开荒
String Integer类型的直接赋值
Spring架构开荒
Spring Ioc容器内置借口实例注入
Spring架构开荒
可以直接将ApplicationContext注入进来,也可以注入BeanFactory、Environment、ResourceLoader、ApplicationEventPublisher\MessagerSource及其实现类

SpringIoc注解设定Scope(作用域)

Spring预定义作用域
Spring架构开荒
自定义作用域
Spring架构开荒
Spring架构开荒
Spring架构开荒
方法注入
Spring架构开荒
Spring架构开荒

通过注解开启Bean的懒加载

Spring架构开荒

通过注解编写Bean初始化及销毁

Spring架构开荒
Spring架构开荒
Spring架构开荒
Spring架构开荒