一、IOC相关注解
1、注解引入
<?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">
<context:component-scan base-package="packageName"/>
<beans/>
# 在spring配置文件中启动注解扫描,加载类中配置的注解项
<context:component-scan base-package="packageName"/>
- 说明:
在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描
扫描过程是以文件夹递归迭代的形式进行的
扫描过程仅读取合法的java文件
扫描时仅读取spring可识别的注解
扫描结束后会将可识别的有效注解转化为spring对应的资源加入IoC容器
- 注意:
无论是注解格式还是XML配置格式,最终都是将资源加载到IoC容器中,差别是数据读取方式不同。
从加载效率上来说注解优于XML配置文件
2、bean实例化注解
# 这些注解使用在类上,用于创建被修饰的类对象,并将创建好的对象存放到IOC容器中
@Component : 组件
@Controller : 控制器
@Service : 服务
@Repository : 仓库
# 说明
@Component : (Component组件) 在类上使用该注解,把资源让spring来管理。
作用: 相当于bean标签,创建当前类对象并存放到IOC容器中
value属性: 指定bean的id,默认bean的id是当前类的类名。首字母小写。
@Controller,@Service,@Repository:
作用、使用、属性: 与@Component的作用和属性是一模一样的
他们只是提供了更加明确的语义化(见名知意),精确指出是哪一层的对象,但不是强制要求的
@Controller:一般用于表现层的注解。 将web层的类创建对象存放到IOC容器中
@Service:一般用于业务层的注解。 将serice层的类创建对象存放到IOC容器中
@Repository:一般用于持久层的注解。 将dao层的类创建对象存放到IOC容器中
# 以上注解相当于下面得到bean标签:
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
代码演示:service层
//@Component
//@Controller
@Service
//@Repository
public class AccountServiceImpl implements AccountService {
@Override
public void add() {
System.out.println("add 方法执行了...");
}
}
3、数据注入的注解
`这些注解使用在属性上,用于给对象中的属性进行赋值:
@Autowired
@Qualifier
@Resource
@Value
`相当于xml中的set注入
<property name="userDao" ref="userDao" ></property>
<property name="userDao" value="基本类型数据或String"></property>
1)注入基本类型和String
@Value
`@Value:
作用:用于注入基本类型和String的注入
value属性: 指定数据的内容,它可以支持Spring的EL表达式(SPEL),写法:${表达式}
`SpEL表达式:
作用: 从IOC容器中获取对应的值
格式: ${key}
属性值来自于properties配置文件
`前提:
# 载properties配置文件中的信息存放到容器中
<context:property-placeholder
location="classpath:jdbc.properties"></context:property-placeholder>
`注意:
当我们使用注解注入数据时,构造器和set方法不是必须的了。
代码演示
xml配置
applicationContext.xml
<!--
注释: 写个程序员看的,当程序员看到注释后就明白了代码的含义
注解: 写给虚拟机看的,当虚拟机在编译代码时,根据不同的注解将代码编译成指定的格式
-->
<!-- 开启组件扫描: 告诉Spring框架,它的注解在哪个包中的类上 -->
<context:component-scan base-package="com.example"></context:component-scan>
<!-- 解析Properties配置文件,将配置文件中的数据加载到IOC容器中 -->
<context:property-placeholder
location="classpath:jdbc.properties"
file-encoding="utf-8">
</context:property-placeholder>
Properties配置
jdbc.username=root
jdbc.password=1234
jdbc.port=3306
service层
@Service("AccountService")
public class AccountServiceImpl1 implements AccountService {
/**
* @Value: 用于注入基本类型和String类型的数据
* Value注解可以使用SPEL表达式获取IOC容器中的数据 ${变量名称}
*/
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.port}")
private int port;
@Override
public void add() {
System.out.println("注入基本类型和String类型的数据.....");
System.out.println(username+" : "+password+" : "+port);
}
}
测试
package com.example.web;
public class AccountClient1 {
@Test
public void test01(){
//1.解析配置文件,得到ApplicationContext对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.调用应用上下文的API从IOC容器中获取bean对象
AccountService service = ac.getBean("AccountService", AccountService.class);
service.add();
}
}
2)注入bean类型
@Autowired
@Autowired: 默认按照bean的类型注入数据,如果类型相同,则按名称注入
`原理:
【被注入的对象,来自IOC容器】
(如果IOC容器中只有一个此类型的对象)-->直接注入
(如果IOC容器中有多个此类型的对象)-->按照IOC容器中对象的名称注入,如果名称不匹配错。
`属性:
required:
true: 此对象必须注入成功,若不成功则报错. 默认值
false: 可以注入不成功
@Qualifier
`作用:
与@Autowired注解一起使用,指定在按照bean类型注入的基础上,再按照bean的名称注入
`属性:
value:指定bean的名称
@Resource
此注解是JDK提供的,作用相当于 @Autowired+@Qualifier
`作用:
默认按照bean的名称注入数据,如果同一个接口有多个实现,可以通过指定属性进行注入
`属性:
name:指定bean的名称注入数据
type:指定bean的类型注入数据
`注意细节:
如果没有对应的名称与之匹配,则按照类型注入
代码演示
service层
package com.example.service.impl;
@Service("AccountService2")
public class AccountServiceImpl2 implements AccountService {
/**
* IOC容器:
* AccountDao:
* AccountDaoImpl name: accountDaoImpl
* AccountDaoImpl2 name: accountDaoImpl2
* type: AccountDao.class
* @Autowired: 将IOC容器中的对象注入到属性上
* 默认按照类型注入,当只有一个该类型对象时,直接注入
* 如果发现多个同类型的对象,则按照变量名称注入
* 如果没有一样的变量名则报错
* required属性: 是否必须注入成功(当前属性需要结合@Qualifier使用)
* true: 必须注入成功,否则报错 (默认值)
* false: 可以注入不成功,如果注入不成功,则为null
*
* @Qualifier: 指定注入的对象的名称
*
* @Resource:
* 默认按照名称注入,如果IOC容器中没有对应名称的对象,则按照类型注入
* type: 指定类型注入
* name: 指定名称注入
*/
//@Autowired(required = false)
//@Qualifier("accountDaoImpl2")
@Resource(type = AccountDao.class,name = "accountDaoImpl")
private AccountDao accountDao;
@Override
public void add() {
System.out.println("33333333333...");
accountDao.add();
}
}
dao层
package com.example.dao;
public interface AccountDao {
void add();
}
----------------------------------
package com.example.dao.impl;
import com.example.dao.AccountDao;
@Repository
public class AccountDaoImpl1 implements AccountDao {
@Override
public void add() {
System.out.println("AccountDaoImpl11111中的方法执行了");
}
}
-----------------------------------
package com.example.dao.impl;
import com.itheima.dao.AccountDao;
import org.springframework.stereotype.Repository;
@Repository
public class AccountDaoImpl2 implements AccountDao {
@Override
public void add() {
System.out.println("AccountDaoImpl22222中的方法执行了");
}
}
测试类
package com.example.web;
public class AccountClient2 {
@Test
public void test01(){
//1.解析配置文件,得到ApplicationContext对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.调用应用上下文的API从IOC容器中获取bean对象
AccountService service = ac.getBean("AccountService2", AccountService.class);
service.add();
}
}
4、bean作用范围与生命周期注解
1)作用范围注解
@Scope
@Scope:
作用: 用于调整bean的作用范围,相当于bean标签的scope属性
使用位置: 被创建的类上
value属性: 指定作用范围的取值。取值是固定的5个,和XML的配置取值是一样的。
singleton: 单实例 默认值
prototype: 多实例
2)生命周期注解
@PostConstruct
@PostConstruct : 热加载数据
作用: 指定初始化方法,相当于init-method
使用位置: 初始化的方法上
@PreDestroy
@PreDestroy : 资源回收
作用: 指定销毁方法,相当于destroy-method
使用位置: 销毁的方法上
3)代码演示
service层
package com.example.service.impl;
/**
* 测试作用范围
* @Scope:
* singleton: 单实例
* prototype: 多实例
* 生命周期:
* @PostConstruct: 设置初始化调用的方法
* @PreDestroy: 设置销毁前调用的方法
*/
@Service("AccountService3")
@Scope("prototype")
public class AccountServiceImpl3 implements AccountService {
@Override
public void add() {
System.out.println("AccountServiceImpl3 中的add方法执行了");
}
@PostConstruct
public void initMethod(){
System.out.println("对象初始化了...");
}
@PreDestroy
public void destoryMethod(){
System.out.println("对象销毁了...");
}
}
测试
package com.example.web;
import com.example.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountClient3 {
@Test
public void test01(){
//1.解析配置文件,得到ApplicationContext对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.调用应用上下文的API从IOC容器中获取bean对象
for (int i = 0; i < 10; i++) {
AccountService service = ac.getBean("AccountService3", AccountService.class);
System.out.println(service);
}
}
@Test
public void test02(){
//1.解析配置文件,得到ApplicationContext对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.调用应用上下文的API从IOC容器中获取bean对象
AccountService service = ac.getBean("AccountService3", AccountService.class);
System.out.println(service);
((ClassPathXmlApplicationContext) ac).close();
}
}
5、纯注解案例演示
使用配置类代替配置文件,将以下注解写在配置类上
@Configuration: 声明该类为Spring的配置文件类
@ComponentScan(basePackages = "com.example") : 指定要扫描的包
@PropertySource(value = "classpath:jdbc.properties") : 将配置文件交给Spring容器管理
@Bean() : 将方法返回的对象存放到IOC容器中,
将返回值存放到IOC容器中,一般将第三方提供的类对加载到IOC容器
1、@Configuration:声明配置类,代替applicationContext.xml的作用
2、@ComponentScan:配置注解扫描 ,这样注解才可以被识别
3、@Bean:将方法的返回值装配到IOC容器中
4、@PropertySource:加载外部资源文件
5、@Import:引入其他配置类
加载配置类,初始化IOC容器,获取工厂
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
@ComponentScan("com.example")
相当于
@ComponentScans(
{ @ComponentScan("com.example.web"),
@ComponentScan("com.example.service")
})
对比下面的applicationContext.xml配置
<!--相当于@ComponentScan 扫描包-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 相当于@PropertySource 读取外部根目录下资源配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置出druidDataSource数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${dataSource.driverClassName}"></property>
<property name="url" value="${dataSource.url}"></property>
<property name="username" value="${dataSource.username}"></property>
<property name="password" value="${dataSource.password}"></property>
</bean>
<!--配置jdbc查询模板,注入druidDataSource数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
SpringConfig类
package com.example.config;
@Configuration
@ComponentScan("com.example")
@PropertySource(value = "classpath:data.properties",encoding = "utf-8")
public class SpringConfig {
//@Bean("存放到IOC容器时的名称")【如果不设置名称,默认为当前方法的名称】
//将方法的返回值存放到IOC容器中
//@Bean("account")
@Bean
public Account getAccount(){
return new Account(1,"景甜",100f);
}
}
测试类
package com.example.web;
import com.example.config.SpringConfig;
import com.example.pojo.Account;
import com.example.service.AccountService;
public class AccountClient {
@Test
public void test01(){
//1.解析配置类,得到ApplicationContext对象
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.调用应用上下文的API从IOC容器中获取bean对象
AccountService service = ac.getBean("AccountService", AccountService.class);
service.add();
}
@Test
public void test02(){
//1.解析配置类,得到ApplicationContext对象
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.调用应用上下文的API从IOC容器中获取bean对象
Account account = ac.getBean("getAccount",Account.class);
System.out.println(account);
}
}
6、注解与xml对照表
注解 | xml | 说明 |
---|---|---|
@Component父注解 @Controller:用于表现层的注解 @Service:用于业务层的注解 @Repository:一般用于持久层的注解 | < bean id="" class=""> | 声明bean交于springIOC管理 |
@Scope | scope=“singleton/prototype” | 生命周期 |
@PostConstruct | init-method | 初始化方法 |
@PreDestroy | destroy-method | 销毁方法 |
@Autowired、@Qualifier @Resource | ref=“自定义类型” | 依赖注入 |
@Value | value=“基础数据类型” | 基本数据类型注入 |
二、JdbcTemplate【了解】
1、原生JDBC开发基本路程
package com.example.jdbc;
public class JdbcDemo {
public static void main(String[] args) throws Exception {
// 查询: 根据id查询,查询id为1的账户信息
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
String url = "jdbc:mysql://localhost:3306/itheima118_spring";
String username = "root";
String password = "root";
Connection conn = DriverManager
.getConnection(url, username, password);
//3.编写sql语句
String sql = "select * from account where id in (?,?) ";
//4.创建预编译语句执行者对象
PreparedStatement pst = conn.prepareStatement(sql);
//5.设置真实值
// 第一个值为sql语句中第几个 ?
// 第二个值为当前?的真实值
pst.setInt(1,1);
pst.setInt(2,3);
//6.执行sql并返回结果
ResultSet rs = pst.executeQuery();
//7.处理结果
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
float money = rs.getFloat("money");
System.out.println(id+" : "+name+" : "+money);
}
//8.关闭资源
rs.close();
pst.close();
conn.close();
}
}
2、JdbcTemplate使用步骤
JdbcTemplate是Spring对JDBC的封装
操作数据库:
`增删改:
当执行增删改操作时,会对数据库原有数据产生影响,返回的是影响的行数
`查询:
单行单列: 基本数据类型
需求: 查询账户表总条数 查询id为1的账户名称
单行多列: map java实体
需求: 查询id为1的账户详情
多行单列: List
需求: 查询账户表所有用户名称
多行多列: List<Map> List<java实体>
需求: 查找账户表的所有信息
1.导入jar包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
2.创建JdbcTemplate对象
public JdbcTemplate(DataSource dataSource)
在创建对象时,传入数据源(连接池对象),JdbcTemplate会自动从连接池对象中获取连接
3.编写sql
String sql = "insert into 表名 values(null,?,?) ";
4.处理返回结果
3、代码演示
package com.itheima.jdbc;
public class Demo02_JdbcTemplate {
public static void main(String[] args) {
// 新增
//1.创建JdbcTemplate对象
JdbcTemplate template = new JdbcTemplate();
// 创建连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 给JdbcTemplate设置连接池对象 set方法
template.setDataSource(dataSource);
//2.编写sql语句
String sql = "insert into account values(null,?,?) ";
//3.调用JdbcTemplate的API执行添加
int i = template.update(sql, "景甜", 500f);
if(i>0){
System.out.println("添加成功...");
}
}
}
4、JdbcTemplate相关API
1)增删改的方法
public int update(final String sql,Object... args)
用于执行INSERT、UPDATE、DELETE等DML语句
参数1 sql: 要执行的sql语句
参数2 args: sql执行时需要的参数
代码演示
package com.example.template;
public class Demo2 {
/**
* JdbcTemplate: 增删改
* int update(String sql,Object args...);
* update执行完毕后返回对数据库影响的行数
* 参数1: 要执行的sql语句
* 参数2: sql语句中?对应的真实参数
*/
private JdbcTemplate template;
@Before
public void init(){
// 创建连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
template = new JdbcTemplate(dataSource);
}
@Test
public void testInsert(){
// 编写sql
String sql = "insert into account values(null,?,?) ";
// 调用API执行
int i = template.update(sql, "刘亦菲", 500f);
if (i>0){
System.out.println("添加成功");
}
}
@Test
public void testUpdate(){
// 编写sql
String sql = "update account set name=?,money=? where id=? ";
// 调用API执行
int i = template.update(sql, "杨幂", 50f,4);
if (i>0){
System.out.println("修改成功");
}
}
@Test
public void testDelete(){
// 编写sql
String sql = "delete from account where id=? ";
// 调用API执行
int i = template.update(sql, 4);
if (i>0){
System.out.println("删除成功");
}
}
}
2)查询的方法
// 查询聚合函数 返回指定类型的一个数据
public <T> T queryForObject(String sql, Class<T> requiredType, Object... args) ★★★ 作用: 用于执行聚合函数
sql: 要执行的sql
requiredType: 返回的类型的字节码对象
args: sql所需要的参数
// 查询聚合函数 返回的结果封装到实体中
public <T> T queryForObject(String sql, RowMapper rm, Object... args)
★★★ 作用: 用于查询一条记录并封装到实体中(注意,若是查询不到结果jdbcTemplate会报异常)
RowMapper接口: 用于手动封装结果数据
mapRow方法,封装一条记录的
我们使用该方法查询一条记录并封装到实体中 BeanPropertyRowMapper<T>(T.class)
// 查询单行多列 将一条查询结果封装到map中返回
public Map<String, Object> queryForMap(String sql, Object... args)
sql: 要执行的sql
args: sql所需要的参数
// 查询单列多行
List<T> queryForList(String sql,T.class, Object... args)
// 查询多行多列 将每条记录放map中,再将每个map放入list中
List<Map<String, Object>> queryForList(String sql, Object... args)
sql: 要执行的sql
args: sql所需要的参数
// 返回一个List集合,List中存放的是RowMapper指定类型的数据
public <T> List<T> query(String sql, RowMapper<T> rowMapper)
★★
返回一条记录: rowMapper(需要自己封装结果集)
返回值: BeanPropertyRowMapper
JDBCTemplate的 query方法用于执行SQL语句,简化JDBC的代码。
同时还可以在SQL语句中使用"?"占位,
Object... args 可变参数中传入对应的参数,可变参数可以传数组.
返回结果:
单行单列: 基本类型或String接收结果即可
select name from 表名 where id = 1;
select count(*) from 表名;
单行多列: Map<key,value> key为字段名称,value为查询到的结果
select * from 表名 where id = 1;
多行单列: List<T>
select name from 表名;
多行多列: List<Map<key,value>>
select * from 表名;
----------常用★★★ -----------
将一条查询结果封装成一个对象: User
select * from User where username="tom" and password="123";
查询所有账户信息: List<Account>
select * from account;
查询总记录数:
select count(*) from 表名;
代码演示
package com.example.template;
import com.example.pojo.Account;
public class Demo3 {
/**
* JdbcTemplate: 查询
* 单行单列:
* T queryForObject(sql,T.class,Object args...)
* 单行多列:
* 多行单列:
* 多行多列:
*/
private JdbcTemplate template;
@Before
public void init(){
// 创建连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
template = new JdbcTemplate(dataSource);
}
/**
* 需求: 查询id为5的账户名称
* 结果: 单行单列
* API:
* T queryForObject(sql,T.class,Object args...);
*/
@Test
public void test01(){
String sql = "select name from account where id = ?";
// 调用API
String name = template.queryForObject(sql, String.class, 5);
System.out.println(name);
}
/**
* 需求: 查询id为5的账户详情?
* 结果: 单行多列
* API:
* Map<String,Object> queryForMap(sql,Object... args)
* map的key: 查询结果的字段名称
* map的value: 查询结果数据
*/
@Test
public void test02(){
String sql = "select * from account where id = ? ";
// 调用API
Map<String, Object> map = template.queryForMap(sql, 5);
for(String key:map.keySet()){
System.out.println(key+" : "+map.get(key));
}
}
/**
* 需求: 查询id为5的账户详情?
* 结果: 单行多列
* API:
* queryForObject(sql,rowMapper接口的实现类,Object... args);
*/
@Test
public void test03(){
String sql = "select * from account where id = ? ";
// 调用API
Account account = template.queryForObject(sql, new RowMapper<Account>() {
// 每查询一条记录RowMapper的mapRow方法就会执行一次
// rs: 本行记录信息
// i: 索引
@Override
public Account mapRow(ResultSet rs, int i) throws SQLException {
System.out.println(i);
int id = rs.getInt("id");
String name = rs.getString("name");
float money = rs.getFloat("money");
Account account = new Account(id, name, money);
return account;
}
}, 5);
System.out.println(account);
}
/**
* 需求: 查询id为5的账户详情?
* 结果: 单行多列
* API:
* queryForObject(sql,BeanPropertyRowMapper<Account>,Object... args);
* 数据库字段要和实体属性名保持一致.
*/
@Test
public void test04(){
String sql = "select * from account where id = ? ";
// 调用API
Account account = template.queryForObject(sql,
new BeanPropertyRowMapper<Account>(Account.class), 5);
System.out.println(account);
}
/**
* 需求: 查询账户表所有的账户名称
* 结果: 多行单列
* API:
* List<T> queryForList(sql,T.class,Object... args);
*/
@Test
public void test05(){
String sql = "select name from account where id in(?,?,?) ";
//API
List<String> list = template.queryForList(sql, String.class, 1, 2, 3);
System.out.println(list);
}
/**
* 需求: 查询账户表所有的账户详情
* 结果: 多行多列
* API:
* List<Map<String,Object>> queryForList(sql,Object... args);
*/
@Test
public void test06(){
String sql = "select * from account where id in(?,?,?) ";
//API
List<Map<String, Object>> list = template.queryForList(sql, 1, 2, 3);
System.out.println(list);
}
/**
* 需求: 查询账户表所有的账户详情
* 结果: 多行多列
* API:
* query();
*/
@Test
public void test07(){
String sql = "select * from account where id in(?,?,?) ";
//API
// 可变参可以传数组
Object params[] = {1,2,3};
List<Account> list = template.query(sql, new BeanPropertyRowMapper<Account>(Account.class), params);
System.out.println(list);
}
@Test
public void test08(){
String sql = "select * from account where id in(?,?,?) ";
//API
// 可变参可以传数组
Object params[] = {1,2,3};
List<Account> list = template.query(sql, new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet rs, int i) throws SQLException {
int id = rs.getInt("id");
String name = rs.getString("name");
float money = rs.getFloat("money");
Account account = new Account(id, name, money);
return account;
}
}, params);
System.out.println(list);
}
}
5、IOC+JDBCTemplate案例
案例需求:"select * from account " 查询所有账户信息
分别使用三种方式完成:
xml方式
xml+注解方式
注解方式
1)xml方式
xml配置
applicationContext.xm
<!-- 解析Properties配置文件,将数据加载到IOC容器中 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- ---------递进引用---------- -->
<!-- 一、创建AccountService对象 -->
<bean id="AccountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="AccountDao"></property>
</bean>
<!-- 二、创建AccountDao对象 -->
<bean id="AccountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="template" ref="JdbcTemplate"></property>
</bean>
<!-- 三、创建JdbcTemplate对象,存放到IOC容器中 -->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 四、创建连接池对象 -->
<!-- 1、Spring自带的连接池 -->
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>-->
<!-- 2、c3p0的连接池 -->
<!--<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>-->
<!-- 3、druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
properties配置
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
dao层
-----------接口---------------
package com.example.dao;
import com.example.pojo.Account;
import java.util.List;
public interface AccountDao {
List<Account> findAll();
}
-----------实现类---------------
package com.example.dao.impl;
import com.example.dao.AccountDao;
import com.example.pojo.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate template;
@Override
public List<Account> findAll() {
String sql = "select * from account ";
List<Account> list = template.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
return list;
}
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
}
service层
-----------接口---------------
package com.example.service;
import com.example.pojo.Account;
import java.util.List;
public interface AccountService {
List<Account> findAll();
}
-----------实现类---------------
package com.example.service.impl;
import com.example.dao.AccountDao;
import com.example.pojo.Account;
import com.example.service.AccountService;
import java.util.List;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
@Override
public List<Account> findAll() {
System.out.println("service层执行了");
return accountDao.findAll();
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
测试类
package com.example.web;
import com.example.pojo.Account;
import com.example.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class AccountClient {
public static void main(String[] args) {
// 获取spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从IOC中获取service对象
AccountService service = ac.getBean("AccountService", AccountService.class);
//
List<Account> list = service.findAll();
System.out.println(list);
}
}
2)xml+注解方式
配置xml:第三方用xml,jar包提供的类用xml,交给spring容器管理
配置注解:自己的类用注解
xml配置
applicationContext.xm
<!-- TODO:开启注解扫描 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 解析properties配置文件,加载数据到IOC -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 创建JdbcTemplate对象,存放到IOC容器中 -->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
</beans>
dao层
package com.example.dao;
import com.example.pojo.Account;
public interface AccountDao {
List<Account> findAll();
}
-----------实现类---------------
package com.example.dao.impl;
import com.example.dao.AccountDao;
import com.example.pojo.Account;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate template;
@Override
public List<Account> findAll() {
String sql = "select * from account ";
List<Account> list = template.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
return list;
}
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
}
service层
-----------接口---------------
package com.example.service;
import com.example.pojo.Account;
public interface AccountService {
List<Account> findAll();
}
-----------实现类---------------
package com.example.service.impl;
@Service("AccountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public List<Account> findAll() {
System.out.println("service层执行了");
return accountDao.findAll();
}
}
测试类
package com.example.web;
import com.example.pojo.Account;
import com.example.service.AccountService;
public class AccountClient {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取service的对象
AccountService service = ac.getBean("AccountService", AccountService.class);
List<Account> list = service.findAll();
System.out.println(list);
}
}
3)注解方式
其他环境同上
Spring核心配置类
package com.example.config;
import com.alibaba.druid.pool.DruidDataSource;
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.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration // 声明配置类
@ComponentScan("com.itheima") // 开启包扫描
@PropertySource("classpath:jdbc.properties") // 解析properties配置文件
public class SpringConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// @Bean("template")
// public JdbcTemplate getJdbcTemplate(){
// JdbcTemplate template = new JdbcTemplate();
// template.setDataSource(getDataSource());
// return template;
// }
@Bean("template")
public JdbcTemplate getJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试类
package com.example.web;
import com.example.config.SpringConfig;
import com.example.pojo.Account;
import com.example.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class AccountClient {
public static void main(String[] args) {
// 获取容器IOC
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
// 从容器中获取service的对象
AccountService service = ac.getBean("AccountService", AccountService.class);
// 调用方法
List<Account> list = service.findAll();
System.out.println(list);
}
}
4) JdbcTemplate整合不同的数据源(连接池)
properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
Spring自带数据源
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
C3P0数据源
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
------------------------------
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
Druid数据源
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.13</version>
</dependency>
-----------------------------------
<!-- druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
三、Spring整合junit
作用: 简化单元测试代码 使用junit 4.12以上版本
// 例如省略:从容器中获取service的对象
AccountService service = ac.getBean("AccountService", AccountService.class);
1、条件
spring-test.jar
<!-- 引入单元测试的jar包 4.12以上 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 导入Spring整合junit的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
@RunWith
@ContextConfiguration
// 在测试类上添加以下两个注解
// 声明spring提供的单元测试环境
@RunWith(SpringJUnit4ClassRunner.class)
// 声明spring的配置信息
@ContextConfiguration(
locations="classpath:applicationContext.xml")(配置路径)
或者 @ContextConfiguration(classes=SpringConfig.class) (配置类)
locations属性:加载xml配置文件
classes属性:加载配置类的字节码
2、Junit整合代码
其他环境同上
1)整合xml方式的测试
package com.example.web;
import com.example.pojo.Account;
import com.example.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(locations = "classpath:applicationContext.xml")
public class AccountTest {
@Autowired
private AccountService service;
@Test
public void testFindAll(){
List<Account> list = service.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
2)整合注解方式的测试
package com.example.web;
import com.example.config.SpringConfig;
import com.example.pojo.Account;
import com.example.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;
/**
* SpringJUnit4ClassRunner: 解析配置文件或配置类,并创建ApplicationContext对象
* @ContextConfiguration: 设置配置文件或配置类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountTest {
@Autowired
private AccountService service;
@Test
public void testFindAll(){
List<Account> list = service.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
四、proxy与cglib动态代理
`方法增强: 在不改变源码的基础上对方法进行增强
继承
装饰模式
动态代理
`AOP: 在不改变源码的基础上对方法进行增强
底层动态代理:
proxy: jdk提供的,基于接口
cglib: 基于子类,生成被代理类的子类
1、继承、装饰模式、动态代理分析
2、基于接口的动态代理(JDK)
作用: 方法增强
要求: 被代理类至少实现一个接口
代码演示
Proxy: 动态代理 有目标对象
需求: 在不改变源码的基础上对run方法进行增强
-------------接口-----------------
public interface Car {
void color();
void run();
String jiaYou(String you);
}
-------------被代理类---------------
public class QQ implements Car {
public void color() {
System.out.println("绿色的");
}
public void run() {
System.out.println("QQ车以每秒1米的速度在挪动....");
}
public String jiaYou(String you) {
return "QQ车正在加"+you;
}
}
-------------动态代理生成的代理类---------
public class TestProxy {
public static void main(String[] args) {
// 被代理类对象
QQ qq = new QQ();
// 生成代理类对象
// proxy: 生成的代理的类对象
Car proxy = (Car) Proxy.newProxyInstance(
TestProxy.class.getClassLoader(),//类加载器
qq.getClass().getInterfaces(),//被代理类实现的接口字节码数组(代理类需要实现这些接口)
//new Class[]{Car.class},//被代理类实现的接口字节码数组
new InvocationHandler() { // 对需要增强的方法进行增强,对不需要增强的方法调用原来的逻辑
/**
* 每次调用代理类的方法时,此方法都会执行
* @param proxy : 生成的代理类对象(慎用)
* @param method : 当前所执行的方法的字节码对象
* @param args : 当前执行的方法所传递的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println("当前执行的方法名称: "+name);
if("run".equals(name)){
System.out.println("QQ车以每秒1千米的速度在飞奔...");
return null;
}
// 反射执行方法(调用原来的逻辑)
return method.invoke(qq,args);
// java中返回值分为2类.
// 第一: 有返回值
// 第二: 无返回值
// 动态代理说,如果执行的方法没有返回值则返回 null
// 如果执行的方法有返回值,则直接将返回值给返回
}
}
);
// 调用代理类的相关方法
proxy.color();
proxy.run();
String you = proxy.jiaYou("97#");
System.out.println(you);
}
}
Proxy: 动态代理 无目标对象 直接生成接口实现类
扩展-生成接口的实现类
public class TestProxy1 {
public static void main(String[] args) {
// 能不能使用动态代理生成接口的代理类
// 生成接口的实现类
// 生成代理类对象
// proxy: 生成的代理的类对象
Car proxy = (Car) Proxy.newProxyInstance(
TestProxy1.class.getClassLoader(),//类加载器
new Class[]{Car.class},//被代理类实现的接口字节码数组
new InvocationHandler() { // 对需要增强的方法进行增强,对不需要增强的方法调用原来的逻辑
/**
* 每次调用代理类的方法时,此方法都会执行
* @param proxy : 生成的代理类对象(慎用)
* @param method : 当前所执行的方法的字节码对象
* @param args : 当前执行的方法所传递的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(name);
Object result = null;
if("run".equals(name)){
System.out.println("QQ车以每秒1千米的速度在飞奔...");
}else if("color".equals(name)){
System.out.println("黄色的");
}else if ("jiaYou".equals(name)){
result = "QQ车正在加"+args[0];
}
return result;
}
}
);
// 调用代理类的相关方法
proxy.color();
proxy.run();
String you = proxy.jiaYou("97#");
System.out.println(you);
}
}
3、基于子类的动态代理(cglib第三方)
作用: 方法增强
要求: 导入cglib的jar包,由于cglib已经被Spring整合了,
所以导入Spring-context包即可
代码演示
被代理类
public class UserService{
public void demo01(){
System.out.println("demo01-----");
}
public void demo01(){
System.out.println("demo02-----");
}
}
测试
public static void main(String[] args) {
// 被代理对象
final UserService userService = new UserService();
// Enhance动态代理
// 基于子类的,动态生成一个类的子类,在生成的子类中对方法进行增强
UserService proxy = (UserService)Enhancer.create(
userService.getClass(),// 参数1:被代理对象的字节码
new MethodInterceptor() { // 参数2: 接口,在原方法执行前对方法进行拦截处理
/**
* 拦截吃了
* @param proxy : 生成的代理类对象(被代理类的子类)
* @param method : 当前执行的方法的字节码对象
* @param args : 当前执行的方法携带的参数数组
* @param methodProxy : 当前执行的方法的代理对象(不用管)
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("111111111111111111111");
result = method.invoke(userService,args);
System.out.println("222222222222222222222");
return result;
}
}
);
proxy.demo1();
proxy.demo2();
}
总结
`封装:
在java中提供了各式各样的容器,这些容器可以用于封装不同类型的数据.
`IOC注解开发:
1.创建对象并将对象存放到IOC容器
@Component
@Controller
@Service("accountService")
@Repository("accountDao")如果value属性值不写,默认为当前类的类名称,首字母小写
@Configuration
2.用于注入数据: 注入 基本类型和String 其他bean对象
【被注入的数据都来自IOC容器】
@Autowired : 按照类型自动注入
@Qualifier : 指定注入的对象名称
@Resource : 按照name注入,也可以按照类型注入
name: 需要注入的对象的名称
type: 需要注入的数据的类型
@Resource(name = "accountDao2",type=AccountDao.class)
如果name和type属性都不写,则按照名称注入
如果写了name,就将指定名称的对象注入进来
如果写了type,就按照类型注入
如果两个属性都写了,那么就必须满足所有的属性
@Value("值"):
值: 直接赋值
值: SpEL表达式,从容器中获取参数
解析properties配置文件中的参数,并存放到IOC容器中
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
3.生命周期和作用范围:
@Scope("singleton或者prototype") (写在类上)
@PostConstruct : 初始化时调用的方法 (写在方法上)
@PreDestory : 销毁时调用的方法 (写在方法上)
4.纯注解:
使用配置类代替配置文件
@Configuration: 声明该类为Spring的配置文件类
@ComponentScan(basePackages = "com.example") : 指定要扫描的包
@PropertySource(value = "classpath:jdbc.properties") : 将配置文件交给Spring容器管理
@Import({其他配置类.class})
@Bean() : 将返回值存放到IOC容器中
`JDBCTemplate:
Spring提供的,对JDBC进行封装的工具
API:
增删改: 执行增删改返回影响的行数
★int update(sql,args...);
查询:
★T queryForObject(sql,T.class,args...);
主要用于查询聚合函数
★T queryForObject(sql,new BeanPropertyRowMapper<T>(T.class),args...);
查询一条记录,并将其封装到对应的实体中(T代表实体)
注意: 实体中属性的名称要和数据库表字段名称保持一致
当查询不到结果时,JdbcTemplate会抛异常
Map<key,value> queryForMap(sql,args...);
查询一条记录,并将这条记录存放到Map集合中
可以用于多表查询,返回一条记录
List<T> queryForList(sql,T.class,args...);
查询多行单列的结果,并将结果封装到List集合中
List<Map<key,value>> queryForList(sql,args...);
查询多行多列,将每一行封装成map,多个map存放到list中
★List<T> query(sql,new BeanPropertyRowMapper<T>(T.class),args...);
查询多行多列,每一个行封装成一个实体对象,多个实体对象存放到list集合中
`动态代理:
Proxy基于接口 : JDK
被代理类对象至少实现一个接口
代理类和被代理类为兄弟关系
cglib基于子类 : cglib
导入jar包坐标
Enhancer
代理和被代理类为父子关系
Spring会自动抉择