Spring5学习笔记

SpringFramework5.x

  • Spring IOC工厂
  • Spring AOP编程
  • 持久层集成
  • 事务处理
  • MVC框架继承
  • 注解编程

2002年Rob Jhonson提出EJB存在的缺陷

EJB(Enterprise Java Bean)缺陷

  1. 运行环境苛刻(需要运行在EJB容器中)
  2. 代码移植性差

总结:EJB是重量级框架

什么是Spring

Spring是一个轻量级JavaEE解决方案,整合了众多优秀的设计模式

  • 轻量级

    • 对于运行环境没有额外要求

      ​ 开源 tomcat resion jetty

      ​ 收费 weblogic websphere

    • 代码移植性高

      ​ 不需要实现额外接口

  • JavaEE的解决方案

    面对每一层都有解决方案

Spring5学习笔记_第1张图片

  • 整合设计模式

    工厂、代理、模板、策略

设计模式

1.广义

面向对象设计中,解决特定问题的经典代码

2.狭义

GOF4人定义的23种设计模式

工厂设计模式

  1. 概念:通过工厂类创建对象

  2. 好处:解耦合

    耦合:代码间的强关联关系,一方的改变会影响另一方

    问题:不利于代码维护

    //简单案例:把接口的实现类硬编码在程序中
    UserServiceImpl userService = new UserServiceImpl();
    

设计简单工厂模式

工厂类

public class BeanFactory {
   

    public static UserService getUserService(){
   
        return new UserServiceImpl();
    }

}

测试类

import com.liu.pojo.BeanFactory;
import com.liu.pojo.User;
import com.liu.pojo.UserService;
import com.liu.pojo.UserServiceImpl;
import org.junit.Test;

public class TestSpring {
   
    
    @Test
    public void test01(){
   

		//UserServiceImpl userService = new UserServiceImpl();

        //通过工厂类创建对象,解耦合
        UserService userService = BeanFactory.getUserService();

        userService.login("liu","123456");

        User user = new User("liushihao","123456");
        userService.register(user);

    }
}

但是这种工厂类的设计过于简单,仍然引入了具体的UserServiceImpl,仍然存在耦合。

改进(反射+配置文件)

对象的创建方式:

  • 直接调用构造方法创建对象

    UserServiceImpl userService = new UserServiceImpl();

  • 通过反射的形式创建对象 解耦合

    Class clazz = Class.forName(“com.liu.pojo.UserServiceImpl”);

    UserService userService = (UserService)clazz.newInstance();

package com.liu.pojo;

public class BeanFactory {
   

    public static UserService getUserService(){
   

        UserService userService = null        ;
        try {
   
            //通过反射一种方式还是无法彻底解决耦合,还是引入了接口实现类的全限定类名
            Class clazz = Class.forName("com.liu.pojo.UserServiceImpl");
            userService =  (UserService)clazz.newInstance();
        } catch (ClassNotFoundException e) {
   
            e.printStackTrace();
        }
         catch (InstantiationException e) {
   
            e.printStackTrace();
        } catch (IllegalAccessException e) {
   
            e.printStackTrace();
        }

        return userService;
    }

}

通过添加小的配置文件解决Class.forName(“com.liu.pojo.UserServiceImpl”);中的耦合问题

配置文件

#Properties类型的集合来存储Properties文件的内容
#特殊的Map key=String value=String
#Properties [userService = com.liu.pojo.UserServiceImpl]
#Properties.getProperty("userService")就可以获取com.liu.pojo.UserServiceImpl

userService = com.liu.pojo.UserServiceImpl

工厂类

package com.liu.pojo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
   

    private static Properties env = new Properties();

    //IO是系统级资源,我们应该避免重复性地打开IO,最好在程序启动时一次性读取想要的内容
    static{
   
        //1.获得输入流
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");

        //2.文件的内容封装到Properties集合中 key="userService" value="com.liu.pojo.UserServiceImpl"
        try {
   
            
            env.load(inputStream);
            inputStream.close();
        
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

    public static UserService getUserService(){
   

        UserService userService = null        ;
        try {
   
            Class clazz = Class.forName(env.getProperty("userService"));
            userService =  (UserService)clazz.newInstance();
        } catch (ClassNotFoundException e) {
   
            e.printStackTrace();
        }
         catch (InstantiationException e) {
   
            e.printStackTrace();
        } catch (IllegalAccessException e) {
   
            e.printStackTrace();
        }

        return userService;
    }
}

日后业务需要改动,只需将新的实现类写入配置文件applicationContext.properties中

但是这样的设计还是存在问题,只要有新的实现类出现,就需要在工厂类中添加

public static XXXX getXXXX(){
   

        XXXX xxxx = null;
        try {
   
            Class clazz = Class.forName(env.getProperty("xxxx"));
            xxxx =  (XXXX)clazz.newInstance();
        } catch (ClassNotFoundException e) {
   
            e.printStackTrace();
        }
         catch (InstantiationException e) {
   
            e.printStackTrace();
        } catch (IllegalAccessException e) {
   
            e.printStackTrace();
        }

        return xxxx;
    }

这样的话会存在大量冗余代码

通用工厂设计

package com.liu.pojo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
   

    private static Properties env = new Properties();

    static{
   
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
   
            env.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

    //key代表小配置文件中的key [userDAO, userService]
    public static Object getBean(String key){
   

        Object ret = null;
        try {
   

            Class clazz = Class.forName(env.getProperty(key));
            ret =  clazz.newInstance();

        }catch (Exception e){
   

        }
        return ret;
    }

}

至此,需要创建对象时只需要调用工厂类的getBean()方法,传入配置文件中对应的字符串即可。

//getBean是静态方法,可以直接类名.方法名进行调用UserService userService = (UserService) BeanFactory.getBean("userService");

通用工厂的使用

  1. 定义类型(类)

  2. 通过配置文件的配置告知给工厂(applicationContext.properties)

    key = value

  3. 通过工厂获得类的对象

    Object ret = BeanFactory.getBean(“key”);

总结

Spring本质:工厂 ApplicationContext(applicationContext.xml)

​ 思路与上述工厂的设计一致,只不过Spring的工厂更为强大。

SpringHelloWorld

环境搭建

Spring的jar包(在pom.xml中设置依赖)


        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.4.RELEASEversion>
        dependency>

Spring的配置文件

  1. 配置文件的路径:任意位置 没有硬性要求

  2. 配置文件的命名:没有硬性要求 建议applicationContext.xml

    一般配置方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLfY6wNf-1620665224815)(D:\截图\image-20210321211433462.png)]

Spring的核心API

  • ApplicationContext

    1. 作用:Spring提供的ApplicationContext这个工厂用于对象的创建
    2. 好处:解耦合
    3. ApplicationContext是接口类型
    接口:屏蔽实现的差异两种实现:	非web环境:ClassPathXmlApplicationContext(main junit)	web环境:XmlWebApplicationContext
    
    1. 重量级资源
    ApplicationContext工厂的对象(ClassPathXmlApplicationContext和XmlWebApplicationContext)占用大量内存重量级资源特点:故一个应用只会创建一个工厂对象ApplicationContext工厂一定是线程安全的(多线程并发访问)
    

程序开发

  1. 创建类型

  2. 配置applicationContext.xml

    <bean id="person" class="com.liu.pojo.Person">bean>
    
  3. 通过工厂类获得对象 ClassPathXmlApplicationContext

    //1.获取Spring的工厂	ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");//2.通过工厂类获得对象	Person person = (Person) context.getBean("person");
    

细节分析

名词解释

Spring工厂创建的对象,叫做bean或者组件(component)

Spring工厂相关的方法

	//通过这种方式获得对象就不需要进行强制类型转换
	Person person = context.getBean("person", Person.class);	

	//当前配置文件中只能有一个
	Person person = context.getBean(Person.class);

	//获取当前配置文件中所有bean标签的id值
	String[] beanDefinitionNames = context.getBeanDefinitionNames();

	//获取配置文件中对应类型的id值
	String[] beanNamesForType = context.getBeanNamesForType(Person.class);

	//判断是否存在对应id的bean  返回值是boolean类型
	//containsBeanDefinition 只能根据id判断,无法判断name
	context.containsBeanDefinition("person");
	
	//containsBean id、name都可以判断
	context.containsBean("person");

配置文件中需要注意的细节

  1. 只配置class属性
<bean class="com.liu.pojo.Person">bean>

​ a)这种没有id值的配置 Spring会自动生成一个id com.liu.pojo.Person#0

​ b)应用场景:如果这个bean只需要使用一次,那么就可以省略id值

​ 如果这个bean需要使用多次或者被其他bean引用,则需要设置id值

  1. name属性

    作用:用于在Spring配置文件中,为bean对象定义别名(小名)

    与id相同的地方

    ​ 1)context.getBean(“id/name”) --> object

    ​ 2)

    与id不同的地方

    ​ 1)别名可以定义多个,id是唯一的

    ​ 2)以前XML的id属性的值有命名要求:以字母开头,后面跟字母、数字、下划线、连字符

    ​ 而name属性的值命名没有要求 如/person

    ​ name属性会应用在特殊命名场景下:/person(spring + struts1)

    但是XML发展到今天已经不存在id命名的限制,现在优先使用id

    ​ 3)工厂的方法中id和name

    	//containsBeanDefinition 只能根据id判断,无法判断name
    	context.containsBeanDefinition("person");
    	
    	//containsBean id、name都可以判断
    	context.containsBean("person");
    

Spring工厂的底层实现原理(简易)

Spring工厂是可以调用private修饰的构造方法创建对象的
Spring5学习笔记_第2张图片

思考

问题:Spring的工厂很强大,那么未来开发中是不是所有对象都交给Spring工厂来创建?

回答:理论上是的。但是有特例—实体对象(entity,会封装数据库表中的数据,这些数据会操作数据库),这类对象交给持久层框架来创建

Spring5.x与日志框架整合

作用

Spring与日志框架整合,日志框架就可以在控制台输出Spring运行过程中的一些重要信息(对象的创建、销毁、进行了什么操作等)。便于了解Spring的运行过程,利于程序的调试

如何整合

早期Spring1、2、3都是与commons-logging.jar进行整合

Spring5.x默认整合的日志框架是logback、log4j2

Spring整合log4j

​ 1.引入log4j相关jar包

​ pom.xml


<dependency>
  
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-log4j12artifactId>
    <version>1.7.25version>
dependency>



<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>

​ 2.引入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

注入(Injection)

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

为什么需要注入

通过编码的方式为成员变量赋值,存在耦合

@Test
    public void test05(){
   
        ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Person person = (Person) context.getBean("person");

        //原来赋值的方法,通过代码为成员变量赋值,存在耦合
        person.setId(1);
        person.setName("liushihao");

        System.out.println(person);

    }

如何进行注入

1.在类中为成员变量提供set、get方法

2.配置Spring配置文件

<bean id="person" name="p" class="com.liu.pojo.Person">
        <property name="id" value="10"/>
        <property name="name" value="david"/>
bean>

注入的好处

解耦合

注入的原理

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

<bean id="person" name="p" class="com.liu.pojo.Person">
  等效于
Account account = new Account();

<property name="id" value="10"/>
  等效于
account.setId(10);

<property name="name" value="david"/>
  等效于
account.setName("david");

Set注入详解

针对于不同类型的成员变量,在标签中需要嵌套不同标签

Set注入类型

JDK内置类型
  • 8种基本类型(byte short int long float double char boolean)+String

    在property标签中嵌套value

    <property name="id">
    	<value>10value>
    property>
    
    <property name="name">
    	<value>liushvalue>
    property>
    
  • 数组类型

    			<property name="emails">
                <list>
                    <value>[email protected]value>
                    <value>[email protected]value>
                    <value>[email protected]value>
                list>
    			property>
    
  • 集合类型

    
    <property name="tels">
          <set>
              <value>138123123value>
              <value>432123432value>
              <value>132543543value>
          set>
    property>
    
    
    <property name="addresses">
        <list>
            <value>centryparkvalue>
            <value>swimmingpoolvalue>
            <value>blocksvalue>
            <value>blocksvalue>
            <value>blocksvalue>
        list>
    property>
    
    
    <property name="qqs">
        <map>
            <entry>
                <key><value>liushvalue>key>
                <value>1234324value>
            entry>
        map>
    property>
    
    
    
    <property name="p">
        <props>
            <prop key="key1">value1prop>
            <prop key="key2">value2prop>
        props>
    property>
    
  • 复杂的JDK类型(Date)

    ​ 需要程序员自定义类型转换器处理。

用户自定义类型
第一种方式
  • 为成员变量提供set get方法

  • 配置文件中进行注入(赋值)

    
    <bean id="userService" class="com.liu.pojo.UserServiceImpl">
    	<property name="userDAO">
        <bean class="com.liu.pojo.UserDAOImpl"/>
    	property>
    bean>
    
第二种方式
  • 第一种方式存在的问题

    假如有多个类都有UserDAO作为成员变量,那么配置文件中
    <property name="userDAO">
        <bean class="com.liu.pojo.UserDAOImpl"/>
    property>
    
    这段代码会重复出现多次 冗余
    且会创建很多次userDAO对象 浪费内存(JVM)资源
    
  • 为成员变量提供set get方法

  • 在配置文件中先创建userDAO,然后在有UserDAO作为成员变量的类创建标签中,用ref标签引用这个userDAO对象

    <bean id="userDAO" class="com.liu.pojo.UserDAOImpl"/>
    
    <bean id="userService" class="com.liu.pojo.UserServiceImpl">
        <property name="userDAO">
            <ref bean="userDAO"/>
        property>
    bean>
    
    <bean id="orderService" class="com.liu.pojo.orderServiceImpl">
        <property name="userDAO">
            <ref bean="userDAO"/>
        property>
    bean>
    
    #Spring4.x 废除了 <ref local=""/> 这个标签和<ref bean=""/>基本等效
    

Set注入的简化写法

基于属性简化
JDK类型注入
<property name="name">
	<value>liushvalue>
property>
简化写法
<property name="name" value="liush"/>
注意:value属性只能简化8中基本类型+String的注入标签

用户自定义类型
<property name="userDAO">
	<ref bean="userDAO"/>
property>
简化写法
<property name="userDAO" ref="userDAO" />
基于p命名空间简化
p即property的缩写
JDK类型注入
<bean id="person" class="com.liu.pojo.Person">
  <property name="name">
    <value>liushvalue>
  property>
bean>
简化写法
<bean id="person" class="com.liu.pojo.Person" p:name="xiaodong" p:id="11" />

用户自定义类型
<bean id="person" class="com.liu.pojo.Person">
  <property name="userDAO">
    <ref bean="userDAO"/>
  property

你可能感兴趣的:(spring)