初学Spring框架

2023-11-04

Spring 是一个java 项目开发的框架技术

所谓框架可以看成一个项目的半成品,已经具备了一个项目项目的基本骨架部分,需要自己实现一些具体的内容

Spring官网

Spring技术的优势:
1、能够简化开发:

  • IoC
  • AOP
    • 事务处理

2、能够整合框架

  • Mybatis
  • MyBatis-plus
  • Struts
  • Struts2
  • Hibernate
  • ……

Spring家族
Spring发展到今天已经形成了一套非常完整丰富的框架,市场上很多项目都基于Spring框架完成
初学Spring应该重点关注一下几种技术

  • Spring Framework
  • Spring boot 提高开发速度
  • Spring Cloud 分布式开发相关技术

Spring Framework 简介

Spring Framework 是Spring生态圈其他项目的基础
Spring4.0 系统架构
在这里插入图片描述

编写项目的时候因为实现功能往往需要new对象,导致耦合度偏高(如DAO层原来的实现类有问题,现在重新编写了一个,在Service层实现时,需要调用DAO层对象,这时就需要new一个新的DAO实现类对象,这样代码就改变了,需要重新编译,重新部署)

  • 解决方案 : 使用对象是,在程序中不要主动使用new 产生对象 转换为由外部提供对象
  • 创建对象的控制权转换到外部,即控制反转 IoC (Inversion of Control)
  • Spring 提供了一个容器,称为IoC容器,当来当IoC思想的外部;现在由原来的:主动 new产生对象,变成由IoC提供对象
  • 现在由IoC提供对象的创建、初始化等一系列操作,被创建的或者被管理的对象 在IoC 容器中统称为 Bean
  • 此外,IoC中把Service层和DAO层的对象都进行了封装管理,但是要注意,Service层是要依赖DAO层的实现的,这里Spring在 IoC容器中实现了二者的依赖关系的绑定, 即 DI (Dependency Injection) 依赖注入

IoC 入门

  • IoC容器管理什么? bean对象也即Service层和DAO层的类对象
  • 如何将被管理的对象告知IoC容器? 配置文件
  • 如何获取IoC对象? 接口
  • IoC对象如何获取bean? 接口中的方法
  • 使用Spring获取那些坐标? pom.xml
实现步骤
  • 1、导入Spring坐标
    在pom文件中导入坐标,才能创建配置文件
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
  • 2、创建Spring配置文件 配置bean
 <?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">

  	    <!--1、配置pom文件导入Spring依赖-->
    	<!--2、配置xml文件中的bean-->
		<!--    bean 标签表示配置bean id 是名字   class 表示对应类名-->
    <bean id="bookDao"  class="com.taotao.dao.impl.BookDaoImpl"/>
    <bean id="bookService"  class="com.taotao.service.impl.BookServiceImpl"/>
</beans>
  • 3、初始化IoC容器获取容器并获取bean
public class APP2 {
    public static void main(String[] args) {
        // 获取 IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//        // 获取bean
//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//        bookDao.save();

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.SaveAll();
    }
}

DI 入门

  • 1、基于IoC管理bean
  • 2、Service中使用new形式创建的Dao对象是否保留?(调用service 方法时会自动new dao对象)(否)
  • 3、怎么获取对象Dao对象?(提供方法)
  • 4、Service 与Dao 之间的关系如何描述?(配置)
步骤
  • 1、删除业务层 Service层中 使用 new的方式创建的Dao对象
  • 2、提供set方法
public class BookServiceImpl implements BookService {


    // 1 删除原来new 出来的Dao对象
    private BookDao dao;
    @Override
    public void SaveAll() {
        // BookDao dao = new BookDaoImpl();
        dao.save();
        System.out.println("BookServiceImpl …… All");
    }
    //2  提供对象set方法 
    public void setBookDao(BookDao bookDao){
        this.dao = bookDao;
    }
}
  • 3、 配置 Service 层对象对 Dao层对象之间的依赖关系
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

		    <!--1、配置pom文件导入Spring依赖-->
		    <!--2、配置xml文件中的bean-->
		    <!--    bean 标签表示配置bean id 是名字   class 表示对应类名-->
    <bean id="bookDao"  class="com.taotao.dao.impl.BookDaoImpl"/>
    <bean id="bookService"  class="com.taotao.service.impl.BookServiceImpl">
    		<!--3、配种 Service 层对象与 Dao层对象之间的依赖关系-->
        <!--这里前者表示 配置哪一个具体的属性(Service 类中属性的名称) ,后者ref 是对应的 id名字-->
        <property name="bookDao" ref="bookDao"></property>
    </bean>


</beans>

4、 可以重新运行APP2程序,输出一样的结果

bean

bean的基础配置

  • bean 的别名 : name属性可以指定别名 别名用逗号分号或者空格分开 作用:在程序中既可以用id 引用也可以用 name引用
  • bean 的作用范围: Spring 默认给我们创建的bean对象是单例的
  • 如果想要非单例的 ,可以在xml 配置文件 将 该 bean属性 scope 指定为 prototype非单例 ; singleton :单例 (默认)
  • 为什么bean默认为单例?可以复用的对象不必创建过多否则占用资源。一般封装实体的对象才用非单例
  • bean 的实例化
    • spring bean对象的创建其实是用反射实现的,反射内部调用的是spring无参的构造方法

    • 上面的案例使用反射机制 使用构造方法实例化bean对象的;创建实例化对象还可以采用静态工厂的方式创建,步骤如下

      • 创建静态工厂接口以及实例化对象
      // 静态工厂
         public class OrderDaoFactory {
           public static OrderDao getOrderDao(){
               return new OrderDaoImpl();
            }
        }
        // Dao接口
       	public interface OrderDao {
       	    public void order();
       	}
       // Dao实现
       	public class OrderDaoImpl implements OrderDao {
       	
       	    @Override
       	    public void order() {
       	        System.out.println("order dao ...");
       	    }
       	}
      
      • 配置bean
      <bean id="orderDao" class="com.taotao.factory.OrderDaoFactory" factory-method="getOrderDao"/>
      

      class指向工厂类对象,factory-menthod 指向 获取对象的方法。

      • 调用IoC容器运行
      public class APPForInstanceOrder {
          public static void main(String[] args) {
      //        OrderDao orderDao = OrderDaoFactory.getOrderDao();
      //        orderDao.order();
              ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
              OrderDao dao = (OrderDao) ctx.getBean("orderDao");
              dao.order();
          }
      
      }
      
    • 实例工厂创建bean对象(工厂对象得到对象采用实例方法,不是静态方法,即去掉上述案例中public static OrderDao getOrderDao() 的 static) 这样要得到对象就必须创建工厂对象,然后再用工厂对象得到想要的对象

      • 配置 bean
        先创建工厂的bean 然后创建 Dao bean
      <bean id= "orderDaoFactory " class="com.taotao.factory.OrderDaoFactory "/>
      <bean id="oderDao" factory-method = "getOrderDao"  factory-bean="orderDaoFactory "/>
      
      • 改进(针对第三种方法 获取对象方法经常不固定的形式的缺陷)
      • 创建一个FactoryBean的类
      public class OrderDaoFactoryBean implements FactoryBean<OrderDao>{
      	public UserDao getObject() throws Exception{
      		return new OrderDaoImpl();
      	}
      	public Class<?> getObjectType(){
      		return UserDao.class;
      	}
      	public boolean isSingleton(){
      		return false;//如果是true或者默认不写这个函数,为单例
      	}
      }
      

      -如此可以简化伤处bean为

       <bean id="oderDao" class="com.taotao.factory.OrderDaoFactoryBean"/>
      

bean的生命周期

  • 在bean对象中写对应的初始化方法和销毁方法
  • 在配置文件中配置(bean标签中 有 init-method 和 destroy-menthod)
  • 发现 销毁方法没有执行
    • 原因: 程序运行在jvm中,程序执行完时,jvm退出时没有给销毁的机会
    • 解决方案:
      • 1、在虚拟机推出前将 IoC容器关闭。PS:ApplicationContext 不提供close方法,但是可以通过其实现类ClassPathXmlApplicationContext有这个方法,所以不用多态的形式创建IoC容器,直接用ClassPathXmlApplicationContext类指向它就好,就能调用它的close方法,这样就能执行destroy method了
      • 2、 设置关闭钩子,创建 IoC容器后(任意位置),注册关闭钩子 即添加一句 ctx.registerShutdownHook();这样就不用close()了,到时候虚拟机关闭时会自动关闭容器
  • 如果bean对象实现了InitializingBean,DisposableBean接口,在xml就不用再配置bean标签了
    这两个接口各自对应两个方法
    InitializingBean对应afterPropertionSet();
    DisposableBean对应destroy();
    其次运行时会发现,dao对象先初始化最后销毁,service在中间,dao方法的执行体在最中间执行。(解释,service依赖dao对象所以dao对象现被创建最后被销毁)此外还会发现set方法(依赖注入时需要在Service层加上Set方法)会优先于service的初始化方法执行
    如果在各个方法中增加了各自的打印方法,会发现这样的执行顺序:
    dao init...
    service->dao set...
    service init(afterPropertionSet)...
    dao function...
    service destroy...
    dao destroy...
    
  • bean对象完整的生命周期:
    • 初始化容器
      • 1、创建对象,分配内存
      • 2、创建构造方法
      • 3、执行属性注入(set操作)
      • 4、执行bean初始化方法
    • 使用bean
      1、执行业务操作
    • 关闭/销毁容器
      1、执行bean销毁方法

依赖注入

  • 依赖注入的方式

  • setter 注入

    • setter注入:引用类型(最开始学的就是引用雷勇的依赖注入)
      - 在bean中定义引用数据类型并提供对应的set方法
      - 在配置文件中 使用property标签 ref 属性注入引用对象,多个引用就创建多个property标签

    • setter注入:简单类型
      举例在 BookDaoImp中定义了基本类型的属性 ,并提供了setter方法

      public class BookDaoImpl implements BookDao{
      		private int connectionNum;
      		private String databaseName;
      		public void book(){
      			System.out.println("Dao book...")
      		}
      		public void setConnectionNum(int connectionNum){
      			this.connectionNum=connectionNum;
      		}
      		public void setDatabaseName(String databaseName){
      			this.databaseName=databaseName;
      		}
       }
      

      这样的话在配置文件中,配置bean如下

      <bean id ="bookDao" class="com.taotao.dao.impl.BookDaoImpl">
      		<property name="connectionNum"   value="10"/>
      		<property name="databaseName"   value="mysql"/>
       </bean>
      
  • 构造器注入(使用构造方法注入)

    • 引用类型
      • 在bean中定义引用数据类型并提供对应构造方法
      • 在配置文件 bean 中 围堵标签 中间添加<constructer>标签,但是要注意里面的name指的是形参的名称,多个bean就用多个<constructer>标签
        <bean id ="bookService" class="com.taotao.service.impl.BookServiceImpl">
           	<constructor-arg name=“bookDao” ref = “bookDao”>
        
      ```
    • 简单类型
      同上
      	<constructor-arg name=“connectionNum” value= “10”>
      
    • 缺陷: 名字必须和形参一致,耦合度高
      • 解决方案1,不写名字 写数据类型
      	<constructor-arg type="int" value= “10”>
      	<constructor-arg type="java.lang.String" value= “mysql”>
      
      • 解决方案2,不写名字 写参数位置
      	<constructor-arg index="0" value= “10”>
      	<constructor-arg index="1"  value= “mysql”>
      
  • 构造方式的选择:

    • 强制依赖使用构造器注入,避免出现null的结果
    • 可选依赖使用setter 注入
    • 自己开发推荐使用setter注入

依赖自动装配

  • 自动装配的方式
    • 按类型

      • 在bean中定义引用数据类型并提供对应setter方法
      • 配置文件中
      <bean id ="bookService" class="com.taotao.service.impl.BookServiceImpl" autowire=“byType”/>
      
      • 注意:1、必须提供setter方法 2、 必须配置实现类的 bean 3、 实现类按类型是唯一的(不能有两个该类型的实现类,如果有就选择按名称)
    • 按名称(不推荐)

        <bean id ="bookService" class="com.taotao.service.impl.BookServiceImpl" autowire=“byName”/>
      
    • 按构造方法(不推荐)

集合注入

  • 数组
  • property 标签的 name 对应 属性名字
  • 里面的 下一级标签代表类型 有 array(可以和list混用) set map props
<property name="array">
		<array>
				<value>100</value>
				<value>200</value>
				<value>300</value>
		</array>
</property>
  • 对于 array list set 用value标签
  • 对于 map 用
    <entry key="key1" value="value1"/> 
    
  • 对于 property 用
    		<prop key="key1">value1</prop>
    

举例 :引入第三方 数据源对象管理

以阿里的 druid 数据库连接池对象为例
在pom文件中引入依赖

    <dependency>
	      <groupId>com.alibaba</groupId>
	      <artifactId>druid</artifactId>
	      <version>1.1.16</version>
    </dependency>

在xml文件中管理相应的对象


    <bean id = "dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--        由于构造器方法参数不够 所以选择相应的 setter 方法进行注入-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/day17"/>
        <property name="username" value="root"/>
        <property name="password" value="1234"/>
    </bean>

在APP方法里面执行


public class APP3 {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource ds = (DataSource) ctx.getBean("dataSource");
        System.out.println(ds);
    }
}

通过导入配置文件(加载properties文件) 进行对象管理

步骤1:准备properties配置文件

resources下创建一个jdbc.properties文件,并添加对应的属性键值对

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root

步骤2:开启context命名空间
在applicationContext.xml中开context命名空间

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

在这里插入图片描述

在配置文件中使用context命名空间下的标签来加载properties配置文件

<context:property-placeholder location="jdbc.properties"/>

使用${key}来读取properties配置文件中的内容并完成属性注入

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

注解开发

注解开发定义bean

  • 1、将原来定义的bean标签删掉
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
  • 2、Dao上添加注解
@Component("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ..." );
    }
}

在这里插入图片描述

  • 3、配置Spring的注解包扫描
    为了让Spring框架能够扫描到写在类上的注解,需要在配置文件上进行包扫描 即<context:component-scan base-package="com.itheima"/>
    	<?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">
        <context:component-scan base-package="com.itheima"/>
    </beans>
    
  • BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象
  • @Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取,如
  • 对于@Component注解,还衍生出了其他三个注解@Controller@Service@Repository

纯注解开发模式

  • 1、创建配置类
    创建一个配置类SpringConfig
public class SpringConfig {
}
  • 2、标识该类为配置类
    在配置类上添加@Configuration注解,将其标识为一个配置类,替换applicationContext.xml
@Configuration
public class SpringConfig {
}
  • 3、用注解替换包扫描配置

在配置类上添加包扫描注解@ComponentScan替换<context:component-scan base-package=""/>

@Configuration
@ComponentScan("com.taotao")
public class SpringConfig {
}
  • 4、创建运行类并执行

创建一个新的运行类AppForAnnotation

public class AppForAnnotation {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

注解开发bean作用范围与生命周期管理

  • 作用范围
@Repository // 表示Dao层 的bean对象
//@Scope设置bean的作用范围
@Scope("prototype") 
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println("book dao save ...");
    }
}
  • 生命周期
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    @PostConstruct //在构造方法之后执行,替换 init-method
    public void init() {
        System.out.println("init ...");
    }
    @PreDestroy //在销毁方法之前执行,替换 destroy-method
    public void destroy() {
        System.out.println("destroy ...");
    }
}
  • 执行
	public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao1 = ctx.getBean(BookDao.class);
        BookDao bookDao2 = ctx.getBean(BookDao.class);
        System.out.println(bookDao1);
        System.out.println(bookDao2);
        ctx.close(); //关闭容器
    }
}

注解开发依赖注入

  • 1、在BookServiceImpl类的bookDao属性上添加@Autowired注解 即可
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    
//	  public void setBookDao(BookDao bookDao) {
//        this.bookDao = bookDao;
//    }
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}
  • @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉

  • 为什么setter方法可以删除呢?

    • 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
    • 普通反射只能获取public修饰的内容
    • 暴力反射除了获取public修饰的内容还可以获取private修改的内容
    • 所以此处无需提供setter方法
  • @Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入

    	@Repository("bookDao")
     	public class BookDaoImpl implements BookDao {
     	    public void save() {
     	        System.out.println("book dao save ..." );
     	    }
     	}
     	@Repository("bookDao2")
     	public class BookDaoImpl2 implements BookDao {
     	    public void save() {
     	        System.out.println("book dao save ...2" );
     	    }
     	}
    
    • 当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候该如何解决,就需要使用到@Qualifier来指定注入哪个名称的bean对象。
    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        @Qualifier("bookDao1")
        private BookDao bookDao;
        
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
    • @Qualifier注解后的值就是需要注入的bean的名称。
    简单数据类型注入
    • 使用@Value注解,将值写入注解的参数中就行了
    	@Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("itheima")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
注解读取properties配置文件

上述 value属性 会有一种感觉就是这个注解好像没什么用,跟直接赋值是一个效果,还没有直接赋值简单,所以这个注解存在的意义是什么?

  • @Value一般会被用在从properties配置文件中读取内容进行使用,具体如何实现?

  • 1、resource下准备properties文件
    jdbc.properties

    name=itheima888
    
  • 2、使用注解加载properties配置文件,
    在配置类上添加@PropertySource注解

    @Configuration
    @ComponentScan("com.itheima")
    @PropertySource("jdbc.properties")
    public class SpringConfig {
    }
    
  • 3、使用@Value读取配置文件中的内容

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("${name}")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    

IOC/DI注解开发管理第三方bean

前面定义bean的时候都是在自己开发的类上面写个注解就完成了,但如果是第三方的类,这些类都是在jar包中,我们没有办法在类上面添加注解,这个时候该怎么办?
遇到上述问题,我们就需要有一种更加灵活的方式来定义bean,这种方式不能在原始代码上面书写注解,一样能定义bean,这就用到了一个全新的注解==@Bean==。

在上述环境中完成对Druid数据源的管理,具体的实现步骤为:

  • 1、pom文件导入对应的jar包
	<dependency>
	    <groupId>com.alibaba</groupId>
	    <artifactId>druid</artifactId>
	    <version>1.1.16</version>
	</dependency>
  • 2、在配置类中添加一个方法,并在方法上添加@Bean注解
    @Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象
    	@Configuration
    	public class SpringConfig {
    		@Bean
    	    public DataSource dataSource(){
    	        DruidDataSource ds = new DruidDataSource();
    	        ds.setDriverClassName("com.mysql.jdbc.Driver");
    	        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
    	        ds.setUsername("root");
    	        ds.setPassword("root");
    	        return ds;
    	    }
    	}
    

注意:不能使用DataSource ds = new DruidDataSource()

因为DataSource接口中没有对应的setter方法来设置属性。

  • 从IOC容器中获取对象并打印
	public class App {
       public static void main(String[] args) {
           AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
           DataSource dataSource = ctx.getBean(DataSource.class);
           System.out.println(dataSource);
       }
   }

为了避免SpringConfig 配置类此类的外部配置项太多,一般还是要单独写一个配置类,对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下。

public class JdbcConfig {
	@Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

现在的问题是,这个配置类如何能被Spring配置类加载到,并创建DataSource对象在IOC容器中?

针对这个问题,有两个解决方案:

  • 方案一 :

    • :在JdbcConfig上添加配置注解@Configuration
    • 在Spring的配置类上添加包扫描(扫描刚才的JdbcConfig 配置类),即在Spring配置类增加注解@ComponentScan(“com.itheima.config”)
  • 方案二:

    • 使用@Import引入
    • 注意这种方式 Jdbc配置类 类上面没有 @configuration
    • 仅需在Spring配置类上增加@import注解
    	@Configuration
    	//@ComponentScan("com.itheima.config")
    	@Import({JdbcConfig.class})
    	public class SpringConfig {
    		
    	}
    

优化上述配置代码

public class JdbcConfig {
    @Value("com.mysql.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    @Value("root")
    private String userName;
    @Value("password")
    private String password;
	@Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
  • 此外如果是引用类型变量的依赖注入,可以在 函数中 使用形参导入
public class JdbcConfig {
    @Value("com.mysql.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    @Value("root")
    private String userName;
    @Value("password")
    private String password;
	@Bean
    public DataSource dataSource(BookDao bookDao){
    	System.out.println(bookDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

Spring 整合 MyBatis

Spring整合Mybatis

第一件事是:Spring要管理MyBatis中的SqlSessionFactory

第二件事是:Spring要管理Mapper接口的扫描

具体该如何实现,具体的步骤为:

  • 步骤1:项目中导入整合需要的jar包
<dependency>
    <!--Spring操作数据库需要该jar包-->
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <!--
		Spring与Mybatis整合的jar包
		这个jar包mybatis在前面,是Mybatis提供的
	-->
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>
  • 步骤2:创建Spring的主配置类
//配置类注解
@Configuration
//包扫描,主要扫描的是项目中的AccountServiceImpl类
@ComponentScan("com.itheima")
public class SpringConfig {
}

  • 步骤3:创建数据源的配置类

在配置类中完成数据源的创建

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
  • 步骤4:主配置类中读properties并引入数据源配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}

  • 步骤5:创建Mybatis配置类并配置SqlSessionFactory
public class MybatisConfig {
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //设置模型类的别名扫描
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        //设置数据源
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}

说明:

  • 使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pyto0AaL-1669382362503)(assets/1630138835057.png)]

  • SqlSessionFactoryBean是前面我们讲解FactoryBean的一个子类,在该类中将SqlSessionFactory的创建进行了封装,简化对象的创建,我们只需要将其需要的内容设置即可。

  • 方法中有一个参数为dataSource,当前Spring容器中已经创建了Druid数据源,类型刚好是DataSource类型,此时在初始化SqlSessionFactoryBean这个对象的时候,发现需要使用DataSource对象,而容器中刚好有这么一个对象,就自动加载了DruidDataSource对象。

  • 使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-avot1UTN-1669382257254)(assets/1630138916939.png)]

    • 这个MapperScannerConfigurer对象也是MyBatis提供的专用于整合的jar包中的类,用来处理原始配置文件中的mappers相关配置,加载数据层的Mapper接口类
    • MapperScannerConfigurer有一个核心属性basePackage,就是用来设置所扫描的包路径
  • 步骤6:主配置类中引入Mybatis配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  • 步骤7:编写运行类

在运行类中,从IOC容器中获取Service对象,调用方法获取结果

public class App2 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountService accountService = ctx.getBean(AccountService.class);

        Account ac = accountService.findById(1);
        System.out.println(ac);
    }
}

  • 步骤8:运行程序
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kH31owdY-1669383663408)(assets/1630139720273.png)]

支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:

  • SqlSessionFactoryBean
  • MapperScannerConfigurer

Spring整合Junit

整合Junit与整合Druid和MyBatis差异比较大,为什么呢?Junit是一个搞单元测试用的工具,它不是我们程序的主体,也不会参加最终程序的运行,从作用上来说就和之前的东西不一样,它不是做功能的,看做是一个辅助工具就可以了。

  • 环境准备

这块环境,大家可以直接使用Spring与Mybatis整合的环境即可。当然也可以重新创建一个,因为内容是一模一样,所以我们直接来看下项目结构即可:

在这里插入图片描述

  • 整合Junit步骤

在上述环境的基础上,我们来对Junit进行整合。

  • 步骤1:引入依赖

pom.xml

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
  • 步骤2:编写测试类

在test\java下创建一个AccountServiceTest,这个名字任意

//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件
public class AccountServiceTest {
    //支持自动装配注入bean
    @Autowired
    private AccountService accountService;
    @Test
    public void testFindById(){
        System.out.println(accountService.findById(1));

    }
    @Test
    public void testFindAll(){
        System.out.println(accountService.findAll());
    }
}

注意:

  • 单元测试,如果测试的是注解配置类,则使用@ContextConfiguration(classes = 配置类.class)
  • 单元测试,如果测试的是配置文件,则使用@ContextConfiguration(locations={配置文件名,...})
  • Junit运行后是基于Spring环境运行的,所以Spring提供了一个专用的类运行器,这个务必要设置,这个类运行器就在Spring的测试专用包中提供的,导入的坐标就是这个东西SpringJUnit4ClassRunner
  • 上面两个配置都是固定格式,当需要测试哪个bean时,使用自动装配加载对应的对象,下面的工作就和以前做Junit单元测试完全一样了
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

初学Spring框架 的相关文章

随机推荐

  • 渲染10w条数据怎么优化,虚拟列表技术上怎么实现,闪烁怎么解决

    当需要渲染大量数据时 可以采取以下优化措施 分批加载 将数据分批加载到页面上 而不是一次性加载所有数据 可以根据滚动位置或用户操作动态加载更多数据 以保持页面的响应性能 虚拟列表技术 虚拟列表技术是一种优化大量数据渲染的方法 它通过只渲染可
  • 存储过程

    好久没用过存储过程了 今天整理一下 一 定义 存储过程是一组为了完成特定功能的SQL语句的集合 它经编译后存储在数据库中 用户通过指定的调用方法执行之 存储过程具有名称 参数及返回值 并且可以嵌套调用 存储过程是经过编译的 以可执行格式永久
  • 【模型部署】Tensorrt学习记录

    tensorrt官方文档 https docs nvidia com deeplearning tensorrt archives tensorrt 723 一 简介 TensorRT是一种深度学习框架 是一种库 官方文档中的称呼是 Ten
  • Oracle查询表字段信息及注释

    查询字段信息 SELECT FROM all tab columns WHERE OWNER OWNER NAME AND TABLE NAME TABLE NAME 或者 SELECT FROM user tab columns WHER
  • 路由表的由来

    路由表的实现方法有三种 1 直连路由 2 静态路由 3 动态路由 直连路由 开启了路由器借口之后 路由表自动感知而来 当路由器吧两个接口的ip地址配置好并开启 就能从其路由表内看到这两个直连子网已经被记录 静态路由 由管理员手工配置的路由
  • webstorm中本地eslint的配置和使用,线上找不到eslint(2018版)

    前言 在webstorm中配置vue的eslint检查 注意 如果 webstrom 搜不到eslint插件 START 解决办法 1 安装 ESlint插件下载 https plugins jetbrains com p
  • MySQL中的事件

    MySQL中的事件 event 是用于执行定时或周期性的任务 类似Linux中的crontab 但是后者只能精确到分钟 事件可以精确到秒 既然MySQL自身能实现定时性任务 那么就不必在应用层实现了 事件由一个特定的线程来管理的 也就是所谓
  • Python 23.opencv 单对象模板匹配

    import cv2 import numpy as np from matplotlib import pyplot as plt img cv2 imread lane jpg 0 img2 img copy template cv2
  • STM32F030 多通道ADC DMA采集

    利用STM32F030C8T6的PA0和PA1引脚来进行两通道ADC采集 通过DMA方式 直接上代码 1 ADC的GPIO引脚配置 void ADC GPIO Config void GPIO InitTypeDef GPIO InitSt
  • 相机旋转 缩放效果 模型整体展示 部分展示

    using UnityEngine using System Collections using System Collections Generic using DG Tweening using UnityEngine EventSys
  • 修改Android镜像文件 ramdisk.img、system.img、userdata.img获取root权限

    首先 介绍一下这三个文件 ramdisk img 是你make android源代码后 生成的 out target product generic root目录下经过打包压缩而成的 system img 是你make android源代码
  • JavaWeb项目导致Linux服务器CPU过高的解决方案

    最近我部署在centos7上的一个项目出现了CPU达到了100 的状态 导致项目崩溃 起初没重视 直接重启项目解决 后面项目运行了几天服务器又报警100 了 这个时候我才意识到项目问题的严重性 然后开始查找原因 查看数据库连接池 发现数据库
  • 关于class.getResourceAsStream() 与class.getClassLoader().getResourceAsStream()区别

    首先用类加载资源文件的方式可以有以下三种 包结构图 1 InputStreaminStream DaoFactory class getResourceAsStream dao properties 2 inStream DaoFactor
  • ES6模块化及webpack配置

    前端使用的一个模块打包工具 https webpack js org webpack安装 安装 node npm 因为webpack是基于node开发的 通过 npm yarn 的方式来安装 webpack 安装方式 全局安装 npm in
  • Linux C++ 网络编程基础(2) : TCP多线程一个server对应多个client

    目录 一 linux posix线程相关函数介绍 二 tcp server基础版本 三 tpc服务端多线程版本 四 tpc客户端代码 tcp编程时 一个server可以对应多个client server端用多线程可以实现 linux下多线程
  • bash_profile和.bashrc的区别

    1 etc profile 此文件为系统的每个用户设置环境信息 当用户第一次登录时 该文件被执行 并从 etc profile d目录的配置文件中搜集shell的设置 2 etc bashrc 为每一个运行bash shell的用户执行此文
  • Java中关于thread的停止问题

    stop Deprecated public final void stop Throwable obj 已过时 该方法具有固有的不安全性 请参阅 stop 以获得详细信息 该方法的附加危险是它可用于生成目标线程未准备处理的异常 包括若没有
  • DES加密Delphi、C#互通(CBC加密模式)

    Delphi 目录 https blog csdn net dkbnull article details 87935698 unit Unit1 interface uses Windows Classes SysUtils Dialog
  • Docker实战-编写Dockerfile

    一 编译镜像 1 编译镜像 Dockerfile类似于Makfile 用户使用docker build就可以编译镜像 使用该命令可以设置编译镜像时使用的CPU数量 内存大小 文件路径等 语法 docker build OPTIONS PAT
  • 初学Spring框架

    Spring 是一个java 项目开发的框架技术 所谓框架可以看成一个项目的半成品 已经具备了一个项目项目的基本骨架部分 需要自己实现一些具体的内容 Spring官网 初学Spring框架 Spring Framework 简介 IoC 入