Spring
Spring简介
优点:
简化开发
框架整合
发展史
Spring Framework系统架构图
目前问题
- 代码书写现状
- 耦合度偏高
- 解决方案
- 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
核心概念
-
IOC(Inversion of Control)控制反转 控制权的反转 创建对象的控制权由程序员变为了spring
使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。通俗的讲就是“将new对象的权利交给Spring,我们从Spring中获取对象使用即可” spring提前把new好的对象存放在一个容器中,这个容器就是核心容器
-
Spring技术对IoC思想进行了实现
-
Spring提供了一个容器,称为IOC容器,用来充当IoC思想中的“外部”
-
IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
-
DI(Dependency Injection)依赖注入
-
类要依赖他的属性,所以我们说类的依赖就是他的属性
-
spring在创建对象时给对象的属性赋值 就称为依赖注入
-
spring ioc 容负责创建对象 我们称之为控制反转 创建对象的同时给属性赋值 我们称之为依赖注入
-
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
我们从spring ioc容器中要了一个数据访问访问层对象赋给了 bookDao这个变量 ,这个过程就叫依赖注入
-
-
- 目标:充分解耦
- 使用IoC容器管理bean(IOC)
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终效果
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
Spring使用
IOC实现步骤
【第一步】导入Spring坐标
【第二步】定义Spring管理的类(接口与实现类)
【第三步】创建Spring配置文件,配置对应类作为Spring管理的bean对象
【第四步】初始化IOC容器(Spring核心容器/Spring容器),通过容器获取bean对象
【第一步】导入Spring坐标
<dependencies>
<!--spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<!--junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
【第二步】定义Spring管理的类(接口)
BookDao接口和BookDaoImpl实现类
package com.tyhxzy.dao;
/**
* 书籍的DAO接口
*/
public interface BookDao {
/**
添加书籍
*/
void save();
}
package com.tyhxzy.dao.impl;
import com.tyhxzy.dao.BookDao;
/**
* 书籍DAO的实现类
*/
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("DAO:添加书籍到数据库");
}
}
BookService接口和BookServiceImpl实现类
package com.tyhxzy.service;
public interface BookService {
/**
添加书籍
*/
void save();
}
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
import com.tyhxzy.service.BookService;
public class BookServiceImpl implements BookService {
//创建成员对象
private BookDao bookDao = new BookDaoImpl();
//实现业务方法
@Override
public void save() {
System.out.println("业务层:调用添加书籍的方法");
bookDao.save();
}
}
【第三步】创建Spring配置文件,配置对应类作为Spring管理的bean对象
- 定义applicationContext.xml配置文件并配置BookServiceImpl
- 注:如果顶部出现提示信息,选择Disable inspection
<?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">
<!--
bean标签:
bean标签的作用: 创建制定类的对象并且存储到spring的容器中
class: 指定你创建的对象所属的类的类全名
id: 该对象在容器中的唯一标识
-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl"/>
</beans>
注意事项:bean定义时id属性在同一个上下文中(IOC容器中)不能重复
【第四步】初始化IOC容器(Spring核心容器/Spring容器),通过容器获取Bean对象
package com.tyhxzy.test;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookService bookService = context.getBean("bookService", BookService.class);
bookService.save();
//3。关闭容器(以后不需要关闭的)
context.close();
}
}
运行结果
DI实现步骤
【第一步】删除使用new的形式创建对象的代码
【第二步】提供依赖对象对应的setter方法
【第三步】配置service与dao之间的关系
实现代码
【第一步】删除使用new的形式创建对象的代码
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
public class BookServiceImpl implements BookService {
//【第一步】删除使用new的形式创建对象的代码,解除对象之间的耦合度
private BookDao bookDao;
//实现业务方法
@Override
public void save() {
System.out.println("业务层:调用添加书籍的方法");
bookDao.save();
}
}
【第二步】提供依赖对象对应的setter方法
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
import com.tyhxzy.service.BookService;
public class BookServiceImpl implements BookService {
//service需要依赖Dao
private BookDao bookDao ;
@Override
public void save() {
bookDao.save();
System.out.println("service:添加书本..");
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
【第三步】配置service与dao之间的关系
在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">
<!--1. 创建Dao的对象,并且把该对象存储到了spring的容器中-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
<!--使用bean标签即可使用spring的ioc的功能,让spring创建BookServiceImpl这个类的对象
id: 该对象在容器中唯一标示,名字可以随意,但是要确保唯一性。
class: 通知spring帮我们创建制定类的对象,并且放入spring的容器中。
-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<!--给对象的bookDao的属性赋值
name: 属性名,属性名是依赖setter方法的。
ref: 引用,引用另外一个对象给该属性赋值。
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
图解演示
<property name="" ref="引入对象的id值">
标签- name:属性名,属性名依赖setter方法
- ref: 引入的对象的id
Bean的基础配置
配置说明
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. 创建Dao的对象,并且把该对象存储到了spring的容器中-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
<!--name作用:给对象起一个别名,name与id的区别:id只能配置一个,name可以配置多个,name多个别名是使用,; 空格分隔-->
<bean id="bookService" name="bookService1,bookService2,bookService4 bookService5" class="com.tyhxzy.service.impl.BookServiceImpl">
<!--给对象的bookDao的属性赋值
name: 属性名,属性名是依赖setter方法的。
ref: 引用,引用另外一个对象给该属性赋值。
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
package com.tyhxzy.test;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookService bookService = context.getBean("bookService2", BookService.class);
bookService.save();
//3。关闭容器(以后不需要关闭的)
context.close();
}
}
Bean作用范围配置
扩展:scope的取值不仅仅只有singleton和prototype,还有request、session、application、 websocket ,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request表示保存到request域中。
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">
<!--1. 创建Dao的对象,并且把该对象存储到了spring的容器中-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
<!--name作用:给对象起一个别名,name与id的区别:id只能配置一个,name可以配置多个,name多个别名是使用,; 空格分隔
scope作用: 指定对象的作用范围, 常用两种: singleton(单例,默认) , prototype(多例)
-->
<bean id="bookService" name="bookService1,bookService2,bookService4 bookService5"
class="com.tyhxzy.service.impl.BookServiceImpl" scope="prototype">
<!--给对象的bookDao的属性赋值
name: 属性名,属性名是依赖setter方法的。
ref: 引用,引用另外一个对象给该属性赋值。
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
package com.tyhxzy.test;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookService bookService1 = context.getBean("bookService2", BookService.class);
BookService bookService2 = context.getBean("bookService2", BookService.class);
System.out.println("两个对象是同一个吗?"+(bookService1==bookService2));
//3。关闭容器(以后不需要关闭的)
context.close();
}
}
最后给大家说明一下:在我们的实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。
- 在<bean>标签上如何配置别名?
- name属性可以配置
- 那Bean的默认作用范围是什么?如何修改?
- 单例,singleton.
- scope="prototype|singleton"
Bean的实例化
Bean的实例化方式有几种
bean本质上就是对象,创建bean使用构造方法完成
实例化Bean的三种方式
构造方法方式
package com.tyhxzy.dao.impl;
import com.tyhxzy.dao.BookDao;
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("BookDaoImpl的无参构造方法创建了....");
}
@Override
public void save() {
System.out.println("DAO:添加了图书..");
}
}
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">
<!--方式一: 利用一个类的无参构造方法创建 以后最为常用的一种方式
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
-->
</beans>
AppTest测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookDao bookDao = context.getBean("bookDao", BookDao.class);
bookDao.save();
//3。关闭容器(以后不需要关闭的)
context.close();
}
}
注意:无参构造方法如果不存在,将抛出异常 BeanCreationException
静态工厂方式
BookFactoryBean工厂类
package com.tyhxzy.factory;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
public class BookFactoryBean {
/*
静态的工厂方法
*/
public static BookDao getBookDao(){
System.out.println("静态工厂方法...");
return new BookDaoImpl();
}
}
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">
<!--方式一: 利用一个类的无参构造方法创建 以后最为常用的一种方式
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
-->
<!--方式二: 利用静态工厂方法创建对象
id: 该对象在spring容器中id
class: 静态工厂类的全类名
factory-method: 静态工厂的方法名
-->
<bean id="bookDao" class="com.tyhxzy.factory.BookFactoryBean" factory-method="getBookDao"/>
</beans>
AppTest测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookDao bookDao = context.getBean("bookDao", BookDao.class);
bookDao.save();
//3。关闭容器(以后不需要关闭的)
context.close();
}
}
运行结果
实例工厂方法方式
BookFactoryBean工厂类
package com.tyhxzy.factory;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
public class BookFactoryBean {
/*
非静态方法创建BookDaoImpl对象
*/
public BookDao getBookDao2(){
System.out.println("非静态工厂方法...");
return new BookDaoImpl();
}
}
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">
<!--方式一: 利用一个类的无参构造方法创建 以后最为常用的一种方式
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
-->
<!--方式二: 利用静态工厂方法创建对象
id: 该对象在spring容器中id
class: 静态工厂类的全类名
factory-method: 静态工厂的方法名
<bean id="bookDao" class="com.tyhxzy.factory.BookFactoryBean" factory-method="getBookDao"/>
-->
<!--方式三:非静态工厂方法创建对象
步骤:
1. 创建工厂类的对象
2. 调用非静态方法创建制定类的对象
-->
<!--1. 创建工厂类的对象-->
<bean id="bookFactoryBean" class="com.tyhxzy.factory.BookFactoryBean"/>
<!--2. 调用非静态方法创建制定类的对象-->
<bean id="bookDao" factory-bean="bookFactoryBean" factory-method="getBookDao2"/>
</beans>
Bean的实例化方式有几种?
- 无参的构造器
- 工厂静态方法
- 工厂非静态方法
Bean的生命周期
生命周期相关概念介绍
- 生命周期:从创建到消亡的完整过程
- bean生命周期:bean从创建到销毁的整体过程
- bean生命周期控制:在bean创建后到销毁前做一些事情
代码演示
提供生命周期控制方法
package com.tyhxzy.dao.impl;
import com.tyhxzy.dao.BookDao;
public class BookDaoImpl implements BookDao {
/*
如果BookDao对象创建的会调用init方法
*/
public void init(){
System.out.println("init方法被调用了,该类的对象已经被创建了..");
}
/*
销毁该类的对象前调用方法
*/
public void destroy(){
System.out.println("destroy被调用了,马上就要销毁该类对象了...");
}
@Override
public void save() {
System.out.println("DAO:添加了图书..");
}
}
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">
<!--
init-method: 指定创建对象的时候调用的初始化方法的方法名
destroy-method: 销毁对象前指定调用的方法
默认情况spring使用的是饿汉单例设计模式,如果需要修改单例设计模式,可以lazy-init修改
lazy-init="true" 懒汉模式
lazy-init=false 饿汉模式 , 默认
-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl" init-method="init" lazy-init="true" destroy-method="destroy" />
</beans>
测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/*
核心知识点:
1. 默认情况spring使用的是饿汉单例设计模式,不是懒汉单例设计模式。在容器启动就创建该类的对象了。
2. 容器关闭的时候才会销毁对象。
*/
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
// //1.创建spring的容器,读取配置文件 , ApplicationContext 是spring的容器的根接口。
//ClassPathXmlApplicationContext这个容器特点:根据类路径去读取配置文件的。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
context.close();
//2. 通过id从spring的容器中查找对象
//BookService bookService = (BookService) context.getBean("bookService");
//从容器中获取对象的时候,告诉容器对象的类型即可
BookDao bookDao = context.getBean("bookDao", BookDao.class);
bookDao.save();
//3。关闭容器(以后不需要关闭的)
context.close();
// context.registerShutdownHook(); //这种方式是注册到jvm,通知jvm容器关闭的时候调用destroy方法。
}
}
spring的bean对象的生命周期? 与什么周期相关的两个属性?
- 创建spring容器的时候创建。
- init-method , destory-method
依赖注入(DI配置)
依赖注入
依赖注入的两种方式
- setter注入
简单类型(八种基础数据类型+stirng类型)
引用类型(很常用) - 构造器注入
简单类型
引用类型
setter方式注入
问题导入
setter方式注入使用什么子标签?
引用类型
在bookService中使用ref注入bookDao
<?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">
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl"/>
<!--依赖注入方式一: 利用setter方法,使用property标签注入-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
简单类型
- 在BookDaoImpl中添加int connectionNumber
- 添加set方法
- 在配置文件中注入10
package com.tyhxzy.dao.impl;
import com.tyhxzy.dao.BookDao;
public class BookDaoImpl implements BookDao {
private int connectionNumber;
public void setConnectionNumber(int connectionNumber) {
this.connectionNumber = connectionNumber;
}
@Override
public void save() {
System.out.println("DAO:添加了图书.."+connectionNumber);
}
}
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">
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl">
<property name="connectionNumber" value="100"/>
</bean>
<!--依赖注入方式一: 利用setter方法,使用property标签注入-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
//测试:从spring的容器中获取对象
@Test
public void testGetBean(){
//1. 获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从容器中获取bookDao对象
BookDao bookDao = context.getBean("bookDao", BookDao.class);
bookDao.save();
//3 关闭容器
context.close();
}
}
构造方式注入
问题导入
构造方式注入使用什么子标签?
引用类型
- 给BookServiceImpl添加构造方法,参数是BookDao
- 在配置文件中给bookService中使用构造器注入bookDao
BookServiceImpl, 添加构造方法
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
import com.tyhxzy.service.BookService;
public class BookServiceImpl implements BookService {
//service需要依赖Dao
private BookDao bookDao ;
//一个带参的构造方法
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
bookDao.save();
System.out.println("service:添加书本..");
}
//删除setter方法
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
}
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">
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl">
<property name="connectionNumber" value="100"/>
</bean>
<!--依赖注入方式一: 利用setter方法,使用property标签注入
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
-->
<!--依赖注入方式二:利用构造方法给对象注入成员变量值-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<!--
index: 构造方法的参数的索引值,从0开始
name: 构造方法的形参的名字
<constructor-arg index="0" ref="bookDao"/>
-->
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans>
测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
@Test
public void testGetServiceBean(){
//1. 获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从容器中获取bookDao对象
BookService bookService = context.getBean("bookService", BookService.class);
bookService.save();
//3 关闭容器
context.close();
}
}
简单类型
-
给BookDao添加整数类型的构造方法
package com.tyhxzy.dao.impl; import com.tyhxzy.dao.BookDao; public class BookDaoImpl implements BookDao { private int connectionNumber; private String databaseName; /* public void setConnectionNumber(int connectionNumber) { this.connectionNumber = connectionNumber; }*/ public BookDaoImpl(int connectionNumber) { this.connectionNumber = connectionNumber; } public BookDaoImpl(int connectionNumber, String databaseName) { this.connectionNumber = connectionNumber; this.databaseName = databaseName; } @Override public void save() { System.out.println("DAO:添加了图书.."+connectionNumber+" 数据库的名称:"+ databaseName); } }
1. 使用配置通过构造器给BookDao注入属性值
```xml
<!--利用构造器方式初始化成员变量-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
<constructor-arg name="databaseName" value="spring_db"/>
</bean>
参数适配【了解】
- 按类型的方式匹配
- 按位置的方式匹配
步骤
-
添加新的成员变量:String databaseName
-
创建2个参数的构造方法
-
生成toString方法
package com.tyhxzy.dao.impl; import com.tyhxzy.dao.BookDao; /** * 书籍DAO的实现类 */ public class BookDaoImpl implements BookDao { //成员变量 private int connectionNumber; private String databaseName; //无参的构造方法 public BookDaoImpl() { } //1个参数的构造方法 public BookDaoImpl(int connectionNumber) { this.connectionNumber = connectionNumber; } //2个参数的构造方法 public BookDaoImpl(int connectionNumber, String databaseName) { this.connectionNumber = connectionNumber; this.databaseName = databaseName; } @Override public String toString() { return "BookDaoImpl{" + "connectionNumber=" + connectionNumber + ", databaseName='" + databaseName + '\'' + '}'; } }
-
分别使用类型匹配和位置匹配的方式注入值
依赖自动装配【理解】
问题导入
如何配置按照类型自动装配?
自动装配概念
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
- 自动装配方式
- 按名称
- 按构造方法
- 不启用自动装配
自动装配类型
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">
<!--利用构造器方式初始化成员变量-->
<bean id="bookDao" class="com.tyhxzy.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
<constructor-arg name="databaseName" value="spring_db"/>
</bean>
<!--autowire: 创建该类对象的时候成员变量的初始化可以根据类型赋值,自动注入是依赖setter方法
创建BookServiceImpl对象的时候发现,该类有一个成员变量需要BookDao,那么spring就会自动从容器中去
查找是否有BookDao这种类型的对象。
-->
<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.dao.impl.BookDaoImpl;
import com.tyhxzy.service.BookService;
public class BookServiceImpl implements BookService {
//service需要依赖Dao
private BookDao bookDao ;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
bookDao.save();
System.out.println("service:添加书本..");
}
}
测试类
package com.tyhxzy.test;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
@Test
public void testGetServiceBean(){
//1. 获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从容器中获取bookDao对象
BookService bookService = context.getBean("bookService", BookService.class);
bookService.save();
//3 关闭容器
context.close();
}
}
小结
依赖注入有几种方式,分别对应的是哪个标签或者属性
- 通过setter方法注入 property标签
- 构造方法注入 constructor-arg
集合注入
注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>tyhxzy</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>tyhxzy</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
注入Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
说明:property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签
小结:
数组: array
list: list
set: set
map: map
properties: props
第三方资源配置管理
管理DataSource连接池对象
目标
使用spring如何创建第三方的对象交给spring的容器
管理Druid连接池
数据库准备
CREATE DATABASE IF NOT EXISTS spring_db CHARACTER SET utf8;
USE spring_db;
CREATE TABLE IF NOT EXISTS tbl_account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DOUBLE
);
INSERT INTO tbl_account VALUES(NULL,'tom',1000);
INSERT INTO tbl_account VALUES(NULL,'jerry',1000);
【第一步】添加Druid连接池依赖
<dependencies>
<!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<!--junit包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
【第二步】配置DruidDataSource连接池Bean对象
在resources下创建Spring的核心配置文件: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">
<!--连接池是需要设置JDBC四大链接参数
驱动类
url
账号
密码
-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
【第三步】在测试类中从IOC容器中获取连接池对象并打印
package com.tyhxzy.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.SQLException;
public class AppTest {
@Test
public void testGetDataSource() throws SQLException {
//1. 创建spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从容器中查找对象
DataSource dataSource = context.getBean("dataSource", DataSource.class);
System.out.println("connection:"+ dataSource.getConnection());
//3. 关闭容器
context.close();
}
}
管理c3p0连接池
【第一步】添加c3p0连接池依赖
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
【第二步】配置c3p0连接池Bean对象
<!--c3p0连接池-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
注意:同一个Spring容器中不能有两个id="dataSource"的连接池。
【第三步】在测试类中从IOC容器中获取连接池对象并打印
package com.tyhxzy.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.SQLException;
public class AppTest {
@Test
public void testC3p0GetDataSource() throws SQLException {
//1. 创建spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从容器中查找对象
DataSource dataSource = context.getBean("c3p0DataSource", DataSource.class);
System.out.println("connection:"+ dataSource.getConnection());
//3. 关闭容器
context.close();
}
}
加载properties属性文件【重点】
目的:将数据库的连接参数抽取到一个单独的文件中,与Spring配置文件解耦。
目标
目标2:加载properties文件写法标准写法该怎么写?
基本用法
【第一步】在resources下编写jdbc.properties属性文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=root
【第二步】在applicationContext.xml中开启开启context命名空间,加载jdbc.properties属性文件
【第三步】在配置连接池Bean的地方使用EL表达式获取jdbc.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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--1. 加载jdbc.properties配置文件
如果需要使用properties文件的值: ${properties的key}
注意:spring在启动的时候是会读取你的系统的用户名,并且使用username作为用户名的key。
所以就会出现覆盖你的用户名。
解决方案:
方案一:system-properties-mode="NEVER" 不使用系统的用户名
方案二:你的properties的username不要使用username,换一个名字。
-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--连接池是需要设置JDBC四大链接参数
驱动类
url
账号
密码
-->
<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>
<!--c3p0连接池-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
配置完成之后,运行之前的获取Druid连接池代码,可以获取到连接池对象就表示配置成功。
配置不加载系统属性
问题
如果属性文件中配置的不是jdbc.username,而是username=root666,那么使用${username}获取到的不是root666,而是计算机的名称。
原因
系统属性的优先级比我们属性文件中的高,替换了我们的username=root666。
解决
解决1:换一个名称,例如不叫username,叫jdbc.username。
解决2:使用system-properties-mode="NEVER"属性表示不使用系统属性。
<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="NEVER"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/spring_db
username=root
password=root
小结
-
使用哪个 标签可以加载Properties文件
-
<context:property-placeholder location=""/>
-
-
读取Properties属性的标准语法是如何
- $
-
如何不读取系统属性
- system-properties-mode="NEVER"
Spring容器
创建容器
创建容器
- 方式一:类路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");
- 方式二:文件绝对路径加载配置文件(完全不用)
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
- 方式三: 加载注解配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
获取bean对象
- 方式一:使用bean名称获取
弊端:需要自己强制类型转换
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 方式二:使用bean名称获取并指定类型
弊端:推荐使用
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
- 方式三:使用bean类型获取
弊端:如果IOC容器中同类型的Bean对象有多个,此处获取会报错
BookDao bookDao = ctx.getBean(BookDao.class);
容器类层次结构
ClassPathXmlApplication FileSystemXmlApplicationContext AnnotationConfigApplicationContext都间接实现了ApplicationContext
ApplicationContext 继承了 BeanFacoty接口
ApplicationContext接口和BeanFactory接口有什么关系?
1.ApplicationContext继承了BeanFactory接口
2. // 这句代码执行完毕只是创建了一个空的容器 没有创建任何对象
BeanFactory ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 当且仅当我们通过getBean从容器中第一次取对象时,容器才开始New对象
BookServiceImpl bs = (BookServiceImpl)ac.getBean("bs");
// 一旦这个代码执行结束,则spring容器创建完毕 并且我们配置applicationContext.xml中的所以bean都被创建对象
ApplicationContext ac1 = new ClassPathXmlApplicationContext("applicationContext.xml");
package com.tyhxzy.test;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.SQLException;
/*
容器类型:
-----| ApplicationContext 所有spring的容器的根接口
--------------| ClasspathXmlApplicationContext 类路径方式查找配置文件容器
--------------| FileSystemXmlApplicationContext 使用配置文件的绝对路径方式去查找
--------------| AnnotationConfigApplicationContext 加载注解的配置类创建的容器,目前还没有办法给大家演示,等会就行
*/
public class ApplicationContextTest {
@Test
public void testGetDataSource() throws SQLException {
//1. 创建spring容器
//类路径方式查找配置文件容器
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//文件的绝对路径查找配置文件,创建spring容器
// FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("G:/applicationContext.xml");
//注解配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//2. 从容器中查找对象
DataSource dataSource = context.getBean("dataSource", DataSource.class);
System.out.println("connection:"+ dataSource.getConnection());
//3. 关闭容器
context.close();
}
}
Spring核心容器总结
容器相关
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
- AnnotationConfigApplicationContext
spring的容器体系是如何
- BeanFactory
- ApplicationContext
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
- AnnotationConfigApplicationContext
从容器获取对象的方式有几种?
- getBean(id|name)
- getBean(id|name,类型)
- getBean(类型)
Spring注解开发
注解开发定义Bean对象
目的:xml配置Bean对象有些繁琐,使用注解简化Bean对象的定义
步骤
1. 在applicationContext.xml文件上扫描注解所在包
2. 在对应类添加注解即可
对象创建
@Service 用于创建业务逻辑层对象
@Repository 用于创建数据访问层对象
@Controller 用于创建web层对象
@Component 原则可以用于创建任意层对象,一般用于创建非三层架构中的对象
依赖注入
@Value 用于简单类型的依赖注入
用于复杂类型的依赖注入
@Autowired(容器中只有一个对象)
@Qualifier(容器中有多个对象。首先在容器中寻找与属性名同名的对象;找不到再找名为注解配置内容的对象)spring的注解
@Resources(直接去容器中找名为注解配置内容的对象;如果没有就去找实现了该接口的与属性名同名的对象)jdk的注解
基本使用
【第一步】在applicationContext.xml中开启Spring注解包扫描
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--开始注解扫描,指定要扫描注解所在的包, 扫描包的时候默认会扫描当前包与子包-->
<context:component-scan base-package="com.tyhxzy"/>
<!--1.创建了Dao-->
<!--<bean id="bookDao1" class="com.tyhxzy.dao.impl.BookDaoImp"/>-->
<!--创建Service的对象-->
<!--<bean id="bookService" class="com.tyhxzy.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao1"/>
</bean>-->
</beans>
【第二步】在类上使用@Component注解定义Bean。
package com.tyhxzy.dao;
public interface BookDao {
public void save();
}
package com.tyhxzy.dao.impl;
import com.tyhxzy.dao.BookDao;
import org.springframework.stereotype.Repository;
/*
@Repository 的作用就是相当于bean标签一样。
<bean id="bookDao1" class="com.tyhxzy.dao.impl.BookDaoImp"/>
注意: 使用@Repository @Service、@Controller、@Component注解创建类的对象的时候都可以指定该对象在容器中的id。
如果你不指定,默认id就是当前类的类名,而且首字母小写。
*/
//@Repository // id=bookDaoImp
@Repository("bookDao")
public class BookDaoImp implements BookDao {
@Override
public void save() {
System.out.println("Dao:保存了图书...");
}
}
【第三步】在类上使用@Component注解定义Service。
package com.tyhxzy.service;
public interface BookService {
public void save();
}
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
@Service 的作用就是相当于bean标签一样。
<bean id="bookDao1" class="com.tyhxzy.service.impl.BookServiceImpl"/>
注意: 使用@Repository @Service、@Controller、@Component注解创建类的对象的时候都可以指定该对象在容器中的id。
如果你不指定,默认id就是当前类的类名,而且首字母小写。
*/
@Service("bookService")
public class BookServiceImpl implements BookService {
//根据类型从容器查找合适对象给该变量注入,而且自动注入不依赖setter方法,也不依赖构造方法
@Autowired
private BookDao bookDao;
@Override
public void save() {
bookDao.save();
System.out.println("service:保存图书..");
}
}
补充说明:如果@Component注解没有使用参数指定Bean的名称,那么类名首字母小写就是Bean在IOC容器中的默认名称。例如:BookDaoImpl对象在IOC容器中的名称是bookDaoImpl。
【第三步】在测试类中获取Bean对象
package com.tyhxzy.test;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
@Test
public void test01(){
//1. 创建容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 获取对象
BookService bookService = context.getBean("bookService", BookService.class);
bookService.save();
}
}
小结
- 创建对象我们可以使用那些的注解,注入属性可以使用哪个注解
- @Repository 用于Dao层
- @Service 用于Service层
- @Controller 用于web层
- @Component 用户其他包,比如工具类..
- @Autowired 根据类型自动注入
- @Repository 用于Dao层
纯注解开发模式
问题1:配置类上使用什么注解表示该类是一个配置类?
问题2:配置类上使用什么注解进行Spring注解包扫描?
纯注解开发模式介绍
- Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
- Java类代替Spring核心配置文件
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({com.tyhxzy.service","com.tyhxzy.dao"})
- 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
//加载配置文件初始化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
2.2 代码演示
【第一步】定义配置类代替配置文件
package com.tyhxzy.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/*
如果该类是一个配置类,该类需要添加@Configuration
@Configuration作用: 表示该类是一个注解的配置类。
@ComponentScan: 指定扫描的包
*/
@Configuration
@ComponentScan("com.tyhxzy")
public class SpringConfig {
}
【第二步】在测试类中加载配置类,获取Bean对象并使用
package com.tyhxzy.test;
import com.tyhxzy.config.SpringConfig;
import com.tyhxzy.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
@Test
public void test01(){
//1. 创建容器
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//2. 获取对象
BookService bookService = context.getBean("bookService", BookService.class);
bookService.save();
}
}
注解开发Bean作用范围和生命周期管理
问题导入
在类上使用什么注解定义Bean的作用范围?
bean作用范围注解配置
- 使用@Scope定义bean作用范围
@Repository
@Scope("singleton")
public class BookDaoImpl implements BookDao {
}
bean生命周期注解配置
- 使用@PostConstruct、@PreDestroy定义bean生命周期
@Repository
@Scope("singleton")
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor ...");
}
@PostConstruct
public void init(){
System.out.println("book init ...");
}
@PreDestroy
public void destroy(){
System.out.println("book destory ...");
}
}
注意:@PostConstruct和@PreDestroy注解是jdk中提供的注解,从jdk9开始,jdk中的javax.annotation包被移除了,也就是说这两个注解就用不了了,可以额外导入一下依赖解决这个问题。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
- 测试类
@Test
public void testPureAnnotation() {
//AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
//关闭容器
ctx.close();
}
注解开发依赖注入
问题导入
问题1:请描述@Autowired注解是如何进行自动装配的?
问题2:请描述@Qualifier注解的作用
使用@Autowired注解开启自动装配模式(按类型)
@Service
public class BookServiceImpl implements BookService {
//@Autowired:注入引用类型,自动装配模式,默认按类型装配
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
说明:不管是使用配置文件还是配置类,都必须进行对应的Spring注解包扫描才可以使用。@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean有多个,那么默认按照变量名和Bean的名称匹配,建议使用@Qualifier注解指定要装配的bean名称
注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法。
使用@Qualifier注解指定要装配的bean名称
目的:解决IOC容器中同类型Bean有多个装配哪一个的问题
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.BookDao;
import com.tyhxzy.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/*
@Service 的作用就是相当于bean标签一样。
<bean id="bookDao1" class="com.tyhxzy.service.impl.BookServiceImpl"/>
注意: 使用@Repository @Service、@Controller、@Component注解创建类的对象的时候都可以指定该对象在容器中的id。
如果你不指定,默认id就是当前类的类名,而且首字母小写。
*/
@Service("bookService")
@Scope("singleton") //指定作用范围: singleton(单例)与prototype(多例)
public class BookServiceImpl implements BookService {
//根据类型从容器查找合适对象给该变量注入,而且自动注入不依赖setter方法,也不依赖构造方法
/*
注意: 使用 @Autowired根据类型自动注入 的时候,如果spring的容器中存在多个相同类型的对象
那么优先使用名字与该属性名一样对象。 如果你需要指定注入某一个对象,那么需要配合@Qualifier注解
去使用,@Qualifier注解可以指定名字去注入,但是@Qualifier一定不能单独使用,需要配合@Autowired使用
*/
@Autowired
@Qualifier("bookDao2")
private BookDao bookDao;
@Override
public void save() {
bookDao.save();
System.out.println("service:保存图书..");
}
}
注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
使用@Value实现简单类型注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
//@Value:注入简单类型(无需提供set方法)
@Value("${name}")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
以上@Value注解中使用${name}从属性文件中读取name值,那么就需要在配置类或者配置文件中加载属性文件。
@Configuration
@ComponentScan("com.tyhxzy")
//@PropertySource加载properties配置文件
@PropertySource({"classpath:jdbc.properties"}) //{}可以省略不写
public class SpringConfig {
}
注意:@PropertySource中加载多文件请使用数组格式配置,不允许使用通配符*
注解开发管理第三方Bean
问题导入
导入自己定义的配置类有几种方式?
【第一步】单独定义配置类
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=root
package com.tyhxzy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
//从配置你文件读取value使用@Value注解
@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对象,并且放入到spring 容器中,需要创建一个方法去创建
@bean注解的作用:
1. 添加@Bean注解的方法会自动调用
2. 方法的返回值对象会进入到spring的容器中,
*/
@Bean("dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
【第二步】将独立的配置类加入核心配置
方式1:@Import注解导入式
package com.tyhxzy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration //表示该类是一个注解的配置类
@Import(JdbcConfig.class) //@Import 引入另外一个配置类
public class SpringConfig {
}
注解开发总结
- 创建bean的注解
- @Component
- @Repository
- @Service
- @Controller
- @Component
- 纯注解开发注解
- @Configuration 表示该类是一个注解配置类
- @ComponentScan 扫描
- @Configuration 表示该类是一个注解配置类
- 生命周期相关注解
- @Scope 作为范围
- @PostConstruct 指定初始化方法
- @PreDestroy 指定销毁前的方法
- @Scope 作为范围
- 为依赖注入的注解
- AutoWired 根据类型注入注入
- @Qualifier 指定名字注入
- @Value
- AutoWired 根据类型注入注入
- 创建第三方bean的对象
- @Bean 1. 方法会被自动调用 2. 方法返回值进入到spring 的容器中
Spring整合其他技术
Spring整合mybatis
思路分析
mybatis进行数据层操作的核心对象是谁?
MyBatis程序核心对象分析
Connection 连接 DataSource 连接池
SqlSession 连接 SqlSessionFactory 连接工厂
整合MyBatis
使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息
使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中
spring配置文件整合mybatis
步骤:
1.导入依赖
spring-context
德鲁伊 数据源
mybatis
mysql
mybatis-spring (spring整合mybatis)
spring-jdbc (spring聚合jdbc)
junit
lombok
2.创建实体类
3.创建数据访问层
4.创建业务逻辑层
5.spring创建对象和属性的配置
6.单元测试
java.lang.NoClassDefFoundError:org/springframework/dao/support/Dao
错误原因是:
缺少依赖 spring-jdbc
第一步:导入依赖pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yxh</groupId>
<artifactId>SpringAndMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringAndMybatis</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<!--junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>compile</scope>
</dependency>
<!--
德鲁伊的依赖 他可以用来创建连接池
-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.22</version>
</dependency>
<!-- spring整合mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
第二步:创建实体类
package com.yxh.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private String id;
private String name;
private String pwd;
}
第三步:创建数据访问层
package com.yxh.mapper;
import com.yxh.domain.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> selectAll();
}
第四步:创建业务逻辑层
package com.yxh.service;
import com.yxh.domain.User;
import com.yxh.mapper.UserMapper;
import java.util.List;
public interface UserService {
List<User> selectUsers();
}
package com.yxh.service;
import com.yxh.domain.User;
import com.yxh.mapper.UserMapper;
import lombok.Setter;
import java.util.List;
@Setter
public class UserServiceImpl implements UserService{
private UserMapper userMapper;
@Override
public List<User> selectUsers() {
return userMapper.selectAll();
}
}
第五步spring创建对象和属性的配置
<?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">
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/login"/>
</bean>
<!--创建sqlsessionFacroty对象 对象名为ssf-->
<bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源对象-->
<property name="dataSource" ref="ds"/>
</bean>
<!--一次性将数据访问层的接口全部创建实现类 并且把实现类new成对象 放spring容器中
basePackage: 数据访问层接口所在的包
sqlSessionFactoryBeanName:sqlSessionFactory的对象名
一旦这个代码执行完 spring容器中会有数据访问层接口对应的实现类的全部对象
对象名为接口名首字母小写
接口名为:StudentsDao 对象名:studentsDao 不能自己改 对象名是死的
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yxh.mapper"/>
<property name="sqlSessionFactoryBeanName" value="ssf"/>
</bean>
<bean id="ss" class="com.yxh.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
</beans>
第六步:单元测试
package com.yxh;
import com.yxh.domain.User;
import com.yxh.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAndMybatisTest {
@Test
public void testSelectUsers(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService ss = applicationContext.getBean("ss", UserService.class);
ss.selectUsers().forEach(System.out::println);
}
}
代码实现
问题1:Spring整合mybatis的依赖叫什么?
问题2:Spring整合mybatis需要管理配置哪两个Bean,这两个Bean作用分别是什么?
1.创建新的maven工程
2.在pom.xml中添加spring-context、druid、mybatis、mysql-connector-java等基础依赖。
<dependencies>
<!-- spring框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
<!-- lombok
注意: 使用lombok前提
1. 安装lombok插件
2. 导入lombok依赖
3. 在实体类上添加@Data注解, 以后你实体类在编译的时候就会自动添加getter、setter、toString...
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- junit4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- spring整合mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
3.准备service和dao层基础代码
package com.tyhxzy.domain;
public class Account {
private Integer id; //主键
private String name; //用户名
private Double money; //金额
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
package com.tyhxzy.dao;
import com.tyhxzy.domain.Account;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* 持久层接口
*/
public interface AccountDao {
@Insert("insert into tbl_account(name,money)values(#{name},#{money})")
void save(Account account);
@Delete("delete from tbl_account where id = #{id} ")
void delete(Integer id);
@Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
void update(Account account);
@Select("select * from tbl_account")
List<Account> findAll();
@Select("select * from tbl_account where id = #{id} ")
Account findById(Integer id);
}
package com.tyhxzy.service;
import com.tyhxzy.domain.Account;
import java.util.List;
/**
* 业务接口
*/
public interface AccountService {
void save(Account account);
void delete(Integer id);
void update(Account account);
List<Account> findAll();
Account findById(Integer id);
}
package com.tyhxzy.service.impl;
import com.tyhxzy.dao.AccountDao;
import com.tyhxzy.domain.Account;
import com.tyhxzy.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public void save(Account account) {
accountDao.save(account);
}
public void update(Account account){
accountDao.update(account);
}
public void delete(Integer id) {
accountDao.delete(id);
}
public Account findById(Integer id) {
return accountDao.findById(id);
}
public List<Account> findAll() {
return accountDao.findAll();
}
}
【第一步】导入Spring整合Mybatis依赖
上面的pom.xml文件中已经导入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
【第二步】创建JdbcConfig配置DataSource数据源
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=root
package com.tyhxzy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}") // @value读取配置文件的属性
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//创建连接池对象
@Bean("dataSource")//这个方法需要自动调用而且返回值放入spring的容器中
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
【第三步】创建MybatisConfig整合mybatis
package com.tyhxzy.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class MybatisConfig {
//mybatis包扫描, 扫描到到接口会创建代理对象的。
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.tyhxzy.dao");
return mapperScannerConfigurer;
}
//由于以后我们不需要再创建SqlSessionFactory,我们都直接交给spring的管理,所以你需要创建
/*
@Bean注解的作用:
1. 方法会自动调用
2. 方法的返回值进入到spring 的容器中
3. 调用方法的时候,如果该方法有形参,会自动从spring 的容器中根据形参的类型主动注入。
*/
@Bean
public SqlSessionFactoryBean createSqlSessionFactory(DataSource dataSource) {
//1. 创建SqlSessionFactory
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
//2. 设置数据源 (连接池)
sessionFactoryBean.setDataSource(dataSource);
//3. 设置别名扫描
sessionFactoryBean.setTypeAliasesPackage("com.tyhxzy.domain");
return sessionFactoryBean;
}
}
【第四步】创建SpringConfig主配置类进行包扫描和加载其他配置类
package com.tyhxzy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration //这是一个配置类
@ComponentScan("com.tyhxzy") //扫描com.tyhxzy下面的所有类
@Import(value = {JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
【第五步】定义测试类进行测试
package com.tyhxzy.test;
import com.tyhxzy.config.SpringConfig;
import com.tyhxzy.domain.Account;
import com.tyhxzy.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyBatisTest {
@Test
public void test01(){
//1. 创建容器
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//2. 取出Service
AccountService accountService = context.getBean("accountService", AccountService.class);
Account account = accountService.findById(1);
System.out.println("用户:"+account);
}
}
运行结果
Spring整合JUnit单元测试
spring整合junit的好处
-
spring一旦整合junit以后以后我们不需要手动创建容器了。
- 如果需要查找对象直接使用依赖注入即可。
代码
【第一步】导入整合的依赖坐标spring-test
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring整合junit -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
<scope>test</scope>
</dependency>
【第二步】使用Spring整合JUnit专用的类加载器
【第三步】加载配置文件或者配置类
package com.tyhxzy.test;
import com.tyhxzy.config.SpringConfig;
import com.tyhxzy.domain.Account;
import com.tyhxzy.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
//指定当前的运行环境是spring,叫SpringRunner帮你创建容器,执行测试的方法
@RunWith(SpringRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml") //指定加载的配置类,或者是配置文件
@ContextConfiguration(classes = SpringConfig.class)
public class MyBatisTest {
@Autowired
private AccountService accountService;
@Test
public void test01(){
Account account = accountService.findById(1);
System.out.println("用户:"+account);
}
}