spring第二天:spring基于注解的IOC以及IoC的案例
1. spring中ioc的常用注解
账户的业务层实现类
scope="" init-method="" destory-method=""
<property name="" value="" ref=""></property>
</bean>
用于创建对象的
他们的作用就和在XML配置文件中编写一个<bean标签实现的功能一样
@Component
: 作用: 用于把当前类对象存入Spring容器中
属性:value
: 用于指定bean的id,当不写value时,他的默认值是当前类名,且首字母该小写.
@Controller
: 一般用在表现层
@Service
: 一般用在业务层
@Repository
: 一般用在持久层
以上三个注解他们的作用和属性与@Compoent
是一模一样的
他们三个是Spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
用于注入数据的
他们的作用就和在XML文件中配置的bean标签中写一个<property标签的作用一样
@Autowired
: 自动按照类型注入.只要容器中有唯一的一个bean对象和要注入的变量类型匹配,就可以注入成功.
如果IOC容器中没有任何bean类型和要注入的变量类型匹配,则报错.
如果IOC容器中有多个类型匹配时
出现的位置:
可以是变量上,也可以是方法上
细节:
在使用注解注入时,set方法就不是必须的了.
@Qualifier
作用: 如果按照类型注入基础之上再按照名称注入.他在给成员注入时==不能单独使用==,但是在给参数注入时可以
属性:
value
: 用于指定注入bean的id
@Resource
: 这按照bean的id注入.他可以独立使用
属性: name: 用于指定bean的id
以上三个注入都只能注入其他bean类型的数据,而基本数据类型和String类型无法使用上述注解另外,集合类型的注入只能通过xml来实现
@Value
: 用于注入基本数据类型
和String
类型数据
属性: value
: 用于指定数据的值,他可以使用spring的SpEL(也就是spring中的el表达式)
SpEL的写法: $(表达式)
用于改变作用范围的
他们的作用就和在bean标签中使用scope属性实现的功能一样
@Scope
: 用于指定bean的作用范围
属性:value
:指定范围的取值.常用取值:singleton
,prototype
和生命周期相关的(了解)
他们的作用就和咋bean标签中国使用init-method和destory-method属性实现的功能一样
@PreDestroy
: 用于指定销毁方法
@PostConstruct
: 用于指定初始化方法
2. spring 基于xml配置的 IoC 的实现账户的CRUD
2.1 需求和技术要求
2.1.1 需求
实现账户的 CRUD 操作
2.1.2 技术要求
使用 spring 的 IoC 实现对象的管理
使用 DBAssit 作为持久层解决方案
使用 c3p0 数据源
2.2 环境搭建
2.2.1 添加依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>spring-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
2.2.2 创建数据库和编写实体类
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)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);
//账户的实体类
class Account implements Serializable {
private Integer id;
private String name;
private Float 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 Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
2.2.3 编写持久层代码
/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
*/
void deleteAccount(Integer accountId);
}
/**
* 账户的DAO层实现类
* <bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl"></bean>
*/
public class AccountDaoImpl implements IAccountDao {
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<Account> findAllAccount() {
try {
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public Account findAccountById(Integer accountId) {
try {
return runner.query("select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void saveAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
} catch (SQLException e) {
e.printStackTrace();
}
}
public void updateAccount(Account account) {
try {
runner.update("update account set name = ? , money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
public void deleteAccount(Integer accountId) {
try {
runner.update("delete from account where id = ?",accountId);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.2.4 编写业务层代码
/**
* 账户业务层的接口
*/
public interface IAccountService {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
*/
void deleteAccount(Integer accountId);
}
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
public void setAccountDao(AccountDaoImpl accountDao) {
}
}
2.2.5 创建并编写配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置Service -->
<bean id="accountService" class="org.example.service.impl.AccountServiceImpl">
<!-- 注入dao-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置Dao-->
<bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl">
<property name="runner" ref="runner"></property>
</bean>
<!-- 配置QueryRunner-->
<bean id = "runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源 -->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy?useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
2.2.6 测试类代码
/**
* 使用Junit单元测试:测试我们的配置
*/
public class AccountTest {
@Test
public void testFindAll() {
//1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
//3. 执行方法
List<Account> accounts = accountService.findAllAccount();
for(Account account : accounts){
System.out.println(account);
}
}
@Test
public void testFindOne() {
//1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
//3. 执行方法
Account account = accountService.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave() {
//1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
//3. 执行方法
Account account = new Account();
account.setName("米老鼠");
account.setMoney(2000.0f);
accountService.saveAccount(account);
testFindAll();
}
@Test
public void testUpdate() {
//1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
//3. 执行方法
Account account = new Account();
account.setId(5);
account.setName("米老鼠B");
account.setMoney(2000.0f);
accountService.updateAccount(account);
testFindAll();
}
@Test
public void testDelete() {
//1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
//3. 执行方法
accountService.deleteAccount(4);
testFindAll();
}
}
2.2.7 分析测试了中的问题
通过上面的测试类,我们可以看出,每个测试方法都重新获取了一次 spring 的核心容器,造成了不必要的重复代码,增加了我们开发的工作量。这种情况,在开发中应该避免发生。
有些同学可能想到了,我们把容器的获取定义到类中去。例如:
public class AccountServiceTest {
private ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
private IAccountService as = ac.getBean("accountService",IAccountService.class);
}
这种方式虽然能解决问题,但是扔需要我们自己写代码来获取容器。
能不能测试时直接就编写测试方法,而不需要手动编码来获取容器呢?
请在今天的最后一章节找答案。
第3章 基于注解的 IOC 配置
3.1 明确:写在最前
学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用 xml 还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
关于实际的开发中到底使用 xml 还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
我们在讲解注解配置时,采用上一章节的案例,把 spring 的 xml 配置内容改为使用注解逐步实现。
3.2 环境搭建
3.2.1 添加依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>spring-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
3.2.2 创建数据库和编写实体类
package org.example;
import org.springframework.context.annotation.Bean;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Float 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 Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
3.2.3 编写持久层代码
/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
*/
void deleteAccount(Integer accountId);
}
/**
* 账户的DAO层实现类
* <bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl"></bean>
*/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<Account> findAllAccount() {
try {
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public Account findAccountById(Integer accountId) {
try {
return runner.query("select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void saveAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
} catch (SQLException e) {
e.printStackTrace();
}
}
public void updateAccount(Account account) {
try {
runner.update("update account set name = ? , money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
public void deleteAccount(Integer accountId) {
try {
runner.update("delete from account where id = ?",accountId);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.2.4 编写业务层代码
package org.example.service;
import org.example.Account;
import java.util.List;
/**
* 账户业务层的接口
*/
public interface IAccountService {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
*/
void deleteAccount(Integer accountId);
}
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
public void setAccountDao(AccountDaoImpl accountDao) {
}
}
3.2.5 创建并编写配置类
/**
* 该类是一个配置类
* spring中的新注解
*@Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写.
*@ComponentScan :通过注解指定spring在创建容器时要扫描的包
* 作用: 用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value: 它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包
* 我们使用此注解就等同于在xml中配置了
* <context:component-scan base-package="org.example"/>
* @Bean
* 作用: 用于把当前方法的返回值作为bean对象存入spring的ioc中
* 属性:
* name: 用于指定bean的id.当不写时,默认值是当前方法的名称
* 细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
* 查找方式和Autowired注解的作用是一样的
*
* @Import() 用于导入其他的配置类
* @Import({SpringConfiguration.class})
* 属性:
* value: 用于指定其他配置类的字节码
* 当我们使用Import的注释之后,有Import注释的类就是父配置类,而导入的到时子配置类
*
* @PropertySource : 用于指定properties文件的位置
* 属性 value : 指定文件的名称了路径.
* 关键字: classpath,表示类路径下
*/
@Configuration
@ComponentScan("org.example")
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 用于创建一个QueryRunner对象
* @param dataSources
* @return
*/
@Bean(name = "runner")
@Scope("prototype") //多例
public QueryRunner createQueryRunner(DataSource dataSources){
return new QueryRunner(dataSources);
}
/**
* 创建数据源对象
* @return
*/
@Bean(name = "dataSource")
public DataSource creatDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
3.2.6 使用Junit单元测试
/**
* 使用Junit单元测试:测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountTest {
@Autowired
private IAccountService accountService;
@Test
public void testFindAll() {
/* //1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2. 得到业务层对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
*/
//3. 执行方法
List<Account> accounts = accountService.findAllAccount();
for(Account account : accounts){
System.out.println(account);
}
}
@Test
public void testFindOne() {
// //1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//
// //2. 得到业务层对象
// IAccountService accountService = (IAccountService) ac.getBean("accountService");
// //3. 执行方法
Account account = accountService.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave() {
// //1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//
// //2. 得到业务层对象
// IAccountService accountService = (IAccountService) ac.getBean("accountService");
// //3. 执行方法
Account account = new Account();
account.setName("米老鼠");
account.setMoney(2000.0f);
accountService.saveAccount(account);
testFindAll();
}
@Test
public void testUpdate() {
// //1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//
// //2. 得到业务层对象
// IAccountService accountService = (IAccountService) ac.getBean("accountService");
// //3. 执行方法
Account account = new Account();
account.setId(5);
account.setName("米老鼠B");
account.setMoney(2000.0f);
accountService.updateAccount(account);
testFindAll();
}
@Test
public void testDelete() {
// //1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//
// //2. 得到业务层对象
// IAccountService accountService = (IAccountService) ac.getBean("accountService");
// //3. 执行方法
accountService.deleteAccount(4);
testFindAll();
}
}