Spring —— IOC

发布时间 2023-06-18 10:22:39作者: 朱呀朱~

Spring — IOC

  • 传统方式:先前 service 层调用 dao 实现类:常用 new 方式,高耦合 ( 即依赖 —— 模块与模块之间的联系 )

    • 而好的程序应该是:高内聚 ( 模块内部功能的联系 ) 低耦合
  • New 的方式就是写死了,是硬编码 ( 一般来说应该是要避免的 ) 要改变的话就是改源代码

  • 将紧耦合变为松耦合,松耦合尽量做到不耦合

    • 采用反射方式解耦

    • 例:旧方法是需要在参数列表内传入一个 new 的对象,而采用反射思想的话就是直接传入一个字符串,而且字符串可以通过控制台输入

      // F1:
      Scanner sc = new Scanner(System.in);
      String str = sc.nextLine(); //读取,之后用 str.equals() 根据控制台情况来具体 new 等操作
      
      // F2:
      Class cls = Class.forName(str); // 填入字符串(类的全包名),用一个Class 类型来接收
      cls.newInstance(); // 完成创建:一个控制台输入包的 java 类对象
      
    • 输入字符串,创建了对象 ——> 反射

  • 反射是框架设计的灵魂,是在程序运行过程中:

    • 对任意一个类,都能够 [ 知道 ] 它的所有属性和方法

    • 对任意一个对象,都能够 [ 调用 ] 它的所有属性和方法

    • 这种动态获取信息以及动态创建对象的功能称为 Java 的反射机制

  • Spring 实现解耦的两项技术:

    1. 反射

    2. 读配置文件

  • Class 对象 ( 类模板,类的模板对象 ) 的获取方式:

    • 对象.getClass() 方法

    • 类.class属性

    • Class.forName(String className) —— 推荐方法

    • 注意:三个方式都是获取的同一个对象,只会执行一次构造方法,即一个类在虚拟机中只能有一个模板对象

    • 例:

      cls.newInstance("A包名");
      A a2 = (A)cls.newInstance(); // 找到模板后,就可以创建对象了
      
    • class 文件:字节码文件。加载到内存就会生成一个类模板对象,就可以在代码 new 的时候复制粘贴来用了

  • 反射缺点:效率要低,没直接创建来的快

  • 了解:spring 官网下载 spring 是一个压缩包,内 libs 里有 class 的 jar 包、解释 API 文档 javadoc、源码源文件 sources ( 三为一体 ),用得着的话就只拷贝 jar 包就好 —— 早期方法,现都用 maven 方法更便利

第一个 Spring 编码

  • 四个基础包 spring-core、beans、context、expression ( 版本要一致 ) 还有 commons-logging 日志基础包,依赖导入时会自动导入 ( Spring 框架依赖于此组件 Apache Commons Logging ),只导 context 也可 ( 其他四个都会自行导入 )

  • xml:

    <bean id = "不能重" class = "全包名...X" ></bean>  //还可能有 scope 属性表示作用范围,singleton 单例(默认),prototype 是多例,每次使用都会创建对象
    
  • Test.java:

    // psvm
    ApplicationContext ctx = new ClassPathXmlApplicationContext("1.xml"); // 创建一个大的容器类,ApplicationContext 是个接口,后为实现类,就会到配置文件中把 bean 创建出来
    X x = ctx.getBean("bean的id", 类模板—X.class) // 其实就很像A a2 = (A)cls.newInstance();
    // x.方法调用(X可以是一个类 / 实现类)
    

Bean对象的生命周期:

  • bean标签的 init-method 属性设置初始化方法
  • bean标签的 destroy-method 属性设置销毁方法
  • 单例对象的生命周期:容器加载 / 创建时对象便出生,直到容器销毁才死亡,即容器存在对象就一直活着
  • 多利对象的生命周期:什么时候用,就什么时候创建 ( 每次使用都会创建 ),对象在使用过程中就会一直活着,死亡交给java垃圾回收机制处理 ( 太多 spring 搞不定 )

IOC 底层原理

  • ioc:控制反转,把对象的创建调用交由 spring 管理,使用目的 —— 降低耦合度

  • ioc 过程:xml 中配 bean <......>,使用工厂类 ( 其实就是容器 ) ApplicationContext = ......,做反射... = ...getBean(...)

  • Spring 提供 IOC 容器实现的两种方式:

    • BeanFactory

      BeanFactory xx = new XmlBeanFactory(new ClassPathResource("1.xml")) // 工厂模式
      xx.getBean // 用的少,原始方式 
      
    • ApplicationContext —— 是子类,功能更强大,用的更多

      其接口有两个实现类

      • ClassPathXmlApplicationContext("Name") 用的多:Name 路径:就是 java 或 resource 下的文件名,如:1.xml
      • FileSystemXmlApplicationContext("文件路径"):文件路径: D:\idea\Workspace.....

