目录
- Spring框架开发方式
- 前言
- 具体案例
- 导入依赖
- 创建数据库表结构
- 创建实体类
- 编写持久层接口和实现类
- 编写业务层接口和实现类
- 配置文件的编写
- IoC注解开发
- 注解开发入门(半注解)
- IoC常用注解
- Spring纯注解方式开发
- Spring整合JUnit测试
Spring框架开发方式
前言
Spring开发主要依赖的就是IoC控制反转思想,将对象的创建权利移交给Spring框架,对各个模块之间进行解耦,实现方式就是DI——依赖注入,这里不清楚的可以看【Spring框架】Spring核心思想IoC以及依赖注入DI详解-CSDN博客这篇文章。
具体案例
我们创建我们项目的大致结构:实体类+业务层+持久层+测试类,这里我们为了清楚的理解Spring框架的开发方式,在持久层方面我们不引入MyBatis进行数据注入,而是选择原始的JDBC程序。好了我们先创建一个最普通的Maven项目,并引入我们的必要依赖(不会创建Maven项目可以看:【Maven】一篇带你了解Maven项目管理工具-CSDN博客):
导入依赖
<dependencies>
<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- slf4j接口https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
<!-- log4j核心https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.24.0</version>
</dependency>
<!-- log4j2绑定到slf4j接口进行实现https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.24.0</version>
<scope>test</scope>
</dependency>
<!-- JUnit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
</dependencies>
创建数据库表结构
create database spring_db;
use spring_db;
create table account(
id int primary key auto_increment,
name varchar(40),
money double
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
创建实体类
public class Account {
private Integer id;
private String name;
private Double money;
public Account() {
}
public Account(String name, Double money, Integer id) {
this.name = name;
this.money = money;
this.id = id;
}
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 +
'}';
}
}
这里我们创建一个简单的账户类,和我们的数据库表结构向对应,有id、姓名、余额三个属性。
编写持久层接口和实现类
接口
public interface AccountDao {
// 查询所有账户
List<Account> findAll();
}
实现类
这里和之前没有引入Spring框架的时候是有区别的,我们可以将数据库连接池对象的创建权利移交给Spring框架,我们只需要在持久层注入连接池对象就可以进行使用,不需要再去new一个对象,我们来看一下对比:
在没有引入Spring之前:
public class AccountDaoImpl implements AccountDao {
/**
* 查询所有的数据
* @return
*/
@Override
public List<Account> findAll() {
// 手动创建连接池对象
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 业务逻辑
System.out.println("业务逻辑...");
return list;
}
}
我们通过手动创建链接池对象的方式进行数据库连接,也就是new一个新的连接池对象。
引入Spring之后:
public class AccountDaoImpl implements AccountDao {
// 注入连接池对象
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 查询所有的数据
* @return
*/
@Override
public List<Account> findAll() {
/*
不再使用手动的方式进行创建连接池对象
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jc.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
*/
System.out.println("业务逻辑...");
return list;
}
}
在引入Spring之后,我们只需要通过Spring注入bean对象就可以了,不需要每次创建新的持久层实现类的时候都去重复连接数据库。
编写业务层接口和实现类
public interface AccountService {
// 查询所有用户
List<Account> findAll();
}
同样的,在这里我们不需要再手动去创建持久层对象,我们只需要通过Spring框架创建对象,并进行依赖注入,就可以完成此功能:
public class AccountServiceImpl implements AccountService {
// 依赖注入
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 查询所有的数据
*/
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
现在我们的业务层和持久层逻辑都已经编写好了,并注入了相关依赖,但是我们这些依赖去哪里拿呢?我们需要一个配置文件:applicationConfig.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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Spring配置数据源 -->
<!-- 这里是将数据源的实例化交给Spring容器管理 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--管理bean-->
<bean id="accountService" class="com.qcby.service.Impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<bean id="accountDao" class="com.qcby.dao.Impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
最后我们编写我们的测试类,执行测试:
public class AccountServiceTest {
@Test
public void run1(){
// 通过读取我们的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取到对象
AccountService accountService = (AccountService) ac.getBean("accountService");
// 调用方法
List<Account> list = accountService.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
测试结果:
以上我们就完成了使用Spring框架来对项目的管理。
IoC注解开发
注解开发入门(半注解)
Spring框架通过配置文件来管理我们移交的对象,我们同样也可以丢弃配置文件,使用springframework
提供的注解来进行开发,比配置文件的方式更便捷,不需要在配置文件中再去配置SpringBean
。
再简单编写一个案例,不再添加实体类和持久层,这里我只写一个业务层的接口和实现类:
接口:
public interface RoomService {
void hello();
}
实现类:
在我们需要Spring管理的类上添加@Component
注解,这个注解的作用就相当于将这个类创建对象的权利移交给Spring框架去管理,也就是想当于我们配置文件中的:<bean id="rs" class="com.xxx.RoomService" />
@Component(value = "rs")
public class RoomServiceImpl implements RoomService {
@Override
public void hello() {
System.out.println("Hello IOC注解...");
}
}
开启注解扫描
我们加入了注解,但是此时我们的Spring框架并没有读取到,我们需要在配置文件中加入开启注解扫描,扫描我们加入注解的类所在的包:
<!--开启注解扫描 com.qcby 所有的包中的所有的类 -->
<context:component-scan base-package="com.qcby" />
如果不开启,就会提示我们找不到名为rs
的这个SpringBean
对象:
编写测试方法进行测试:
public class AnnotationTest {
/**
* 测试注解创建Bean对象
*/
@Test
public void run1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
RoomService roomService = (RoomService) applicationContext.getBean("rs");
roomService.hello();
}
}
IoC常用注解
注解 | 说明 |
---|---|
@Component | 用于实例化Bean对象 作用于实体类 |
@Controller | 用于实例化Bean对象 作用于web层 |
@Service | 用于实例化Bean对象 作用于service层 |
@Repository | 用于实例化Bean对象 作用于dao层 |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 必须和@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,使用name属性,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 声明Spring创建对象的模式 单例singleton|多例prototype |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 相当于init-method |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 destroy-method |
Spring纯注解方式开发
纯注解的方式是微服务架构开发的主要方式,所以非常重要。纯注解的目的就是要舍弃臃肿的配置文件,用相同作用的配置类进行代替。
首先编写我们的实体类:
@Component
public class Order {
@Value("小明")
private String name;
@Value("1000")
private Integer money;
public String getName() {
return name;
}
public Integer getMoney() {
return money;
}
@Override
public String toString() {
return "Order{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
编写持久层接口和实现类:
public interface OrderDao1 {
void saveOrder(String name,Integer money);
}
@Component(value = "odi")
public class OrderDao1Impl implements OrderDao1 {
// 注入dataSource
@Autowired
@Qualifier(value = "dataSource1")
private DataSource dataSource;
@Override
public void saveOrder(String name,Integer money) {
Connection connection = null;
PreparedStatement stmt = null;
ResultSet rs = null;
int num;
try {
connection = dataSource.getConnection();
String sql = "insert into account(name,money) values(?,?)";
stmt = connection.prepareStatement(sql);
stmt.setString(1, name);
stmt.setInt(2, money);
num = stmt.executeUpdate();
if (num > 0) {
System.out.println("插入数据成功");
} else {
System.out.println("插入数据失败");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
编写业务层接口和实现类:
public interface OrderService1 {
void saveOrder();
}
@Component(value = "aa")
public class OrderService1Impl implements OrderService1 {
@Autowired
@Qualifier(value = "odi")
private OrderDao1 orderDao1;
@Autowired
@Qualifier(value = "order")
private Order order;
private String name;
private Integer money;
// 这里要延迟加载一下,不然会报空指针异常
// 因为在注入order的时候,其中的name和money都还没注入进来
@PostConstruct
public void init() {
this.name = order.getName();
this.money = order.getMoney();
}
@Override
public void saveOrder(){
orderDao1.saveOrder(name, money);
}
}
编写配置类:
@Configuration
@ComponentScan(value = "com.qcby")
public class SpringConfig {
@Bean("dataSource1")
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("20020322");
return dataSource;
}
}
这里使用@Configuration
进行声明,声明这是一个配置类,并且用@ComponentScan
注解对包进行扫描,最后编写测试类
// 加载我们的配置类,代替application.xml文件
@ContextConfiguration(classes = SpringConfig.class)
public class demo1Test {
@Test
public void run(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
OrderService1 orderService = (OrderService1)applicationContext.getBean("aa");
orderService.saveOrder();
}
}
Spring整合JUnit测试
可以看到在测试类中,每次测试一个方法,我们都需要进行配置文件或者是配置类的读取,然后再通过依赖注入的方式获取到对象,最后通过对象对方法进行调用。Spring提供了整合Junit单元测试的技术,可以简化测试开发。
我们通过引入以下依赖,使用我们的Spring框架整合JUnit测试
<!-- Spring整合JUnit测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
我们通过在测试类上添加注解:@RunWith(SpringJUnit4ClassRunner.class)
来整合我们的JUnit测试,这个写法是固定的,当然你也可以通过配置文件的方式,在配置文件中添加对应的测试对象即可:
// 整合JUnit测试
@RunWith(SpringJUnit4ClassRunner.class)
// 加载我们的配置类,代替application.xml文件
@ContextConfiguration(classes = SpringConfig.class)
public class demo1Test {
// 测试对象注入
@Autowired
private OrderService1 orderService;
@Test
public void run(){
// ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
// OrderService1 orderService = (OrderService1)applicationContext.getBean("aa");
orderService.saveOrder();
}
}