此处 @Spring知识点整合(中) 和 @Spring知识点整合(下)
我的环境
idea 2019.1
jdk 1.8
Spring 5
Lombok插件 这里是使用和安装链接(包括idea和eclipse) @简单粗暴节省JavaBean代码插件 Lombok.jar
下2篇是 @ 12 - 15 和 @ 16 - 18
1.Spring创建对象是单例还是多例 以及 ApplicationContext 的三个常用实现类
接口 ApplicationFactory 创建对象是单例的 推荐使用
接口 BeanFactory 创建对象 是多例的
ApplicationContext 的三个常用实现类
ClassPathXmlApplicationContext 可以加载类路径下的配置文件 要求配置文件必须在类的路径下 否则加载不了
FileSystemXmlApplicationContext 可以加载本地的文件,任意磁盘路径(必须有访问权限)
AnnotationConfigApplicationContext 用于读取注解创建容器
2.Spring创建对象的三种方式
<!-- 把对象交给Spring来管理 -->
<!-- Spring对bean的管理细节
1.创建bean的三种方式
2.bean对象的作用范围
3.bean对象的生命周期
-->
<!--
创建bean的三种方式
第一种方式:使用默认构造函数创建
在spring的配置文件中使用bean标签,配置id和class属性之后.且没有其他的标签的时候,
采用的就是默认构造函数创建bean对象,此时如果bean中没有默认构造函数,就会报错
-->
<bean id="accountService" class="cn.icanci.service.impl.AccountServiceImpl"></bean>
<bean id="account" class="cn.icanci.dao.domain.Account"></bean>
<!--
第二种方式:使用普通工长方法中的对象(使用某个类中的方法创建对象,并存入spring容器) 如下
-->
<bean id="userFactory" class="cn.icanci.dao.domain.Account"></bean>
<bean id="user" factory-bean="userFactory" factory-method="canConvert"></bean>
<!--
第三种方式:使用静态方法中的的静态方法创建对象
-->
<bean id="user" class="cn.icanci.dao.domain.Account" factory-method="getUser"></bean>
3.Bean对象的作用范围的调整
<!-- bean的作用域的调整
bean标签的scope属性,就是用于指定bean的作用范围
值:
singleton(默认值,就是单例的)
prototype 不是单例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境的时候,就是session
-->
<bean id="accountBean" class="cn.icanci.dao.domain.Account" scope="singleton"></bean>
4.Bean对象的生命周期
bean对象的生命周期:
单例对象:
出生:当容器创建的时候对象出生
活着:只要容器还在,对象就一直活着
死亡:容器销毁,对象消亡
总结:单例对象跟随容器的创建和销毁
多例对象:
出生:使用的时候才创建
活着:对象只要使用就一直活着
死亡:当对象长时间不用,由Java的垃圾回收机制销毁
5.Spring中的依赖注入
<!--
Spring中的依赖注入
依赖注入:
Dependency Injection
IOC的作用:
降低程序之间的耦合(依赖关系)
依赖关系的管理:
以后都交给spring来管理
在当前类所需要的对象,由Spring为我们提供,我们只需要在配置文件中说明
依赖关系的维护
就称为依赖注入
依赖注入:
能注入的数据,有三类:
基本类型和String
其他bean类型(在配置文件中或者注解配置过的bean)
复杂类型/注解类型
注入的方式.有三种:
第一种:使用构造函数提供
第二种:使用set方法提供
第三种:使用注解提供
-->
<!-- 构造函数注入
使用的标签是:constructor-arg
标签出现的位置:bean的内部
标签中的属性:
type:用于指定要注入的数据类型,也就是构造函数中的某个或者某些数据类型
index:用于指定要注入的数据给构造函数指定索引的参数赋值,索引的位置从0开始
name:用于指定构造函数中的名称 最常用
ref:引用关联的对象 其他的bean类型,也就是bean对象
value:值 为String类型和基本数据类型
特点:
优势:在获取bean对象的时候,注入数据必须的操作,否则对象无法创建bean对象
缺点:在创建对象的时候,必须提供,即使用不到
-->
<bean id="accoutService" class="cn.icanci.service.impl.AccountServiceImpl">
<constructor-arg name="username" value="icani"></constructor-arg>
<constructor-arg name="password" value="icanci"></constructor-arg>
<constructor-arg name="date" ref="now"></constructor-arg>
</bean>
<!--
配置一个日期对象
-->
<bean id="now" class="java.util.Date"></bean>
<!--
set方法注入
涉及的标签:property
出现的位置:bean里面
标签的属性:
name:用于指定注入的时候的方法set方法的名称
value:用于提供基本数据类型和String类型的参数
ref:用于指定其他bean的类型数据
优势:
创建对象的时候没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须优有值,我们无法保证set方法有值
经常使用
-->
<bean id="accountService2" class="cn.icanci.service.impl.AccountServiceImpl">
<property name="username" value="haxi" ></property>
<property name="password" value="12"></property>
<property name="date" ref="now"></property>
</bean>
测试
package cn.icanci.service.impl;
import cn.icanci.service.IAccountService;
import lombok.Data;
import java.util.*;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/4 23:23
* @ClassAction:
*/
@Data
public class AccountServiceImpl implements IAccountService {
//如果是经常变化的数据,并不适用于注入的方式
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties properties;
@Override
public void saveAccount() {
System.out.println("AccountServiceImpl.saveAccount");
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(properties);
}
}
bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签
list array set
用于给Map结构注入的标签
map props
结构相同,标签可以互换
-->
<bean id="accountService" class="cn.icanci.service.impl.AccountServiceImpl">
<property name="myStrs">
<array value-type="java.lang.String">
<value>AAA</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>
<property name="myList">
<list value-type="java.lang.String">
<value>哈希</value>
<value>demo</value>
</list>
</property>
<property name="mySet">
<list value-type="java.lang.String">
<value>哈希</value>
<value>demo</value>
</list>
</property>
<property name="myMap">
<map value-type="java.lang.String">
<entry key="1">
<value>bbb</value>
</entry>
<entry key="2">
<value>bbb</value>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="1">aaa</prop>
<prop key="2">333</prop>
</props>
</property>
</bean>
</beans>
6.常见的IOC注解以及其分类和作用
1.用于创建对象的 (此处需要配置 bean.xml的配置文件 告诉Spring创建Spring容器的时候需要扫描的包)
导入约束:bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
告知spring容器在创建要扫描的包.配置所需要的标签不是在beans的约束中,
而是一个名称为context名称空间和约束中
-->
<context:component-scan base-package="cn.icanci"></context:component-scan>
</beans>
他们的作用就是和在bean.xml中编写一个bean标签是一样的
@Componet:
用于把当前对象存储如到Spring容器中
属性:value 当我们不写的时候,默认就是类名的首字母小写
@Controller 表现层
@Service 业务层
@Repository 持久层
以上三个注解他们的作用和属性和Component是一样的
他们是spring框架为我们提供的三层架构的注解
测试:
package cn.icanci.service.impl;
import cn.icanci.service.IAccountService;
import org.springframework.stereotype.Component;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
}
}
package cn.icanci.ui;
import cn.icanci.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.ui
* @Date: Created in 2020/1/6 13:23
* @ClassAction: 测试bean的IOC
*/
public class Client {
/**
* 获取Spring获取
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext as = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) as.getBean("accountServiceImpl");
System.out.println(accountService);
System.out.println("-------------------------------------------------");
accountService.saceAccount();
}
}
打印的结果
cn.icanci.ui.Client
cn.icanci.service.impl.AccountServiceImpl@5427c60c
-------------------------------------------------
AccountServiceImpl.saceAccount
Process finished with exit code 0
2.用于注入数据的
他们的作用和在beam里面写一个<property></property>
Autowired
作用:自动按照类型注入,只要容器中有一个唯一的bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的类型匹配,就报错,
如果有多个,需要指定id,此时变量名就是唯一id
出现位置:
可以是变量上,也可以是方法上
细节:
在使用注解注入的时候,set方法不是必须的了
Qualifier:
作用:在按照类中的注释的基础之上在按照名称注入,
它给类成员注入时候不能单独使用,但是在给方法注入的时候可以
属性:
value:用于指定注入bean的id
Resource:
作用:直接按照bean的id使用,可以独立使用
属性:
name:用于指定bean的id
以上注解都只能注入其他bean的类型,而基本数据类型和String类型不能使用上述注解
另外,集合类型的注入只能使用XML实现
value注解:
作用:用于注入基本数据类型和String类型的数据
属性:
value:用于指定数据的值,它可以使用spring中的SpEL
SpEL的写法:${表达式}
测试(是上一个例子进行修改):
bean.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
https://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="cn.icanci"></context:component-scan>
<bean id="accountDao" class="cn.icanci.dao.impl.AccountDaoImpl"></bean>
</beans>
AccountDaoImpl.java
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction:
*/
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("AccountDaoImpl.saveAccount");
}
}
AccountServiceImpl.java
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
accountDao.saveAccount();
}
}
测试类:
package cn.icanci.ui;
import cn.icanci.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.ui
* @Date: Created in 2020/1/6 13:23
* @ClassAction: 测试bean的IOC
*/
public class Client {
/**
* 获取Spring获取
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext as = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) as.getBean("accountServiceImpl");
System.out.println(accountService);
System.out.println("-------------------------------------------------");
accountService.saceAccount();
}
}
打印结果:
cn.icanci.service.impl.AccountServiceImpl@3bd94634
-------------------------------------------------
AccountServiceImpl.saceAccount
AccountDaoImpl.saveAccount
如果此处不使用xml配置bean,使用注解,只需要更改3个地方
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import org.springframework.stereotype.Service;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction:
*/
@Service("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("AccountDaoImpl.saveAccount");
}
}
修改bean.xml 删除掉
<bean id="accountDao" class="cn.icanci.dao.impl.AccountDaoImpl"></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"
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">
<context:component-scan base-package="cn.icanci"></context:component-scan>
</beans>
为AccountDaoImpl.java添加注解 @Service("accountDao")
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import org.springframework.stereotype.Service;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction:
*/
@Service("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("AccountDaoImpl.saveAccount");
}
}
在AccountServiceImpl.java 给 变量添加注解 @Autowired
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
accountDao.saveAccount();
}
}
此时打印的结果:
cn.icanci.service.impl.AccountServiceImpl@7181ae3f
-------------------------------------------------
AccountServiceImpl.saceAccount
AccountDaoImpl.saveAccount
华丽的分割线之下是Qualifier注解的使用
复制一个AccountDao实现类
实现类1
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import org.springframework.stereotype.Service;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction:
*/
@Service("accountDao1")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("AccountDaoImpl.saveAccount");
}
}
实现类2
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import org.springframework.stereotype.Service;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction:
*/
@Service("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("AccountDaoImpl.saveAccount");
}
}
此时运行测试类:会报错
解决方案1:修改AccountServiceImpl.java代码 如下
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao1;
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
accountDao1.saveAccount();
}
}
此时运行测试:
但是这不是我们想要的结果,方案2:修改AccountServiceImpl.java 添加注解 @Qualifier("accountDao1")
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao;
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
accountDao.saveAccount();
}
}
运行测试的结果:
注意:
Qualifier 在给类成员注入的时候必须和 Autowired 一起使用 否则报错
居然需要添加两个标签.所以此时,解决方案3:使用 @Resource注解
修改 AccountServiceImpl.java
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.service.IAccountService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction:
*/
@Component
public class AccountServiceImpl implements IAccountService {
@Resource(name="accountDao1")
private IAccountDao accountDao;
@Override
public void saceAccount() {
System.out.println("AccountServiceImpl.saceAccount");
accountDao.saveAccount();
}
}
注意点:此时不是value了,而是name
测试结果:
上述注解只能注入其他bean类型数据,基本数据类型和String类型无法注入,同时集合类型只能通过xml配置
Value注解 :用于注入基本数据类型和String类型的数据
属性:用于指定数据的值
3.用于改变作用范围的
用于改变作用范围的
和在bena标签里面使用scope是一样的
scope:
作用:指定baen的范围
value:指定取值范围,常用取值:singleton prototype
默认不写就是单例的 singleton 注解写在类上
4.和生命周期相关 了解
在bean标签中使用init-method和destroy-method是一样的
PreDestroy 销毁的注解
用于指定销毁的方法
PostConstruct 初始化的注解
用于指定初始化的方法
7.这里插入一个案例 使用Spring注解和xml以及c3p0实现CRUD
第一步:创建maven工程 导入坐标 pom.xml
<?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>cn.icanci</groupId>
<artifactId>spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
第二步:创建数据库以及表,并且增加数据 account.sql
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);
第三步:创建实体类 Account.java
注意:我这里使用了一个插件 Lombok 怎么使用和安装这里是链接 @简单粗暴节省JavaBean代码插件 Lombok.jar
package cn.icanci.domain;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.domain
* @Date: Created in 2020/1/9 17:38
* @ClassAction: Account的JavaBean
*/
@Data
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
}
第四步:创建dao层 IAccountDao.java AccountDaoImpl.java
IAccountDao.java
package cn.icanci.dao;
import cn.icanci.domain.Account;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao
* @Date: Created in 2020/1/9 16:26
* @ClassAction:
*/
public interface IAccountDao {
/**
* 查询所有账户
* @return 返回List集合 泛型为 Account
*/
List<Account> findAllAccount();
/**
* 根据id查找一个
* @param id 需要查找的id
* @return 返回Account对象
*/
Account findAccountById(Integer id);
/**
* 保存一个账户
* @param account 需要保存的账户的对象
*/
void saveAccount(Account account);
/**
* 更新一个账户的信息
* @param account 需要更新的账户对象
*/
void updateAccount(Account account);
/**
* 删除一个账户信息
* @param accountId 需要删除的账户的id
*/
void deleteById(Integer accountId);
}
AccountDaoImpl.java
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.domain.Account;
import lombok.Setter;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction: AccountDao的持久层CRUD
*/
public class AccountDaoImpl implements IAccountDao {
@Setter
private QueryRunner runner;
@Override
public List<Account> findAllAccount() {
try {
return runner.query("select * from account", new BeanListHandler<Account>(Account.class));
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id = ?",new BeanHandler<>(Account.class),id);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public void saveAccount(Account account) {
try {
runner.update("insert into account (name,money) values(?,?)",account.getName(),account.getMoney());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void updateAccount(Account account) {
try {
runner.update("update account set name = ?,money=? where id = ?",account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void deleteById(Integer accountId) {
try {
runner.update("delete from account where id = ?",accountId);
}catch (Exception e){
e.printStackTrace();
}
}
}
第五步:创建service层 IAccountService.java AccountServiceImpl.java
IAccountService.java
package cn.icanci.service;
import cn.icanci.domain.Account;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service
* @Date: Created in 2020/1/6 13:20
* @ClassAction: AccountService的顶级接口 账户的业务层接口
*/
public interface IAccountService {
/**
* 查询所有账户
* @return 返回List集合 泛型为 Account
*/
List<Account> findAllAccount();
/**
* 根据id查找一个
* @param id 需要查找的id
* @return 返回Account对象
*/
Account findAccountById(Integer id);
/**
* 保存一个账户
* @param account 需要保存的账户的对象
*/
void saveAccount(Account account);
/**
* 更新一个账户的信息
* @param account 需要更新的账户对象
*/
void updateAccount(Account account);
/**
* 删除一个账户信息
* @param accountId 需要删除的账户的id
*/
void deleteById(Integer accountId);
}
AccountServiceImpl.java
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.domain.Account;
import cn.icanci.service.IAccountService;
import lombok.Setter;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction: 账户的CRUD 的实现类 业务层调用持久层
*/
public class AccountServiceImpl implements IAccountService {
@Setter
private IAccountDao accountDao;
@Override
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteById(Integer accountId) {
accountDao.deleteById(accountId);
}
}
第六步:编写 bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置service-->
<bean id="accountService" class="cn.icanci.service.impl.AccountServiceImpl">
<!-- 注入Dao-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置accountDao对象-->
<bean id="accountDao" class="cn.icanci.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner-->
<property name="runner" ref="runner"></property>
</bean>
<!-- 配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源-->
<constructor-arg name="ds" ref="ds"></constructor-arg>
</bean>
<!-- 配置数据源-->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 来凝结数据库的信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/icanci"></property>
<property name="user" value="root"></property>
<property name="password" value="ok"></property>
</bean>
</beans>
第七步:测试 AccountServiceTest.java
package cn.icanci.test;
import cn.icanci.domain.Account;
import cn.icanci.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.test
* @Date: Created in 2020/1/9 18:14
* @ClassAction: AccountService 的单元测试.测试配置
*/
public class AccountServiceTest {
private IAccountService accountService ;
@Test
public void testFindAll() {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
accountService = (IAccountService) ac.getBean("accountService");
//执行业务
List<Account> listAccount = accountService.findAllAccount();
for (Account account : listAccount) {
System.out.println(account);
}
}
@Test
public void testFindOne() {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
accountService = (IAccountService) ac.getBean("accountService");
//执行业务
System.out.println(accountService.findAccountById(1));
}
@Test
public void testSave() {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
accountService = (IAccountService) ac.getBean("accountService");
//执行业务
Account account = new Account();
account.setName("haxi");
account.setMoney(12222F);
accountService.saveAccount(account);
}
@Test
public void testUpdate() {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
accountService = (IAccountService) ac.getBean("accountService");
//执行业务
Account account = accountService.findAccountById(4);
account.setName("hashTable");
account.setMoney(12222F);
accountService.updateAccount(account);
System.out.println("----------------------------------------------------------------");
testFindAll();
}
@Test
public void testDelete() {
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//获取对象
accountService = (IAccountService) ac.getBean("accountService");
//执行业务
testFindAll();
accountService.deleteById(4);
System.out.println("----------------------------------------------------------------------");
testFindAll();
}
}
测试结果:
8.这里插入一个案例 使用Spring注解和xml以及c3p0实现CRUD 但是使用注解的方式配置Service和Dao 这里使用第7点的项目,我只把修改的代码放下
bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring在创建容器的时候需要扫描的包-->
<context:component-scan base-package="cn.icanci"></context:component-scan>
<!-- 配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源-->
<constructor-arg name="ds" ref="ds"></constructor-arg>
</bean>
<!-- 配置数据源-->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 来凝结数据库的信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/icanci"></property>
<property name="user" value="root"></property>
<property name="password" value="ok"></property>
</bean>
</beans>
AccountServiceImpl.java
package cn.icanci.service.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.domain.Account;
import cn.icanci.service.IAccountService;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.service.impl
* @Date: Created in 2020/1/6 13:21
* @ClassAction: 账户的CRUD 的实现类 业务层调用持久层
*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteById(Integer accountId) {
accountDao.deleteById(accountId);
}
}
AccountDaoImpl.java
package cn.icanci.dao.impl;
import cn.icanci.dao.IAccountDao;
import cn.icanci.domain.Account;
import lombok.Setter;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.dao.impl
* @Date: Created in 2020/1/9 16:27
* @ClassAction: AccountDao的持久层CRUD
*/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
@Override
public List<Account> findAllAccount() {
try {
return runner.query("select * from account", new BeanListHandler<Account>(Account.class));
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id = ?",new BeanHandler<>(Account.class),id);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public void saveAccount(Account account) {
try {
runner.update("insert into account (name,money) values(?,?)",account.getName(),account.getMoney());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void updateAccount(Account account) {
try {
runner.update("update account set name = ?,money=? where id = ?",account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void deleteById(Integer accountId) {
try {
runner.update("delete from account where id = ?",accountId);
}catch (Exception e){
e.printStackTrace();
}
}
}
测试类不变.测试一下:(这里我就只放了一个测试的图,其他的测试都是通了的)
9.Spring的新注解 这里继续使用上面第8点的项目
/**
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 此处使用的是dbutils连接池
* ComponentScan
* 作用:用于通过注解指定spring在创建容器的时候需要扫描的包
* 属性:
* value:她和basePackages的作用是一样的,都是用于指定创建容器需要扫描的包
* bean
* 作用:用于把当前方法的返回值作为bean对象存入到ioc容器中去
*属性:
* name:用于指定bean的id,当不写的时候,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法的时候.如果方法有参数,spring框架会去容器中找有没有可用的bean对象
* 查找的方式和Autowired注解的作用是一样的
* Import
* 作用:用于导入其他的配置类
* 属性:
* value 用于指定其他的配置类的字节码
* 当我们使用Import的那个类,就是主配置类
* PropertySource
* 属性:
* value:就是配置文件的路径
* PropertySource("classpath:....")
*
*/
现在去除掉bean.xml文件使用Java类作为配置文件
父配置类 SpringConfiguration.java
package cn.icanci.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.config
* @Date: Created in 2020/1/9 19:25
* @ClassAction: 作用 和 bean.xml的作用是一样的 是一个配置类
*/
//@Configuration
@ComponentScan(basePackages = "cn.icanci")
@Import(JdbcConfiguration.class)
public class SpringConfiguration {
}
子配置类
package cn.icanci.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.config
* @Date: Created in 2020/1/9 19:52
* @ClassAction: JDBC 链接数据库相关的配置类
*/
//@Configuration
public class JdbcConfiguration {
/**
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
*
* @return 返回数据源队长
*/
@Bean(name = "dataSource" )
public DataSource createDataSource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/icanci");
ds.setUser("root");
ds.setPassword("ok");
} catch (Exception e) {
e.printStackTrace();
}
return ds;
}
}
测试: 此处测试和上一点一致
10.Spring的新注解 代码部分优化 这里继续使用上面第8点的项目
增加 jdbcConfig.properties 配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/icanci
jdbc.username=root
jdbc.password=ok
JdbcConfiguration.java
package cn.icanci.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.config
* @Date: Created in 2020/1/9 19:52
* @ClassAction: JDBC 链接数据库相关的配置类
*/
//@Configuration
public class JdbcConfiguration {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
*
* @return 返回数据源对象
*/
@Bean(name = "dataSource")
public DataSource createDataSource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driverClass);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
} catch (Exception e) {
e.printStackTrace();
}
return ds;
}
}
SpringConfiguration.java
package cn.icanci.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.config
* @Date: Created in 2020/1/9 19:25
* @ClassAction: 作用 和 bean.xml的作用是一样的 是一个配置类
*/
//@Configuration
@ComponentScan(basePackages = "cn.icanci")
@Import(JdbcConfiguration.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
测试结果:
如果使用两个数据源,名字id不一样,怎么写?这是细节
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource) {
return new QueryRunner(dataSource);
}
package cn.icanci.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.config
* @Date: Created in 2020/1/9 19:52
* @ClassAction: JDBC 链接数据库相关的配置类
*/
//@Configuration
public class JdbcConfiguration {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource) {
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
*
* @return 返回数据源对象
*/
@Bean(name = "ds1")
public DataSource createDataSource1() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driverClass);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
} catch (Exception e) {
e.printStackTrace();
}
return ds;
}
/**
* 创建数据源对象
*
* @return 返回数据源对象
*/
@Bean(name = "ds2")
public DataSource createDataSource2() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driverClass);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
} catch (Exception e) {
e.printStackTrace();
}
return ds;
}
}
11.Spring整合Junit
第一步:导入Spring整合Junit的jar(坐标)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
第二步:使用Junit提供的注解把原有的main方法(此处Junit内部有个main方法),替换为spring的 @Runwith
@RunWith(SpringJUnit4ClassRunner.class)
第三步:告知Spring的运行器. Spring和IOC创建是基于xml还是基于注解的,并且说明位置 @ContextConfiguration
localtions:指定xml文件的位置,加上classpath关键字.表示在类路径下
classes:指定注解类所在的位置
现在来看代码:其他代码和上一个点一致
package cn.icanci.test;
import cn.icanci.config.SpringConfiguration;
import cn.icanci.domain.Account;
import cn.icanci.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* @Author: icanci
* @ProjectName: spring
* @PackageName: cn.icanci.test
* @Date: Created in 2020/1/9 18:14
* @ClassAction: AccountService 的单元测试.测试配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
@Autowired
private IAccountService accountService;
@Test
public void testFindAll() {
//执行业务
List<Account> listAccount = accountService.findAllAccount();
for (Account account : listAccount) {
System.out.println(account);
}
}
@Test
public void testFindOne() {
//执行业务
System.out.println(accountService.findAccountById(1));
}
@Test
public void testSave() {
//执行业务
testFindAll();
Account account = new Account();
account.setName("haxi哈哈123");
account.setMoney(12222F);
accountService.saveAccount(account);
testFindAll();
}
@Test
public void testUpdate() {
//执行业务
Account account = accountService.findAccountById(7);
account.setName("hashTable");
account.setMoney(12222F);
accountService.updateAccount(account);
System.out.println("----------------------------------------------------------------");
testFindAll();
}
@Test
public void testDelete() {
//执行业务
testFindAll();
accountService.deleteById(7);
System.out.println("------------------------------------------------------------------");
testFindAll();
}
}
现在来测试一下:
居然挂了,为什么?
因为我用的是Spring5.默认Spring5不再支持Junit4.12以下版本,而我用的是Junit4.10
现在我修改为 4.12版本就可以了
测试一下:成功.其他的测试也通过了
因为本篇幅有点多,所以我分为两部分,可能看起来相对不累一些.
欢迎小伙伴和俺一起学习呀 有啥问题请提出 此处 @我自己 这是QQ聊天的链接嗷 随时欢迎
此处 @Spring知识点整合(中) 和 @Spring知识点整合(下)