说说创建Spring容器这几个类的功能
ClassPathXmlApplicationContext:在类路径下加载配置文件
FileSystemXmlApplicationContext:在文件系统下使用绝对路径加载配置文件
AnnotationConfigApplicationContext:加载注解的配置类
bean标签的属性有什么作用?
属性 | 说明 |
---|---|
id | 唯一标识 |
name | 可以有多个名字,使用逗号,空格,分号分隔 |
class | 类全名 |
scope | singleton:单例对象 prototype:多例对象 |
init-method | 初始化方法 |
destroy-method | 销毁方法,只对单例有效 |
lazy-init | 延迟加载 |
使用构造器注入的属性
描述 | |
---|---|
index | 索引位置,从0开始 |
name | 参数名字 |
type | 参数类型 |
value | 简单类型=八种基本类型+String |
ref | 注入引用类型 |
set注入的属性
描述 | |
---|---|
name | 参数名 |
value | 简单类型=八种基本类型+String |
ref | 注入引用类型 |
理解Spring相关注解的含义
设置扫描基包
创建对象的注解 | 说明 |
---|---|
@Component | 用于普通类 |
@Controller | 用于控制器 |
@Service | 用于业务类 |
@Repository | 用于持久层 |
依赖注入的注解 | 说明 |
---|---|
@Autowired | 1. 按类型匹配注入 2. 如果有多个类型,按名字 3. 如果有多个名字或找不到就抛出异常 |
@Qualifier | 按名字匹配 |
@Value | 读取配置文件,把值注入到属性中 |
对象范围与生命周期 | 说明 |
---|---|
@Scope | 单例或多例对象 |
@Lazy | 延迟加载 |
@PostConstruct | 初始化方法 |
@PreDestroy | 销毁方法 |
JdbcTemplate是Spring提供的一个模板类,它是对jdbc的封装。用于数据库持久层的操作,它的特点是:简单、方便。
它简化了JDBC的使用,并有助于避免常见错误。它执行核心的JDBC工作流程,我们只需要写SQL语句,并且从中获取结果就可以了。
JDBC中操作:
- 获取Connection
- 获取PreparedStatement
- 执行增删改查
- 查询还要获取ResultSet
- 处理异常:SQLException
- 关闭连接
使用JdbcTemplate就只要写增删改查的SQL语句就可以了
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`birthday` date DEFAULT NULL,
`sex` char(1) DEFAULT '男',
`address` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
);
insert into `student`(`username`,`birthday`,`sex`,`address`) values
('孙悟空','1980-10-24','男','花果山水帘洞'),
('白骨精','1992-11-12','女','白虎岭白骨洞'),
('猪八戒','1983-05-20','男','福临山云栈洞'),
('蜘蛛精','1995-03-22','女','盤丝洞');
select * from student;
pom.xml添加依赖
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.0.RELEASEversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.22version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
创建实体:Student.java
package com.itheima.entity;
import java.sql.Date;
public class Student {
private int id;
private String username;
//注:日期是java.sql.Date类型
private Date birthday;
private String sex;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
创建测试类
package com.itheima.test;
import com.itheima.entity.Student;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class TestStudent {
@Test
public void testFindOne() {
//1.创建数据源:使用Spring提供的数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUsername("root");
ds.setPassword("root");
ds.setUrl("jdbc:mysql:///day46");
ds.setDriverClassName("com.mysql.jdbc.Driver");
//2.创建JdbcTemplate对象,传入数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
//3.调用JdbcTemplate的方法访问数据库,查询一条记录。参数1:SQL语句,参数2:映射对象,参数3:要替换的一个或多个占位符
//RowMapper是一个接口,作用:将结果集封装成一个实体类对象。我们使用它提供的实现类
//BeanPropertyRowMapper是RowMap接口的实现类,前提:表的列名与实体类的属性名相同,参数就是要封装的实体类类型
Student student = jdbcTemplate.queryForObject("select * from student where id=?", new BeanPropertyRowMapper<>(Student.class), 1);
System.out.println(student);
}
}
1.表的字段名与类中的属性名相同,表的字段名大小写不区分。
2.表的字段名如果有多个单词使用下划线隔开,与Java中驼峰命名的属性相对应。
表中字段名 | 类对应的属性名 |
---|---|
name或NAME | name |
dept_name | deptName |
注:增删改使用的是同一个方法
@Test
public void testDelete() {
//1.创建数据源:使用Spring提供的数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUsername("root");
ds.setPassword("root");
ds.setUrl("jdbc:mysql:///day46");
ds.setDriverClassName("com.mysql.jdbc.Driver");
//2.创建JdbcTemplate对象,传入数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
//update方法:用于增删改的操作,参数1:SQL语句,参数2:替换占位符的值,返回影响的行数
int row = jdbcTemplate.update("delete from student where id=?", 3);
System.out.println("删除了" + row + "行记录");
}
JdbcTemplate类 | 说明 |
---|---|
public JdbcTemplate(DataSource dataSource) | 作用:创建模板对象 参数:数据源,从中获取连接对象 |
public int update(String sql, Object…args) | 作用:实现增删改的操作 参数1:SQL语句 参数2:替换占位符的值 返回值:返回影响的行数 |
RowMapper |
作用:查询1条记录 参数: 1) SQL语句 2) 指定映射关系的接口 3) 替换占位符的值 返回值:查询的那条记录的对象 |
接口的作用:
RowMapper接口中的方法 | 说明 |
---|---|
T mapRow(ResultSet rs, int rowNum) throws SQLException | 作用:将结果集封装成一个对象 参数: 1) 查询出来的结果集 2) 这是第几行 返回值:封装好的对象 |
BeanPropertyRowMapper类的构造方法 | 说明 |
---|---|
public BeanPropertyRowMapper(Class |
作用:实现了上面的接口 参数:要封装的实体类类型 |
在Spring容器中管理JdbcTemplate
快捷键:Ctrl+F12:显示当前类中所有的方法
配置applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql:///day46"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
beans>
编写测试代码
package com.itheima.test;
import com.itheima.entity.Student;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
* 从容器中获取JdbcTemplate对象直接使用
*/
public class TestDemo2 {
ClassPathXmlApplicationContext context;
JdbcTemplate jdbcTemplate;
//在每个测试方法前调用
@Before
public void init() {
//创建Spring容器
context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取模板对象
jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
}
//添加操作
@Test
public void testAdd() {
int row = jdbcTemplate.update("insert into student values(null,?,?,?,?)", "唐三藏", "1990-11-23", "男", "女儿国");
System.out.println("添加了" + row + "行记录");
}
//查询所有记录
@Test
public void testFindAll() {
//query查询多条记录,查询所有的男生
List<Student> students = jdbcTemplate.query("select * from student where sex=?", new BeanPropertyRowMapper<>(Student.class), "男");
students.forEach(System.out::println);
}
//在每个测试方法之后关闭
@After
public void destory() {
//关闭容器
context.close();
}
}
在Spring的配置文件中需要配置哪2项?
配置数据源对象
配置JdbcTemplate对象,注入数据源
扩展:@Before 在每个测试方法前执行 @After 在每个测试方法后执行
使用第三方厂商的数据源
修改pom.xml文件,导入c3p0, druid的包,同时还需要导入日志记录包
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.7version>
dependency>
将log4j.properties文件复制到resources目录下
log4j.rootLogger=debug, stdout
log4j.category.org.springframework=debug
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
修改applicationContext.xml配置文件
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql:///day46"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
bean>
修改applicationContext.xml配置文件
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql:///day46"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
bean>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.0.RELEASEversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.30version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
drop table if exists account;
-- 创建数据表,账户表
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10),
money DOUBLE -- 金额
);
-- 添加数据
INSERT INTO account (name, money) VALUES ('周瑜', 1000), ('小乔', 1000);
select * from account;
package com.itheima.entity;
/**
账户实体类
*/
public class Account {
private Integer id;
private String name; //名字
private Double money; //余额
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public Account() {
}
public Account(Integer id, String name, Double money) {
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
package com.itheima.dao;
import com.itheima.entity.Account;
import java.util.List;
public interface AccountDao {
/**
* 添加1个账户
*/
int save(Account account);
/**
* 查询所有的账户
*/
List<Account> findAll();
}
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.entity.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
* 实现类
*/
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
//set方法注入对象
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int save(Account account) {
return jdbcTemplate.update("insert into account values(null,?,?)", account.getName(), account.getMoney());
}
@Override
public List<Account> findAll() {
return jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
}
}
方法与DAO层中的方法相同
package com.itheima.service;
import com.itheima.entity.Account;
import java.util.List;
/**
* 业务层接口
*/
public interface AccountService {
/**
* 添加1个账户
*/
int save(Account account);
/**
* 查询所有的账户
*/
List<Account> findAll();
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
import java.util.List;
/**
* 业务类
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public int save(Account account) {
return accountDao.save(account);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
在DAO层需要注入哪个对象?
JdbcTemplate对象
在业务层需要注入哪个对象?
DAO对象
<context:property-placeholder location="classpath:druid.properties"/>
注:键的命名
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/day46?characterEncoding=utf8
jdbc.driverClassName=com.mysql.jdbc.Driver
<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="classpath:druid.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="com.itheima.dao.impl.AccountDaoImpl" id="accountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
bean>
<bean class="com.itheima.service.impl.AccountServiceImpl" id="accountService">
<property name="accountDao" ref="accountDao"/>
bean>
beans>
package com.itheima.test;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
//相当于表示层
public class TestAccount {
//获取业务层对象
AccountService accountService;
//在每个测试方法前调用
@Before
public void init() {
//创建Spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取业务层对象
accountService = context.getBean("accountService", AccountService.class);
}
//添加操作
@Test
public void testAdd() {
Account account = new Account(3, "刘备",10d);
accountService.save(account);
}
//查询所有记录
@Test
public void testFindAll() {
List<Account> accountList = accountService.findAll();
accountList.forEach(System.out::println);
}
}
对象之间的注入顺序:
数据源 -> 模板对象 -> DAO -> Service
applicationContext.xml
<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">
<import resource="applicationContext-service.xml"/>
<import resource="applicationContext-dao.xml"/>
beans>
applicationContext-service.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.itheima.service.impl.AccountServiceImpl" id="accountService">
<property name="accountDao" ref="accountDao"/>
bean>
beans>
applicationContext-dao.xml
<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="classpath:druid.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="com.itheima.dao.impl.AccountDaoImpl" id="accountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
bean>
beans>
import标签属性 | 说明 |
---|---|
resource | 指定导入的配置文件 |
使用XML+注解的方式改造上面的账户案例
使用@Repository与@Autowired注解,创建对象并给对象属性赋值
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 实现类
*/
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(Account account) {
return jdbcTemplate.update("insert into account values(null,?,?)", account.getName(), account.getMoney());
}
@Override
public List<Account> findAll() {
return jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
}
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 业务类
*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public int save(Account account) {
return accountDao.save(account);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.itheima"/>
<context:property-placeholder location="classpath:druid.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
beans>
package com.itheima.test;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
//相当于表示层
public class TestAccount {
//获取业务层对象
AccountService accountService;
//在每个测试方法前调用
@Before
public void init() {
//创建Spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取业务层对象
accountService = context.getBean("accountService", AccountService.class);
}
//查询所有记录
@Test
public void testFindAll() {
List<Account> accountList = accountService.findAll();
accountList.forEach(System.out::println);
}
}
哪些类使用XML配置,哪些类使用注解配置?
第三方的jar包使用XML配置,自己写的类使用注解
配置业务层和DAO层分别用哪个注解?
@Service
@Repository
@Autowired
纯注解配置的分析
应用场景:以后实现零配置来做项目,XML的配置都放在类中,这个类就是配置类,以后这个配置类不用我们写,而其它的框架去实现(SpringBoot)
基于注解的 IoC 配置已经完成,但是大家都发现了一个问题:我们依然离不开 spring 的 xml 配置文件,那么能不能省略这个 applicationContext.xml,所有配置都用注解来实现呢?
我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:
<context:component-scan base-package="com.itheima"/>
如果也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
另外,数据源和 JdbcTemplate的配置也需要靠注解来实现。
我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate对象如何从配置文件中移除呢?
为了模块化设计,将与数据库有关的配置写到另一个配置类中:JdbcConfig
@ComponentScan的属性 | 作用 |
---|---|
basePackages | 参数是一个字符数组,指定一个或多个基包的名字 |
value | 同上 |
作用:读取Java的属性文件(.properties)
value[]属性:指定一个或多个属性文件的名字
注:@PropertySource可以不用写classpath,因为注解默认从类路径下加载
创建包com.itheima.config
在包中创建配置类
使用@Value("${键}")注入数据库连接的属性值
编写数据源有关的方法
注:在每个方法上添加@Bean注解,无需指定value,方法通过类型匹配的方式从容器中去查找对象
package com.itheima.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration //表示这是一个配置类,如果被其它配置类导入,这个可以省略
@PropertySource("druid.properties") //默认从类路径下去加载文件
public class JdbcConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driverClassName}")
private String driverClassName;
/**
* 创建数据源对象
* Bean注解的作用:放在方法上
* 1. 会将这个方法的返回值放在Spring容器中
* 2. 如果方法有参数,会按类型匹配的方式从容器中去查找,找到后注入进来
* 3. 也可以指定放在容器中的名字,如果没有指定名字,默认是方法的名字
*/
@Bean
public DataSource createDataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setUsername(username);
ds.setPassword(password);
ds.setUrl(url);
ds.setDriverClassName(driverClassName);
return ds;
}
@Bean
public JdbcTemplate createJdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
}
注:类中没有任何其它的代码
思考:此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 主配置类
*/
@Configuration
@Import(JdbcConfig.class) //指定一个或多个类对象,导入另一个配置类
@ComponentScan("com.itheima") //指定一个或多个要扫描的基包名字
public class SpringConfig {
}
容器通过:AnnotationConfigApplicationContext类创建
package com.itheima.test;
import com.itheima.config.SpringConfig;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
//相当于表示层
public class TestAccount {
//获取业务层对象
AccountService accountService;
//在每个测试方法前调用
@Before
public void init() {
//创建Spring容器,指定配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//获取业务层对象
accountService = context.getBean("accountService", AccountService.class);
}
//查询所有记录
@Test
public void testFindAll() {
List<Account> accountList = accountService.findAll();
accountList.forEach(System.out::println);
}
}
注解 | 作用 |
---|---|
@Configuration | 这是一个配置类 |
@ComponentScan | 指定要扫描的基包 |
@Import | 导入其它的配置类 |
@PropertySource | 读取属性配置文件 |
@Value | 将配置文件中属性值注入给成员变量 |
@Bean | 1. 把方法的返回值放到容器中 2. 可以指定名字,如果没有指定名字,方法名就是id 3. 如果方法有参数,通过类型匹配的方式从容器中去找 |
在测试类中,我们都需要自己创建容器对象和业务对象:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService)context.getBean("accountService");
如何去掉这些代码,使用自动注入的方式获取业务对象呢?
Spring容器对象由JUnit帮我们创建好
容器中的业务对象也由JUnit创建好,我们只需要直接使用业务对象即可
针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 Spring 容器,我们就无须手动创建了,问题也就解决了。
我们都知道, JUnit 单元测试它自己无法知晓我们是否使用了Spring 框架,更不用说帮我们创建 Spring 容器了。不过好在, JUnit4 给我们提供扩展的一个注解**@RunWith**,可以让我们替换掉它自己本身的运行器。这时,我们使用 Spring 框架提供的运行器,可以读取配置文件(或注解)来创建Spring容器。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.0.RELEASEversion>
<scope>testscope>
dependency>
package com.itheima.test;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
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;
//指定运行器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件
public class TestAccount2 {
//获取业务层对象
@Autowired
AccountService accountService;
//查询所有记录
@Test
public void testFindAll() {
List<Account> accountList = accountService.findAll();
accountList.forEach(System.out::println);
}
}
package com.itheima.test;
import com.itheima.config.SpringConfig;
import com.itheima.entity.Account;
import com.itheima.service.AccountService;
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;
/**
* Spring整合JUnit
* 纯注解的方式
* 1. RunWith 是JUnit的注解,可以指定其它第三方的运行器。指定为Spring编写的运行器:SpringJUnit4ClassRunner
* 2. ContextConfiguration读取Spring的配置文件,纯注解:指定一个或多个配置类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class TestAccount2 {
//由Spring自动注入业务对象
@Autowired
AccountService accountService;
//查询所有记录
@Test
public void testFindAll() {
List<Account> accountList = accountService.findAll();
accountList.forEach(System.out::println);
}
}
@RunWith注解作用?
指定第三方的运行器
@ContextConfiguration注解作用?
指定配置文件
对一个类的功能进行增强或对类中的方法进行拦截,应用场景:要增强一个类或方法,但不能修改原来的类代码。
如:对业务类中每个方法添加日志记录的功能,使用代理模式给每个方法添加了日志,又没有修改原来的业务代码。
因为这个代理类和方法由JDK自带的,类名:Proxy
学习使用cglib来代理对象
特点:cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类或实现java接口,它广泛的被许多AOP的框架使用,为他们提供方法的拦截。
如果一个类并没有实现任何的接口,则无法使用上面所说的JDK动态代理,这时需要使用CGLIB代理,本质上是对原有类的继承,子类重新相应的方法。
代理SuperStar这个类,这个类没有实现任何的接口
有两个方法:唱歌,演戏
对演戏的方法进行代理
package com.itheima.proxy;
/**
* 真实对象:被代理的对象
*/
public class SuperStar {
/**
* 唱歌
* @param song
*/
public void sing(String song) {
System.out.println("宝强唱:" + song);
}
/**
* 演戏
*/
public void act(double money) {
System.out.println("拍戏出场费:" + money);
}
}
package com.itheima.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 使用代理模式
*/
public class Fans {
public static void main(String[] args) {
System.out.println("真实对象:");
//真实对象
SuperStar s1 = new SuperStar();
//唱歌的方法不变
s1.sing("常回家看看");
//对演戏的方法进行增强
s1.act(800);
System.out.println("代理对象:");
/**
* 创建代理对象
* 参数1:被代理的类类型
* 参数2:回调函数
*/
SuperStar s2 = (SuperStar) Enhancer.create(SuperStar.class, new MethodInterceptor() {
/**
* 每个被代理的方法都会执行一次
* @param proxy 生成的代理对象
* @param method 被代理的方法对象
* @param args 方法的参数
* @param methodProxy 生成代理方法
* @return 方法运行的返回值
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//获取方法名字
String methodName = method.getName();
//判断是不是act方法,对原有的功能进行增强
if ("act".equals(methodName)) {
//获取出场费价格
double money = (double) args[0];
if (money < 1000) {
System.out.println("没有档期");
return null;
}
}
//调用原来的方法
return method.invoke(s1, args);
}
});
//调用代理对象方法
s2.sing("绿光");
s2.act(1700);
}
}
JDK代理对象:class com.sun.proxy.$Proxy11
CGLIB代理对象:class com.itheima.cglibproxy.SuperStar$$EnhancerByCGLIB$$2a0abf81
Cglib动态代理也称之为子类代理 : 代理类继承了真实对象
Enhancer类名 | 描述 |
---|---|
static Object create(Class type, Callback callback) | 创建代理对象 参数1:被代理的类型 参数2:回调函数 |
MethodInterceptor接口继承了Callback接口 | 描述 |
---|---|
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable |
每个被代理的方法都会执行1次 1. 代理对象 2. 真实方法 3. 方法参数 4. 生成的代理方法 |
能够使用JdbcTemplate的模板
1. 创建数据源
2. 创建JdbcTemplate对象,注入数据源
3. 方法:
1) 增删改方法:update()
2) 查询1条记录:queryForObject()
3) 查询多条记录:query()
RowMapper接口,用来将表的字段与实体类的属性进行映射
BeanPropertyRowMapper子类
能够配置Spring的连接池
DriverManagerDataSource
DruidDataSource
上面2种的属性相同
ComboPooledDataSource
能够使用JdbcTemplate完成增删查改操作
BeanPropertyRowMapper类的构造方法 | 说明 |
---|---|
public BeanPropertyRowMapper(Class |
用来将表的字段与实体类的属性进行映射,已经实现的子类 |
使用注解改造CRUD工程
第三方的类使用:XML的配置
自己写的类使用:使用注解
@Repository 用在持久层
@Service 用在业务层
@Autowired 依赖注入
基于纯注解改造CRUD工程
注解 | 作用 |
---|---|
@Configuration | 这是一个配置类 |
@ComponentScan | 扫描基包 |
@Import | 导入其它配置类 |
@PropertySource | 加载Java的属性文件 |
@Value | 将属性值注入到成员变量中 |
@Bean | 用在方法上 1. 方法的返回值放到容器中 2. 可以指定id,如果没有指定方法名就是id 3. 如果方法有参数,自动按类型匹配的方式注入 |
能够实现Spring框架整合Junit
注解名 | 属性作用 |
---|---|
@RunWith | 指定第三方的运行器 |
@ContextConfiguration | 指定Spring的配置文件 locations: 默认,指定xml配置文件 |
classes: 指定配置类 |
掌握动态代理的两种实现方式
CGLIB代理:用于被代理的类没有实现接口的时候,本质上是重写父类的方法进行增强
Enhancer类名 | 描述 |
---|---|
static Object create(Class type, Callback callback) | 创建代理对象 |
MethodInterceptor接口继承了Callback接口 | 描述 |
---|---|
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable |
每个被代理的方法都会调用一次的回调函数 |
Memorial Day is 513 days |