Spring IOC 配置 Bean 的两种方式

  • 依赖注入 DI ( Dependency Injection ) 是 ioc 的具体体现

  • Bean 的依赖注入值类型:

    • 字面值
    • 其他 Bean
    • 集合类
  • 基于 xml 文件的方式 —— 集中式的元数据,与源代码无绑定 ( Bean 的 id 不指定的话就默认为类名 )

    • 新建xml的话除了bean等,开头部分还需要加xmlns工作空间,xsi里的 http://...... 约束两个,一共改三处
  • Bean 的依赖关系通常有两种实现方式:

    • 构造器注入

      • bean 子标签 constructor-arg 里,属性 name、value:只能是基本数据类型 + String ( 除开 String 的引用数据类型不能写入 ) —— 似表单
      • 其中 name:注入参数在构造函数中的名称。ref:其他 bean 的 id。value:注入的数据值。在 bean 标签里填写完值后,就可以在 Test 测试时 getBean 后直接输出一串对象值:内容就是 bean 里 value 所写
    • 属性 setter 注入

      • 属性注入 ( 推荐 ) 用的最多 —— 即把要用的对象当作属性来看,什么时候要什么时候就给你用

        属性注入:service 的 bean 子标签 property:name 为所取的属性名 ( != 变量 ),ref 为引用的 daoimpl 的 id

    • 注意:

      • 因为常如以下所用,所用就慢慢默认变量为属性了,实则不是

      img

      • name 前提是在 service 所需类里加上 daoimpl 的 getter and setter 方法 ( 方法是针对属性的,而属性就是 bean 里的子标签的 name ),service 里非 new 写的 UserDaoImpl dao 只是一变量,而 xml 里的 name 属性指的是 getXxx 和 setXxx 后面的首字母小写的 xxx ( 但在此处大写也可以 )
      • 若是 bean 的 dao 想更换,那就可以直接在 dao 层增加,虽是更改了源码,但实际上新增 dao 可以由别人写,自己只接受其 class 字节码文件即可 ( 跨平台特点 ),不需要编译,可以直接用 ———— bean更方便,解耦
  • 基于注解的方式 —— 分散式的元数据,与源代码紧绑定

    • 在四个 jar 包基础上再加上 aop 依赖包

    • 不配置 bean 还想创建出来,就得加一个扫描到包的 context 标签:

      <context:component-scan base-package="com.qut" ></context:component-scan>
      
    • 在 serviceimpl 开头加上 ( 即作用到类上 ) @Component(value = “xxxx”) ———— 作用就相当于在 spring 的配置 xml 中写一个 bean 标签

      • 没写 value 的话就是类名的首字母小写
    • @Controller ( 用控制器上 - 表现层 )、@Service ( 推荐到 Service 层 - 业务层 )、@Repository ( 推荐 Dao 层 - 持久层 ) —— 作用属性都相同,是 spring 为了提供更明确的语义

    • 注意:

      • @Autowired:主要用在只有唯一一个类型匹配,此时 public UserDao ud; 就可以删去 xml 方式时的 get 和 set ud 的方法了

      • @Qualifier 与 @Autowired 搭配使用:当一个 UserDao 有两个 Impl 实现类时,需要在 @Autowired 后再加上 @Qualifier(“xxxImpl”) 指定 service 层用哪个 Impl 实现类

      • @Resource 使用偏少

      • @Value 使用偏少

代码

//        通过控制台输入信息
        Scanner sc = new Scanner(System.in);
//        获取控制台输入字符
        String str = sc.nextLine();
//        Class可以看作一个模板,找到了str字符串对应模板
        Class cls1 = Class.forName(str); // ("com.qut.test.A")  -F1
//		  Class cls1 = a.getClass(); // A a = new A();  -F2
// 		  Class cls2 = A.class; // A a = new A();  -F3
// 		  完成创建
        cls1.newInstance();

//        配成一个大的容器ctx:含有1.xml内的信息
		ApplicationContext ctx = new ClassPathXmlApplicationContext("1.xml");
// 		  通过容器获取A实例 ——— 反射
		A a1 = ctx.getBean("mybean1",A.class);	 // 1.xml里一bean的id为:mybean1