一、第三方资源配置管理
以DataSource连接池对象为例,进行第三方资源配置管理。
1. 管理DataSource连接池对象
spring整合Druid、C3P0数据库连接池
1.1 管理Druid连接池
1、准备数据
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);
2、导入druid的依赖坐标
<dependencies>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!--druid连接池-->
<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>
3、配置druid连接池的bean对象(在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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源配置,使用druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/spring_db?useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
</beans>
mysql5.7的数据库驱动:一般会在url后面添加参数useSSL=false
(关闭安全验证)
mysql8的数据库驱动:需要添加时区serverTimezone=Asia/Shanghai
。
4、编写测试(从IOC容器中获取连接池对象)
public class App {
public static void main(String[] args) {
// 创建IoC容器对象,加载spring核心配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从IOC容器中获取连接池对象
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
1.2 管理C3P0连接池
1、导入C3P0的依赖坐标
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
2、配置c3p0连接池的bean对象(在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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源配置,使用c3p0数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring_db?useSSL=false"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="1000"/>
</bean>
</beans>
3、编写测试(从IOC容器中获取连接池对象)
public class App {
public static void main(String[] args) {
// 创建IoC容器对象,加载spring核心配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从IOC容器中获取连接池对象
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
2. 加载properties属性文件
将数据库连接参数抽取到一个单独的配置文件中,与Spring核心配置文件解耦。
1、编写jdbc.properties
属性文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=123456
需要加前缀jdbc.xxx,不然后面使el表达式获取不到配置文件中的属性值。
2、在spring核心配置文件中开启context命名空间,加载jdbc.properties属性文件
突发小技巧:使用idea提示,生成上面的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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载数据库配置文件-->
<context:property-placeholder location="jdbc.properties"/>
</beans>
3、在配置连接池Bean的地方使用EL表达式获取jdbc.properties属性文件中的值
<bean 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>
Spring加载properties文件写法(XML配置)
1、加载properties文件标准格式:
<context:property-placeholder location="classpath:*.properties"/>
2、不加载系统属性:
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
3、加载多个properties文件:
<context:property-placeholder location="jdbc.properties,msg.properties"/>
4、加载所有properties文件:
<context:property-placeholder location="*.properties"/>
二、Spring容器
1. 创建Spring容器
方式1:类路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
方式2:文件路径加载配置文件(不常用)
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
方式3:加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");
2. 获取bean对象
方式1:使用bean名称获取(弊端:需要自己强制类型转换)
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
方式2:使用bean名称获取并指定类型 (推荐使用,不需要类型转换)
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
方式3:使用bean类型获取(弊端:如果IOC容器中同类型的Bean对象有多个,此处获取会报错)
BookDao bookDao = ctx.getBean(BookDao.class);
3. 容器类的体系结构
找到BeanFactory接口后,按下Ctrl+h就出来架构图(在org.springframework.beans.factory包下)
4. BeanFactory
//类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao", BookDao.class);
bookDao.save();
BeanFactory创建完毕后,所有的Bean均为延迟加载,也就是说我们调用getBean()方法获取Bean对象时才创建Bean对象并返回给我们。
面试题:ApplicationContext和BeanFactory的区别与联系?
-
ApplicationContext是BeanFactory的子接口,BeanFactory是spring容器的顶层接口。
-
BeanFactory创建单实例的时机:第一次调用getBean的时候才创建。
-
ApplicationContext在容器初始化的时候就会将里面配置的所有单实例对象创建好。
- ClasspathXmlApplicationContext:类路径下加载xml。
- FileSystemXmlApplicationContext:操作系统任意路径下加载xml。
- AnnotationConfigApplicationContext:注解类。
三、Spring注解开发
1. 定义Bean对象
某某老师说注解开发是主流,接下来我们使用注解简化Bean对象的定义????
1、在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"
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">
<!--扫描com.baidou包及子包下的类中注解-->
<context:component-scan base-package="com.baidou"/>
</beans>
2、在类上使用@Component注解定义Bean。
// 使用@Component定义bean,通过value属性定义bean的名字
// 如果注解中没有使用参数设置名字,那么bean的默认名称是类名首字母小写
@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
@Component
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
如果@Component注解没有使用参数指定Bean的名称,那么类名首字母小写就是Bean在IOC容器中的默认名称。例如:BookServiceImpl对象在IOC容器中的名称是bookServiceImpl。
3、在测试类中获取bean对象
package com.baidou;
import com.baidou.dao.BookDao;
import com.baidou.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过名字获取dao层的bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
//通过类型获取service层的bean
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
运行结果:
注意:在测试类中不要调用bookService的save方法,因为还没有给BookServiceImpl中的bookDao赋值,所以调用bookService的save方法会出现空指针异常。
2. @Component注解
我们知道@Component注解是定义bean的,相当于bean标签的配置。
Spring为@Component
注解提供了三个衍生注解:
-
@Controller
:定义表现层的bean。 -
@Service
:定义业务层的bean。 -
@Repository
:定义数据层的bean。
@Repository
public class BookDaoImpl implements BookDao {
}
@Service
public class BookServiceImpl implements BookService {
}
@Controller
public class Controller {
}
3. 纯注解开发模式
Spring3.0开启了纯注解开发模式,使用Java类代替Spring核心配置文件。(配置类)
- @Configuration注解:声明当前类是spring配置类。
- @ComponentScan注解:用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式。(组件扫描)
1、定义配置类代替spring核心配置文件。
package com.baidou.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //声明当前类是Spring配置类(也是一个组件@Component)
//Spring组件扫描,相当于<context:component-scan base-package="com.baidou"/>
@ComponentScan("com.baidou") //配置bean的包扫描路径
//配置多个路径书写为字符串数组格式:@ComponentScan({"com.baidou.service","com.baidou.dao"})
public class SpringConfig {
}
2、用注解分别注册业务和数据层的bean。(在实现类上用)
package com.baidou.dao;
import org.springframework.stereotype.Repository;
@Repository("bookDao") //定义数据层的bean,然后bean的名字设置为bookDao
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ...");
}
}
package com.baidou.service;
import com.baidou.dao.BookDao;
import org.springframework.stereotype.Service;
@Service //定义业务层的bean
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
3、在测试类中加载配置类,获取Bean对象。
package com.baidou;
import com.baidou.config.SpringConfig;
import com.baidou.dao.BookDao;
import com.baidou.service.BookServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppForAnnotation {
public static void main(String[] args) {
// 加载spring配置类方式获取spring容器对象
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);//参数:Class<?>... componentClasses
// 从容器中获取bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookServiceImpl bookService = ctx.getBean(BookServiceImpl.class);
bookService.setBookDao(bookDao);
bookService.save();
}
}
运行结果:
4. Bean作用范围和生命周期管理(注解开发)
4.1 bean的作用范围配置
使用==@Scope==定义bean作用范围。
@Repository
@Scope("singleton") // 定义的bean的作用范围:singleton单例
public class BookDaoImpl implements BookDao {
}
@Repository
@Scope("singleton") // 非单例
public class BookDaoImpl implements BookDao {
}
4.2 bean的生命周期配置
使用==@PostConstruct==、@PreDestroy定义bean生命周期。
package com.baidou.dao;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao 无参构造器执行了 ...");
}
@PostConstruct //初始化操作
public void init(){
System.out.println("book init ...");
}
@PreDestroy //销毁操作
public void destroy(){
System.out.println("book destory ...");
}
@Override
public void save() {
System.out.println("book dao save ...");
}
}
package com.baidou;
import com.baidou.config.SpringConfig;
import com.baidou.dao.BookDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppForAnnotation {
public static void main(String[] args) {
// 加载spring配置类方式获取spring容器对象
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);//参数:Class<?>... componentClasses
// 从容器中获取bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// 调用bean的方法
bookDao.save();
// 关闭spring容器
ctx.registerShutdownHook();
}
}
注意:@PostConstruct和@PreDestroy注解是jdk中提供的注解,从jdk9开始,jdk中的javax.annotation包被移除了,也就是说这两个注解就不能用了,可以额外导入一下依赖解决这个问题。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
5. 依赖注入(注解开发)
思考:
- @Autowired注解是如何进行自动装配的。
- @Qualifier注解的作用。
5.1 使用@Autowired注解开启自动装配模式(默认按类型装配)
dao接口实现类:
package com.baidou.dao;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ...");
}
}
service接口实现类:
package com.baidou.service;
import com.baidou.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
/*
@Autowired:自动装配,默认按照类型去查找对应的bean,如果找到就直接注入;
使用自动装配,就不用提供set、构造器了,@Autowired底层用反射方式注入;
*/
@Autowired
private BookDao bookDao;
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
测试类:
package com.baidou;
import com.baidou.config.SpringConfig;
import com.baidou.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
// 创建spring容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
// 获取容器中的bean
BookService bookService = ctx.getBean(BookService.class);
// 调用service中方法
bookService.save();
}
}
不管是使用配置文件还是配置类,都必须进行对应的Spring注解包扫描才可以使用。
@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean有多个,那么默认按照变量名和Bean的名称匹配,建议使用@Qualifier注解指定要装配的bean名称。
注意:自动装配基于反射方式创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法。(让我想起了单例模式????)
5.2 使用@Qualifier注解指定要装配的bean名称
@Qualifier作用:解决IOC容器中同种类型Bean有多个,并通过名称去选择指定的bean装配。
package com.baidou.service;
import com.baidou.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
/*
@Autowired:自动装配,默认按照类型去查找对应的bean,如果找到就直接注入
使用自动装配,就不用提供set、构造器了,@Autowired底层用反射方式注入
*/
@Autowired
//private BookDao bookDaoImpl2; //若按照类型查找多个的话,将成员变量名作为bean的名字去找,若找到直接注入。找不到就报错(不是唯一的bean异常)
@Qualifier("bookDaoImpl2")//使用@Qualifier注解指定要装配bean的名称
private BookDao bookDao;
/*
// 解决@Autowired自动装配,同类型bean过多,不是唯一的bean异常:
方案1:将成员变量名取成具体bean的名字。(类名首字母小写)
方案2:使用@Qualifier设置要装配bean的名字。
*/
@Override
public void save() {
System.out.println("book service save ...");
// bookDaoImpl2.save();
bookDao.save();
}
}
注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用。
5.3 使用@Value实现简单类型注入
????使用@Value实现简单类型注入:
package com.baidou.dao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl2 implements BookDao {
@Value("小舞")
String name;
@Override
public void save() {
System.out.println("book dao save2 ...");
System.out.println("name:"+name);
}
}
????读取属性文件的内容,然后进行简单类型注入:
@PropertySource和@Value组合使用,可以将自定义属性文件中的属性变量值注入到当前类的使用@Value注解的成员变量中。
1、定义book.properties文件
name=javayyds
2、在配置类中,添加@PropertySource注解,加载properties配置文件
package com.baidou.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.baidou")
//@PropertySource:加载properties配置文件
@PropertySource({"classpath:book.properties"}) //{}可以省略不写
public class SpringConfig {
}
3、在@Value注解使用${name}从properties文件中读取name值
package com.baidou.dao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl2 implements BookDao {
@Value("${name}")
String name;
@Override
public void save() {
System.out.println("book dao save2 ...");
System.out.println("name:"+name);
}
}
注意:@PropertySource()中加载多个文件时请使用数组格式配置,不允许使用通配符。
5.4 使用@Resource注解自动装配
@Resource是JDK提供的注解,它默认按照变量名(bean的名字)查找,若找到直接装配,若按名字找不到就会按照类型查找并装配;
若找到多个相同类型bean也会报错;(也可以通过name属性指定注入bean的名字)
package com.baidou.service;
import com.baidou.dao.BookDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class BookServiceImp2 implements BookService {
@Resource(name = "bookDaoImpl2")
private BookDao bookDaoImpl;
@Override
public void save() {
System.out.println("book service save ...");
bookDaoImpl.save();
}
}
@Autowired和@Resource的区别:
-
@Resource和@Autowired都是用来自动装配的。
-
@Autowired注解,默认按类型装配。如果出现多个相同类型的bean会报错(唯一bean异常),解决方案:使用@Qualifier与@Autowired组合使用指定装配bean的名称。
-
@Resource注解,是JDK提供的,它默认按照变量名(即bean的名字)查找,若找到直接装配,若按名字找不到就会按照类型查找并装配;
6. 管理第三方Bean(注解开发)
使用@Bean注解配置第三方bean,然后将第三方bean注入到项目的spring容器中。、
以druid的数据源为例。
1、导入依赖:
<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>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
2、自定义配置类JdbcConfig,配置第三方bean。(@Configuration+@Bean方式,将第三放bean放到IOC容器中)
package com.baidou.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
// @Configuration // 在spring配置类做配置的统一体管理,此类就不用加@Configuration注解了
public class JdbcConfig {
//@Bean:表示当前方法的返回值是一个bean对象,然后将方法返回值的bean对象放到IOC容器中。
//可以通过@Bean注解的name或value属性设置bean的名字。
// 默认方法名就是bean的名字
@Bean
// @Bean("ds")
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("123456");
return ds;
}
}
3、在spring配置类中加载自定义配置类JdbcConfig。
方式1:通过组件扫描方式(@ComponentScan)
@Configuration
// @ComponentScan({"com.baidou.config","com.baidou.service","com.baidou.dao"})//扫秒多个包下的bean
@ComponentScan("com.baidou") //扫描主包以及子包下的bean
public class SpringConfig {
}
方式二:使用@Import注解导入组件
@Configuration
@Import({JdbcConfig.class}) //导入其他的配置类
public class SpringConfig {
}
7. 为第三方Bean注入资源(注解开发)
配置类中如何注入简单类型数据,如何注入引用类型数据?
- 简单类型: 使用@PropertySource+@Value 读取配置文件中的数据
- 引用类型:通过形参注入
7.1 为第三方bean注入简单类型
1、定义jdbc.properties文件(需要使用前缀jdbc.xxx,要不它会把配置文件中的属性当成系统属性)
jabc.driver=com.mysql.jdbc.Driver
jabc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jabc.userName=root
jabc.password=123456
2、在spring配置文件中,加载.properties文件
@Configuration
@Import({JdbcConfig.class})
@PropertySource({"classpath:jdbc.properties"})//加载jdbc.properties文件
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:表示当前方法的返回值是一个bean对象,然后将方法返回值的bean对象放到ioc容器中
@Bean
public DataSource dataSource() {
// 创建德鲁伊的数据源,并配置基础的参数
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
注意:如果@Value()中使用了EL表达式读取properties属性文件中的内容,那么就需要在spring配置类中加载properties属性文件。
7.2 为第三方bean注入引用类型
//Spring会自动从IOC容器中找到BookDao对象赋值给参数bookDao变量,如果没有就会报错。
@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;
}
注意:引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。
四、Spring整合Junit单元测试
1、导入依赖
<!--junit版本>=4.12-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spring整合junit 版本务必和其他的spring的一致-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2、编写测试类:
- @RunWith(SpringJUnit4ClassRunner.class),使用Spring整合Junit专用的类加载器。
- 加载配置文件或者配置类(使用@ContextConfiguration注解)。
@RunWith(SpringJUnit4ClassRunner.class)//使用Spring整合Junit专用的类加载器(对原始junit进行增强)
@ContextConfiguration(classes = {SpringConfig.class}) //加载spring配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载spring核心配置文件
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}
五、Spring整合MyBatis
整合MyBatis:
- 使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息。
- 使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中。
1、创建一个普通的Maven工程
2、向pom.xml
文件导入相关依赖
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 数据库连接池druid(德鲁伊) -->
<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>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</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>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- spring整合junit相关的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
3、导入SQL脚本
-- 如果数据库spring_db存在就把他删掉
drop database if exists spring_db;
-- 创建数据库spring_db并设置字符集utf-8
create database spring_db default charset utf8;
-- 使用spring_db数据库
use spring_db;
-- 创建账户表
create table tb_account(
id int primary key auto_increment comment 'id',
name varchar(20) comment '账户名称',
money double comment '账户余额'
);
-- 插入数据
insert into tb_account values(null,'zhangsan',1000),
(null,'lucy',3500),
(null,'白豆五',7500);
4、编辑pojo类:
public class Account implements Serializable {
private Integer id; //id
private String name; //账户名称
private Double money;//账户余额
// 无参构造方法
public Account() {
}
// 满参构造方法
public Account(Integer id, String name, Double money) {
this.id = id;
this.name = name;
this.money = money;
}
//getter/setter方法
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;
}
//toString方法
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
5、编写dao接口:
// 操作数据库的接口
public interface AccountDao {
// 添加账户
@Insert("insert into tb_account values (null,#{name},#{money})")
void save(Account account);
// 根据id删除账户
@Delete("delete from tb_account where id=#{id} ")
void deleteById(int id);
// 根据id修改账户
@Update("update tb_account set name=#{name},money=#{money} where id=#{id}")
void update(Account account);
// 查所有
@Select("select * from tb_account")
List<Account> findAll();
// 根据id查账户
@Select("select * from tb_account where id=#{id}")
Account selectById(int id);
}
6、编写service接口和实现类:
public interface AccountService {
// 添加账户
void add(Account account);
// 根据id删除账户
void deleteById(int id);
// 根据id修改账户
void update(Account account);
// 查所有
List<Account> findAll();
// 根据id查账户
Account selectById(int id);
}
@Service //定义业务层Bean交给Spring容器管理
public class AccountServiceImpl implements AccountService {
@Autowired //依赖注入(默认按类型自动装配)
private AccountDao accountDao;
@Override
public void add(Account account) {
accountDao.save(account);
}
@Override
public void deleteById(int id) {
accountDao.deleteById(id);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public Account selectById(int id) {
return accountDao.selectById(id);
}
}
7、编写JDBC配置类:
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,返回dataSource对象
@Bean //表示当前方法的返回值是一个Bean对象,交给spring容器去管理
public DataSource dataSource(){
// 创建Druid数据源对象
DruidDataSource ds = new DruidDataSource();
// 配置数据源四个基本参数
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
// 返回bean
return ds;
}
}
db.properties:
jabc.driver=com.mysql.jdbc.Driver
jabc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jabc.userName=root
jabc.password=123456
8、编写mybatis配置类:
public class MyBatisConfig {
//定义bean,SqlSessionFactoryBean,用于构造sqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
//如何为第三方bean注入资源:只需写入形参即可,spring会自动从ioc容器找DataSource对象并赋值给变量名dataSource。
// 创建sqlSessionFactoryBean对象
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
// 设置数据源
ssfb.setDataSource(dataSource);
// 返回bean
return ssfb;
}
// 定义bean,返回mapperScannerConfigurer对象
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
// 创建MapperScannerConfigurer对象
MapperScannerConfigurer msc = new MapperScannerConfigurer();
//包扫描,可以获取包下所有dao接口,配合sqlSessionFactory对象中的sqlSession,创建接口的mapper代理对象,然后放入spring容器中
msc.setBasePackage("com.baidou.dao");
// 返回bean
return msc;
}
}
9、编写主配置类:
@Configuration //声明当前类是一个spring配置类
@ComponentScan("com.baidou") //组件扫描
@Import({JdbcConfig.class,MyBatisConfig.class}) //导入其他的配置类
@PropertySource({"classpath:db.properties"}) //加载类路径下的properties配置文件
public class SpringConfig {
}
10、编写测试
10.1、测试Druid数据源是否配置成功
public class TestJDBC {
@Test
public void testJDBC() {
// 通过注解方法获取IOC容器
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource dataSource = app.getBean(DataSource.class);
System.out.println(dataSource);
/*
{
CreateTime:"2022-12-17 21:46:30",
ActiveCount:0,
PoolingCount:0,
CreateCount:0,
DestroyCount:0,
CloseCount:0,
ConnectCount:0,
Connections:[]
}
*/
}
}
10.2 测试接口
@RunWith(SpringJUnit4ClassRunner.class)//使用Spring整合Junit专用的类加载器
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountServiceTest {
@Autowired
private AccountService accountService;
/**
* 测试添加账户
*/
@Test
public void testAdd() {
accountService.add(new Account(null, "王五", 500d));
}
/**
* 测试根据id修改账户
*/
@Test
public void testUpdate() {
// 原先信息 id=3 name=”白豆五“ money=7500
// 修改信息 id=3 name=”白豆五“ money=7000
accountService.update(new Account(3, "白豆五", 7000d));
}
/**
* 测试查所有
*/
@Test
public void findAll() {
List<Account> list = accountService.findAll();
list.forEach(System.out::println);
// Account{id=1, name='zhangsan', money=1000.0}
// Account{id=2, name='lucy', money=3500.0}
// Account{id=3, name='白豆五', money=7000.0}
// Account{id=4, name='王五', money=500.0}
}
/**
* 根据id删除账户
*/
@Test
public void testDeleteById() {
accountService.deleteById(4);
}
/**
* 根据id查账户
*/
@Test
public void testSelectById() {
Account account = accountService.selectById(3);
System.out.println(account);
// Account{id=3, name='白豆五', money=7000.0}
}
}
项目结构: