Spring笔记(1) – 工厂

时间:2020-10-28 作者:admin

Spring笔记(1) – 工厂

文章目录

前言

此笔记由孙哥的视频整理而来
作者: Wyt

孙哥B站视频链接如下:
孙哥说Spring5 全部更新完毕 完整笔记、代码看置顶评论链接~学不会Spring? 因为你没找对人

正文

1. 概述

1.1 EJB(Enterprise Java Bean)

存在的问题:

1. 运行环境苛刻
2. 代码移植性差

1.2 Spring是什么

  • 概念
Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IOC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
  • 优势

    1. 方便解耦,简化开发
    2. AOP编程的支持
    3. 声明式事物的支持
    4. 方便程序的测试
    
  • 轻量级

1. 对于运行环境是没有额外要求的
   开源 tomcat resion jetty 
   收费 weblogic  websphere 
2. 代码移植性高
   不需要实现额外接口
  • 分层,多模块

Spring笔记(1) - 工厂
Spring笔记(1) - 工厂

  • 整合设计模式
1. 工厂
2. 代理
3. 模板
4. 策略

1.3 设计模式

1. 广义概念
面向对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4人帮定义的23种设计模式:工厂、适配器、装饰器、门面、代理、模板...

2. Spring入门

2.1 知识铺垫

1. javase基础
2. mysql的基本操作
3. 了解jdbc
4. maven

2.2 环境支持

1. JDK
2. Maven
3. IDEA
Spring官方网站: www.spring.io

2.3 Maven中各类文件的介绍

Maven包名
	src
		main
			java: 用来存放项目主要的代码: .java
			resouce: 存放配置文件: .xml .propertise
		test: 存放测试类: .java
	target: 存放依赖: .jar
	pom.xml: 对象模型(Project Object Model)管理文件
	包名.iml: IDEA生成的对Module的配置信息
1. 一般在resouce文件夹新建applicationContext.xml
	用于管理bean对象,aop配置等
	(理论上可以任意命名,放在任意位置)
2. 使用时:
	ApplicationContext ctx = new 		
	ClassPathXmlApplicationContext("/applicationContext.xml");

2.4 重要概念

1. bean:
	Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。
	在applicationContext.xml中, 
	用<bean id="###" class="相对位置">, 引入某个类并赋值id(唯一), 其他`属性有如name(别名)等
2. 依赖管理: 
	2.1 Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为“依赖注入”的方式来管理Bean之间的依赖关系。
	2.2 使用依赖注入,不仅可以为Bean注入普通的属性值,还可以注入其他Bean的引用。依赖注入是一种优秀的解耦方式,其可以让Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起。

https://www.runoob.com/maven/maven-manage-dependencies.html

3. 反射

https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html

2.5 第一个Spring

1. 配置pom.xml
2. 创建java类对象
3. 配置applicationContext.xml
4. 添加测试代码并运行

详细步骤:

1. 配置pom.xml, 指定编码格式和JDK版本
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
        ">
        
	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.complier.source>15</maven.complier.source>
        <maven.complier.target>15</maven.complier.target>
    </properties>
2. 在pom.xml中引入相关依赖
	<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>
	
		<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    <dependencies>
3. 创建java类对象
	在main.java创建com.hello.HelloWorld.java
	添加方法
	public void hey() {
		System.out.println("HelloWorld");
	}
4. 在resource创建applicationContext.xml
	创建bean
	<bean id="helloworld" class="com.hello.helloworld"/>
5. 在test新建测试类Test01.java
	类中添加测试方法: 
	//import org.junit.Test
	@Test
	public void test01() {
		//获取工厂对象
		ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
		//根据xml中bean的id,获取bean对象(强制类型转换)
        HelloWorld hello = (HelloWorld) ctx.getBean("helloworld");
        //HelloWorld hello = ctx.getBean("helloworld", HelloWord.class);
        
        //调用HelloWorld的成员方法
        hello.hey();
	}

2.6 细节分析

  • Spring工厂的相关的方法
//通过这种方式获得对象,就不需要强制类型转换
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
        

//当前Spring的配置文件中 只能有一个<bean class是Person类型
Person person = ctx.getBean(Person.class);
System.out.println("person = " + person);
        

//获取的是 Spring工厂配置文件中所有bean标签的id值  person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
  System.out.println("beanDefinitionName = " + beanDefinitionName);
}
        

//根据类型获得Spring配置文件中对应的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
  System.out.println("id = " + id);
}
        

//用于判断是否存在指定id值得bean
if (ctx.containsBeanDefinition("a")) {
  System.out.println("true = " + true);
}else{
  System.out.println("false = " + false);
}
      

//用于判断是否存在指定id值得bean
if (ctx.containsBean("person")) {
  System.out.println("true = " + true);
}else{
  System.out.println("false = " + false);
}
  • 配置文件中的细节
1. 只配置class属性
<bean  class="com.baizhiedu.basic.Person"/>
a) 上述这种配置 有没有id值 com.baizhiedu.basic.Person#0
b) 应用场景: 如果这个bean只需要使用一次,那么就可以省略id值
            如果这个bean会使用多次,或者被其他bean引用则需要设置id值

2. name属性
作用:用于在Spring的配置文件中,为bean对象定义别名(小名)
相同:
   1. ctx.getBean("id|name")-->object
   2. <bean id="" class=""
      等效
      <bean name="" class=""
区别:
   1. 别名可以定义多个,但是id属性只能有一个值
   2. XML的id属性的值,命名要求:必须以字母开头,字母 数字 下划线 连字符 不能以特殊字符开头 /person
         name属性的值,命名没有要求 /person
      name属性会应用在特殊命名的场景下:/person (spring+struts1)
      
      XML发展到了今天:ID属性的限制,不存在 /person
   3. 代码
       //用于判断是否存在指定id值得bean,不能判断name值
       if (ctx.containsBeanDefinition("person")) {
           System.out.println("true = " + true);
       }else{
           System.out.println("false = " + false);
       }


       //用于判断是否存在指定id值得bean,也可以判断name值
       if (ctx.containsBean("p")) {
           System.out.println("true = " + true);
       }else{
           System.out.println("false = " + false);
       }

2.7 API介绍-ApplicationContext

* 接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
	ApplicationContext的实现类:
1. ClassPathXmlApplicationContext 
	它是从类的根路径下加载配置文件 推荐使用这种
2. FileSystemXmlApplicationContext 
	它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3. AnnotationConfigApplicationContext
	当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

2.8 思考

问题:未来在开发过程中,是不是所有的对象,都会交给Spring工厂来创建呢?
回答:理论上 是的,但是有特例 :实体对象(entity)是不会交给Spring创建,它是由持久层框架进行创建。

3. 设计模式之工厂模式

https://www.runoob.com/design-pattern/factory-pattern.html

3.1 简介

1. 概念:
		工厂(如applicationContext.xml)中事先引入bean对象,
		则我们可以通过工厂类,创建对象
        上文中通过new ClassPathXmlApplicationContext()获取工厂类
3. 好处:解耦合
   耦合:指定是代码间的强关联关系,一方的改变会影响到另一方
   问题:不利于代码维护
   简单:把接口的实现类,硬编码在程序中

3.2 通用工厂的使用方式

1. 定义类型 (类)
2. 通过配置文件的配置告知工厂(applicationContext.xml)
   key = value
3. 通过工厂获得类的对象
   Object ret = BeanFactory.getBean("key")

Spring笔记(1) - 工厂

4. Spring5.x与日志框架的整合

  • 简介
Spring与日志框架进行整合,日志框架就可以在控制台中,输出Spring框架运行过程中的一些重要的信息。
好处:便于了解Spring框架的运行过程,利于程序的调试
  • Spring如何整合日志框架
默认
  Spring1.2.3早期都是于commons-logging.jar
  Spring5.x默认整合的日志框架 logback log4j2

Spring5.x整合log4j 
  1. 引入log4j jar包
  2. 引入log4.properties配置文件
  1. pom.xml中的配置
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
  1. resources中log4j.properties的配置
# resources文件夹根目录下
### 配置根
log4j.rootLogger = debug,console

### 日志输出到控制台显示
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

5. 注入(Injection)

5.1 什么是注入

概念:通过Spring工厂及配置文件,为所创建对象的成员变量赋值

5.1.1 为什么要注入

原始方法:直接通过编码的方式,为对象的成员变量赋值,存在耦合

Spring笔记(1) - 工厂

5.1.2 如何进行注入

  • 类的成员变量提供set get方法
  • 配置spring的配置文件
 <bean id="person" class="com.hello.Person">
   <property name="id">
     <value>01</value>
   </property>
   <property name="name">
     <value>xiaoming</value>
   </property>
</bean>

5.1.3 注入的优势

解耦合

5.1.4 Spring注入的原理

Spring通过底层调用对象属性对应的set方法,完成成员变量的赋值,这种方式我们也称之为set注入

5.1.5 JDK内置类型转换

1. String
2. 数组
3. Set
4. List
5. Map
6. Properties(特殊)

6. 构造注入

6.1 相关概念

注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法 通过配置文件 为成员变量赋值
构造注入:Spring调用构造方法 通过配置文件 为成员变量赋值

6.2 开发步骤(以Cat类为例)

6.2.1 提供有参构造方法

public class Cat implements Serializable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

6.2.2 Spring的配置文件

<bean id="cat" class="com.hello.Cat">
  <constructor-arg>
    <value>Kity</value>
  </constructor-arg>
  <constructor-arg>
    <value>2</value>
  </constructor-arg>
</bean>

6.3 构造方法重载

6.3.1 参数个数不同时

通过控制<constructor-arg>标签的数量进行区分 

6.3.2 参数个数相同时

通过在标签引入 type属性 进行类型的区分 <constructor-arg type="">

6.4 set注入与构造注入的比较

1. 构造注入需要重载,操作麻烦
2. Spring框架底层, 大量应用了 set注入

总结:set注入应用更多

7. IOC与依赖注入(重点)

7.1 反转控制IOC (Inverse of Control)

1. 控制:对于成员变量赋值的控制权
2. 反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成 (注入)
3. 好处:解耦合, 
		即如需对成员变量赋值, 在配置文件操作, 而不是在代码中操作
		减少了直接对代码的操作
4. 底层实现:工厂设计模式

7.2 依赖注入 (Dependency Injection DI)

1. 依赖注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值
2. 依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一			个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)。
3. 好处:解耦合
  • 历史背景
	Rod Johnson是第一个高度重视以配置文件来管理Java实例的协作关系的人,他给这种方式起了一个名字:控制反转(Inverse of Control,IoC)。后来Martine Fowler为这种方式起了另一个名称:依赖注入(Dependency Injection),因此不管是依赖注入,还是控制反转,其含义完全相同。当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有两种做法:

1. 原始做法: 调用者主动创建被依赖对象,然后再调用被依赖对象的方法;

2. 简单工厂模式: 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。

	注意上面的主动二字,这必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取,变成了被动接受——所以Rod Johnson称之为控制反转。

	另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入。

8. Spring工厂创建复杂对象

8.1 什么是复杂对象

	在java中,不能通过new关键字创建的对象,都称为复杂对象,如抽象类(abstract,例如Calendar日期类)、接口(interface,JDBC中的Connection连接类)。

8.2 Spring工厂创建复杂对象的三种方式

8.2.1 FactoryBean接口

  • 开发步骤
    1. 实现FactoryBean接口

Spring笔记(1) - 工厂

	2. Spring配置文件的配置
# 如果Class中指定的类型 是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象  Connection
<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
  • 细节

    - 如果就想获得FactoryBean类型的对象   ctx.getBean("&conn")
      获得就是ConnectionFactoryBean对象
    
    - isSingleton方法
      返回  true 只会创建一个复杂对象
    
      返回 false 每一次都会创建新的对象
      问题:根据这个对象的特点 ,决定是返回true (SqlSessionFactory) 还是 false  (Connection)
    
    - mysql高版本连接创建时,需要制定SSL证书,解决问题的方式
      url = "jdbc:mysql://localhost:3306/suns?useSSL=false"
      
    - 依赖注入的体会(DI)
      把ConnectionFactoryBean中依赖的4个字符串信息 ,进行配置文件的注入 
    	好处:解耦合
    <bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/>
      <property name="username" value="root"/>
      <property name="password" value="123456"/>
    </bean>
    
    • FactoryBean总结
    - Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,后续讲解Spring整合其他框架,大量应用FactoryBean
    

8.2.2 实例工厂

  • 对象创建时期
* 工厂在构造方法初始化时,会将类进行实例化放在工厂中
  • 优势
1. 避免Spring框架的侵入 
2. 整合遗留系统 
  • 开发步骤
 <bean id="connFactory" class="com.hello.factorybean.ConnectionFactory"></bean>

 <bean id="conn"  factory-bean="connFactory" factory-method="getConnection"/>

8.2.3 静态工厂

  • 对象创建时期
* 工厂初始化之前,工厂中的类已经被实例化放在工厂容器中
  • 开发步骤 (看不懂的先跳过)
public class StaticConnectionFactory {
    public static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名称?useSSL=false", "root", "123456");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return conn;
    }
}

<bean id="conn" class="com.hello.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
  • 拓展: https://www.cnblogs.com/dyj-blog/p/8867028.html

8.3 Spring工厂创建对象的总结

Spring笔记(1) - 工厂

9. 控制Spring工厂创建对象的次数

9.1 如何控制简单对象的创建次数

  • Bean属性-scope的使用:
1. 功能:控制简单对象的创建次数
2. 常用选项:
	sigleton:只会创建一次简单对象 默认值
	prototype:每一次都会创建新的对象
3. 使用: 在.xml配置文件中对bean对象操作
	<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>

9.2 如何控制复杂对象的创建次数

* 在对象中添加成员方法

FactoryBean{
   isSingleton(){
      return true  只会创建一次
      return false 每一次都会创建新的
   }

}
* 如没有isSingleton方法 还是通过scope属性 进行对象创建次数的控制

9.3 为什么要控制对象的创建次数

* 好处:节省不别要的内存浪费 
  • 什么样的对象只创建一次?

    1. SqlSessionFactory
    2. DAO
    3. Service
    
  • 什么样的对象 每一次都要创建新的?

    1. Connection
    2. SqlSession | Session
    3. Struts2 Action
    

10. Spring配置和相关API

10.1 applicationContext.xml

  • 在类路径下(resources)创建applicationContext.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"             		   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans      	             http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

10.2 ApplicationContext

1. ApplicationContext的继承体系
	applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
	
2. ApplicationContext的实现类

	1)ClassPathXmlApplicationContext 
	它是从类的根路径下加载配置文件 推荐使用这种

	2)FileSystemXmlApplicationContext 
	它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

	3)AnnotationConfigApplicationContext
	当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

10.3 配置文件参数化

把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中

1. Spring的配置文件中存在需要经常修改的字符串?
   存在 以数据库连接相关的参数 代表
2. 经常变化字符串,在Spring的配置文件中,直接修改
   不利于项目维护(修改)
3. 转移到一个小的配置文件(.properties)
   利于维护(修改)
   
配置文件参数化:利于Spring配置文件的维护(修改)
1. 配置文件参数的开发步骤
  • 提供一个小的配置文件(.properities)
名字:随便 (jdbc.properties)
放置位置:随便 (resources文件夹下)
以键值对方式书写: key=value

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/suns?useSSL=false
jdbc.username = root
jdbc.password = 123456
  • Spring的配置文件(xml)与小配置文件(propertise)进行整合
1. <beans>属性中加入
	xmlns:context="http://www.springframework.org/schema/context"
	
2. <beans>属性选项xsi:schemaLocation中加入
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

3. 加载jdbc.properties
    <context:property-placeholder location="classpath:/jdbc.properties"/>
    
4. 配置数据源对象
    <bean id="conn" class="com.wyt.factorybean.ConnectionFactoryBean">
    	<propertise name="key1" value="value1"/>
    	<propertise name="key2" value="value2"/>
    	...
    </bean>
  • 在Spring配置文件中通过${key}获取小配置文件中对应的值

Spring笔记(1) - 工厂

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。