Spring
IOC容器XML配置【案例】
JdbcTemplate 操作数据库
介绍
JdbcTemplate是Spring提供的一个模板类,它是对jdbc的封装。用于支持持久层的操作。它的特点是:简单、方便。它简化了JDBC的使用并有助于避免常见错误。它执行核心的JDBC工作流程,留下应用程序代码以提供SQL并提取结果。query update pstmt.executeUpdate(insert,update,delete ,create,drop)
此类执行SQL查询或更新,在ResultSets上启动迭代并捕获JDBC异常,并将其转换为org.springframework.dao 程序包中定义的通用异常,信息量更大的异常层次结构 。
环境准备
步骤:
1、创建表、录入数据
2、创建项目spring02_00_jdbcTemplate
,添加依赖
3、编写实体类
实现:
1、创建表、录入数据
2、创建项目spring02_00_jdbcTemplate
,添加依赖
<?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>xxx.xxx</groupId>
<artifactId>spring02_00_jdbcTemplate</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--spring核心支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!--springjdbc支持包,提供了JdbcTemplate-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
2、编写实体类
public class Account {
private Integer accountId;
private Integer uid;
private Double money;
// 省略get、set、toString()
}
测试类
查询
需求
- 实现查询全部
- 实现根据ID查询
实现
public class App1_query {
private JdbcTemplate jdbcTemplate;
// 初始化:每次再执行junit方法之前先执行@Before注解修饰的方法
@Before
public void before(){
// 创建连接池
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///mybatis");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 创建jdbc模板工具类
jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* 1. 查询全部 (查询列与对象属性一致)
* 注意:
* A. 通过BeanPropertyRowMapper把查询结果自动封装为对象;
* B. 要求:查询的列,一定要与对象属性一致,才可以正确封装(不区分大小写)
*/
@Test
public void findAll_1() {
// 查询
List<Account> list = jdbcTemplate.query(
"select * from account",
new BeanPropertyRowMapper<Account>(Account.class));
System.out.println("list = " + list);
}
/**
* 2. 查询全部 (查询列与对象属性不一致)
*/
@Test
public void findAll_2() {
String sql = "SELECT accountId id,uid userId,money FROM account";
List<Account> list = jdbcTemplate.query(sql, new RowMapper<Account>() {
// 参数1:结果集;参数2:当前的行号
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
// 告诉jdbcTemplate,如何封装一行数据
Account account = new Account();
account.setAccountId(rs.getInt("id"));
account.setUid(rs.getInt("userId"));
account.setMoney(rs.getDouble("money"));
return account;
}
});
System.out.println("list = " + list);
}
/**
* 根据主键查询
*/
@Test
public void findById() {
// 查询条件
Integer id = 1;
// 执行查询
Account account =
jdbcTemplate.queryForObject(
"select * from account where accountId=?",
new BeanPropertyRowMapper<Account>(Account.class),id);
System.out.println(account);
}
}
更新
public class App2_update {
private JdbcTemplate jdbcTemplate;
// 初始化:每次再执行junit方法之前先执行@Before注解修饰的方法
@Before
public void before(){
// 创建连接池
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///mybatis");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 创建jdbc模板工具类
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
public void delete() {
//jdbcTemplate.update("insert/update/delete/drop/create...");
// 执行删除 (增删改都是update方法)
jdbcTemplate.update("delete from account where accountId=?",3);
}
}
使用 spring 的 IOC 的实现账户的CRUD(一)搭建环境
需求
-
实现账户表的crud操作:
-
表结构
-
技术架构
A. 对象管理: IOC容器
B. 操作数据库: JdbcTemplate
C. 连接池: 使用DruidDataSource
步骤
搭建好项目的环境:
- 创建项目:spring02_01_crud_xml
- 添加依赖
- 编写Account实体类
- 编写dao接口、实现
- 编写service接口、实现
实现
搭建好项目的环境:
-
创建项目:spring02_01_crud_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>xxx.xxx</groupId> <artifactId>spring02_01_crud_xml</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
-
编写Account实体类
public class Account { private Integer accountId; private Integer uid; private Double money; public Integer getAccountId() { return accountId; } public void setAccountId(Integer accountId) { this.accountId = accountId; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "accountId=" + accountId + ", uid=" + uid + ", money=" + money + '}'; } }
-
编写dao接口、实现
接口
public interface AccountDao { /** * 添加 * @param account */ void save(Account account); /** * 修改 * @param account */ void update(Account account); /** * 删除 * @param id */ void delete(Integer id); /** * 查询全部 * @return */ List<Account> findAll(); /** * 根据id查询 * @param id 查询的主键 * @return 根据主键查询的记录封装的对象并返回 */ Account findById(Integer id); }
实现
public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate; // 交给springioc容器注入jdbcTemplate对象。 (依赖注入) public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void save(Account account) { jdbcTemplate.update("insert into account values(null,?,?)",account.getUid(),account.getMoney()); } @Override public void update(Account account) { jdbcTemplate.update("update account set uid=?,money=? where accountId=?", account.getUid(),account.getMoney(),account.getAccountId()); } @Override public void delete(Integer id) { jdbcTemplate.update("delete from account where accountId=?",id); } @Override public List<Account> findAll() { return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<Account>(Account.class)); } @Override public Account findById(Integer id) { return jdbcTemplate.queryForObject("select * from account where accountId=?", new BeanPropertyRowMapper<Account>(Account.class),id); } }
-
编写service接口、实现
public interface AccountService { /** * 添加 * @param account */ void save(Account account); /** * 修改 * @param account */ void update(Account account); /** * 删除 * @param id */ void delete(Integer id); /** * 查询全部 * @return */ List<Account> findAll(); /** * 根据id查询 * @param id 查询的主键 * @return 根据主键查询的记录封装的对象并返回 */ Account findById(Integer id); }
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; // 交给ioc容器,依赖注入dao对象 public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void save(Account account) { accountDao.save(account); } @Override public void update(Account account) { accountDao.update(account); } @Override public void delete(Integer id) { accountDao.delete(id); } @Override public List<Account> findAll() { return accountDao.findAll(); } @Override public Account findById(Integer id) { return accountDao.findById(id); } }
使用 spring 的 IoC 的实现账户的CRUD(二)spring配置
步骤
1、编写applicationContext.xml ,SpringIOC容器配置
2、测试
实现
1、编写applicationContext.xml ,SpringIOC容器配置
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--创建连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--创建JdbcTemplate,注入连接池-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--创建dao,注入JdbcTemplate-->
<bean id="accountDao" class="xxx.xxx.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!--创建service,注入dao-->
<bean id="accountService" class="xxx.xxx.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
2、测试
public class App {
@Test
public void find() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取指定名称的service
AccountService accountService = (AccountService) ac.getBean("accountService");
// 调用service方法
System.out.println(accountService.findAll());
}
@Test
public void save() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取指定名称的service
AccountService accountService = (AccountService) ac.getBean("accountService");
// 调用service方法
Account account = new Account();
account.setUid(41); // 注意:uid是外键
account.setMoney(9999D);
accountService.save(account);
}
}
补充
可以把数据库连接信息放到jdbc.properties中,方便统一管理。所以项目可以先引入jdbc.properties文件
如何在springioc容器中,加载jdbc.properties配置文件?
核心代码
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--加载jdbc.properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--创建连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
...
...
...
注解的使用
基于注解的 IOC 配置(一)环境搭建
实现
步骤:
- 创建项目:spring02_02_annotation
- 添加依赖
- 创建User1对象
- 编写bean.xml配置文件
- 测试
实现:
-
创建项目:
spring02_02_annotation
-
添加依赖
<?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>xxx.xxx</groupId> <artifactId>spring02_02_annotation</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
-
创建User1对象
/** * @Component * 1. 作用:创建对象,加入ioc容器 * 2. 需要开启注解扫描 * 3. 加入容器的对象名称,默认是类名,首字母小写 * 相当于:<bean id="user1" class="..."/> * 4. 也可以指定加入容器中的对象的名称 * @Component 默认加入容器的对象名称是类名首字母小写,如:user1 * @Component("user") 加入容器的对象名称是user */ @Component("user") public class User1 { public User1(){ System.out.println("创建User对象"); } }
-
编写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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- component-scan 开启注解扫描 base-package 1. 指定扫描的包,会扫描当前包下所有的类,以及当前包下的所有子包下所有的类 (基包) 2. 如果扫描多个包逗号隔开 --> <context:component-scan base-package="xxx.xxx.entity"/> </beans>
-
测试
public class App { @Test public void test() { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); User1 user = (User1) ac.getBean("user"); System.out.println(user); } }
基于注解的 IOC 配置(二)用于创建对象的注解
注解说明
@Controller: 一般用于表现层的注解。 同@Component
@Service: 一般用于业务层的注解。 同@Component
@Repository: 一般用于持久层的注解。 同@Component
@Component: 除了上述情形以外的工具类,使用此注解。
代码演示
//@Controller("user") 用于修饰表现层的类 web
//@Service("user") 用于修饰业务层的类 service
//@Repository("user") 用于修饰持久层的类 dao
@Component("user") // 除了上面以外的类都用@component注解,比如工具类
public class User2 {
public User2(){
System.out.println("创建User对象");
}
}
基于注解的 IOC 配置(三)注入数据 A @Autowired修饰字段
注解说明
- @Autowired 作用: 给对象中的属性赋值
- 修饰在字段上,会自动根据字段的类型、或名称进行注入。
代码
第一步:
@Component("user")
public class User3 {
/**
* @Autowired
* 1. 作用:依赖注入的注解,给属性赋值。
* 2. 根据类型,自动去容器中找@Autowired修饰的字段的类型注入(赋值)。
* 3. 根据名称,如果容器中该类型对应的对象有多个,会根据字段名称去容器中找对象注入。
* 如果根据名称没有找到,就报错。
* 4. 属性
* required
* true 默认,表示去容器中找对象注入,没有找到就报错。
* false 去容器中找对象,找不到不报错,默认NULL值
*/
@Autowired
private String name;
@Override
public String toString() {
return "User3{" +
"name='" + name + '\'' +
'}';
}
}
第二步:
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--
component-scan 开启注解扫描
base-package
1. 指定扫描的包,会扫描当前包下所有的类,以及当前包下的所有子包下所有的类 (基包)
2. 如果扫描多个包逗号隔开
-->
<context:component-scan base-package="xxx.xxx.entity"/>
<!--创建一个字符串-->
<bean id="str" class="java.lang.String">
<constructor-arg value="Rose"/>
</bean>
<bean id="name" class="java.lang.String">
<constructor-arg value="Jacky"/>
</bean>
</beans>
基于注解的 IOC 配置(四)注入数据 B @Autowired修饰方法
代码
@Autowired修饰方法与修饰在字段上基本一样:
第一步:
@Component("user")
public class User4 {
/**
* 1. 根据形参类型,去容器中找该类型的对象注入。
* 2. 如果该类型的对象有多个,就根据形参名称注入
*/
@Autowired
public void setName(String name){
System.out.println(name);
}
}
第二步:
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xxx.xxx.entity"/>
<!--创建一个字符串-->
<bean id="str" class="java.lang.String">
<constructor-arg value="Rose"/>
</bean>
<bean id="name" class="java.lang.String">
<constructor-arg value="Jacky"/>
</bean>
</beans>
基于注解的 IOC 配置(四)注入数据 C @Qualifier
注解说明
- @Autowired注解,只能根据字段类型或字段名称注入,不能自己指定名称注入。
- @Qualifier注解可以让@Autowired注解按照指定名称注入。
- 通常:@Autowired与@Qualifier都是配置一起使用的。
代码
@Component("user")
public class User5 {
/**
* @Qualifier
* 1. 可以让@Autowired根据指定的名称注入
* 2. 通常:@Autowired与@Qualifier要配置使用。
* 3. @Qualifier可以单独使用,用于方法参数。
*/
@Autowired
@Qualifier("str2") // 去容器中找str2对应的对象注入
private String name;
@Override
public String toString() {
return "User5{" +
"name='" + name + '\'' +
'}';
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xxx.xxx.entity"/>
<!--创建一个字符串-->
<bean id="str1" class="java.lang.String">
<constructor-arg value="Rose"/>
</bean>
<bean id="str2" class="java.lang.String">
<constructor-arg value="Jacky"/>
</bean>
</beans>
基于注解的 IOC 配置(五)注入数据 D @Resource
注解说明
- 在jdk1.8以后版本不提供支持
- @Resource可以根据名称、类型注入
代码
@Component("user")
public class User6 {
/**
* @Resource
* 1. 实现依赖注入的注解
* 2. 相当于@Autowired + @Qualifier
* 3. 通过属性指定只根据名称或类型注入
*/
@Resource(name = "str2")
private String name;
@Override
public String toString() {
return "User5{" +
"name='" + name + '\'' +
'}';
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xxx.xxx.entity"/>
<!--创建一个字符串-->
<bean id="str1" class="java.lang.String">
<constructor-arg value="Rose"/>
</bean>
<bean id="str2" class="java.lang.String">
<constructor-arg value="Jacky"/>
</bean>
</beans>
基于注解的 IOC 配置(六)注入数据 E @Value
注解说明
- 直接给简单类型的属性赋值
- 也可以配置文件取值
代码
@Component("user")
public class User7 {
/**
* @Value
* 1. 直接给简单类型的字段赋值
* 2. 也可以获取配置文件值
*/
@Value("100")
private int id;
@Value("Jacky")
private String name;
@Override
public String toString() {
return "User7{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
基于注解的 IOC 配置(七)对象范围与生命周期相关注解
注解说明
@Scope 【对象范围】
@PostConstruct 【修饰初始化方法,创建对象之后执行】
@PreDestroy 【修饰回收资源方法,销毁容器之前执行】
@Lazy 【延迟初始化】
对比XML:
<bean id="" class=""
scope="" 使用@Scope注解取代
init-method="" 使用@PostConstruct注解取代
destroy-method="" 使用@PreDestroy注解取代
lazy-init="" 使用@Lazy注解取代
/>
代码
第一步:
/**
* @Scope
* 1. 对象范围
* 2. 属性值
* singleton 默认单例
* prototype 表示多例
* @Lazy
* 1. 对象懒加载
* 2. 如果不设置默认单例的对象在创建容器时候就创建,设置懒加载后,第一次用的时候才创建
* @PostConstruct
* 1. 执行初始化操作
* 2. 创建对象之后执行
* @PreDestroy
* 1. 用于释放资源
* 2. 对单例有效,且要调用容器的close()方法才触发
*/
@Component("user")
@Scope("singleton")
@Lazy
public class User8 {
public User8(){
System.out.println("创建User对象");
}
@PostConstruct
public void init(){
System.out.println("..init");
}
@PreDestroy
public void destroy(){
System.out.println("...destroy");
}
}
第二步:测试
public class App {
@Test
public void test() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
User8 temp1 = (User8) ac.getBean("user");
User8 temp2 = (User8) ac.getBean("user");
System.out.println(temp1);
System.out.println(temp2);
// 销毁容器
ac.close();
}
}
总结
12个注解
- 创建对象相关的四个注解:
@Component 创建对象加入容器, 例如:工具类、其他组件
@Repository 创建对象加入容器, 例如:标识数据库访问层的组件
@Service 创建对象加入容器, 例如:标识乐屋逻辑层的组件
@Controller 创建对象加入容器 例如:标识控制层的组件
- 依赖注入的四个注解:
@Autowired 从容器中找对象,给属性赋值。根据类型、名称去容器中找。
@Qualifier 结合Autowired使用,可以指定按照名称去容器找对象注入。
@Value 给简单类型属性直接赋值/获取配置文件值@Value(“${key}”)
@Resource 从容器中找对象,给属性赋值。 根据名称、类型去容器中查找
- 生命周期相关的四个注解:
@Scope 单例/多例
@PostConstruct 初始化方法,创建对象后执行
@PreDestroy 销毁先执行
@Lazy 延迟初始化
- 重点:@Component 、@Autowired
使用原则
使用原则:
- 无论使用XML还是注解实现IOC容器,底层实现都是一样的,做的时期都是一样的。
- 这两种都要掌握:XML、注解
- 哪种方便我们开发使用,就应用哪些方式(注解、xml)
项目应用
推荐用法:
- 自己写的类用注解
- jar包中的类用XML
注解改造【案例】
案例:使用注解改造案例
实现
步骤:
- 创建项目、准备项目环境:spring02_03_crud_anno
- 修改dao实现
- 修改service实现
- 修改applicationContext.xml配置
实现:
-
创建项目、准备项目环境:spring02_03_crud_anno
-
修改dao实现
// 创建对象,加入容器,加入容器的对象名称默认是类名首字母小写:accountDaoImpl @Repository public class AccountDaoImpl implements AccountDao { // 依赖注入:去容器中找JdbcTemplate类型的对象。 @Autowired private JdbcTemplate jdbcTemplate; ..... }
-
修改service实现
// 创建对象,加入容器,加入容器的对象名称默认是类名首字母小写:accountServiceImpl @Service public class AccountServiceImpl implements AccountService { // 依赖注入:去容器中找AccountDao类型的对象注入 @Autowired private AccountDao accountDao; .... }
-
修改applicationContext.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 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="xxx.xxx"/> <!--加载jdbc.properties配置文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--创建连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--创建JdbcTemplate,注入连接池--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
测试
public class App {
@Test
public void find() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取指定名称的service
AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
// 调用service方法
System.out.println(accountService.findAll());
}
@Test
public void save() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 根据类型去容器中找该类型对象的对象
AccountService accountService = ac.getBean(AccountService.class);
// 调用service方法
Account account = new Account();
account.setUid(41); // 注意:uid是外键
account.setMoney(9999D);
accountService.save(account);
}
}
纯注解改造【案例】
spring纯注解配置(一)环境准备
关键点
- 想要取代配置,使用纯注解。就要找到每一个配置对应的注解
- 分析: 涉及哪些注解?
spring纯注解配置(二)@Configuration与@ComponentScan
注解说明
@Configuration 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。 获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。
@ComponentScan用于指定 spring 在初始化容器时要扫描的包。
作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package=“xxx.xxx”/>是一样的。
代码
步骤
- 准备项目环境、创建项目:spring02_04_crud_fullanno
- 编写SpringConfiguration, 注解配置类
- 修改测试
实现:…
-
准备项目环境、创建项目:spring02_04_crud_fullanno
-
编写SpringConfiguration, 注解配置类
/** * @Configuration * 1. 修饰一个配置管理类,用来取代配置文件 * 2. 创建容器时候,加载此注解修饰的类即可 * * @ComponentScan * 1. 注解扫描 * 2. 取代<context:component-scan base-package="xxx.xxx"/>这里的配置 * */ @Configuration @ComponentScan("xxx.xxx") public class SpringConfiguration { }
-
修改测试 (现在还不能执行,没有写完…)
public class App { @Test public void find() { // 创建容器 ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); // 从容器中获取对象 AccountService accountService = ac.getBean(AccountService.class); // 查询 System.out.println(accountService.findAll()); } }
spring纯注解配置(三)@Bean、@PropertySource、@Import
注解说明
@Bean 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器
@PropertySource 用于加载.properties 配置文件
@Import用于导入其他配置类
代码
-
定义一个类:JdbcConfig, 作用:封装数据库操作相关的配置(如:加载配置文件、创建连接池、创建JdbcTemplate)
/** * @PropertySource * 1. 加载配置文件 * 2. 相当于<context:property-placeholder location=""/> */ @PropertySource("jdbc.properties") public class JdbcConfig { /*配置文件取值*/ @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 * 1. 修改在方法上,自动把方法返回的对象加入ioc容器; * 2. name属性,可以指定加入容器的对象的名称(可选) * 举例:@Bean(name = "dataSource") * */ @Bean public DataSource createDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(this.driver); dataSource.setUrl(this.url); dataSource.setUsername(this.username); dataSource.setPassword(this.password); return dataSource; } /** * 创建jdbc模板工具类对象,加入容器。 * @Bean * 1. 自动把方法返回的对象加入容器 * 2. 如果方法有参数,自动根据参数的类型进行注入;如果类型有多个,根据形参名称注入 */ @Bean public JdbcTemplate createJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } }
-
在SpringConfiguration类中加载JdbcConfig
/** * @Configuration * 1. 修饰一个配置管理类,用来取代配置文件 * 2. 创建容器时候,加载此注解修饰的类即可 * * @ComponentScan * 1. 注解扫描 * 2. 取代<context:component-scan base-package="xxx.xxx"/>这里的配置 * * @Import * 1. 加载其他的配置管理类 * 2. 相当于配置文件中的<import resource=""/> */ @Configuration @ComponentScan("xxx.xxx") @Import(JdbcConfig.class) public class SpringConfiguration { }
spring纯注解配置(四)测试@Qualifier单独使用
修改JdbcConfig.java
/**
* @PropertySource
* 1. 加载配置文件
* 2. 相当于<context:property-placeholder location=""/>
*/
@PropertySource("jdbc.properties")
public class JdbcConfig {
/*配置文件取值*/
@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
* 1. 修改在方法上,自动把方法返回的对象加入ioc容器;
* 2. name属性,可以指定加入容器的对象的名称(可选)
* 举例:@Bean(name = "dataSource")
*
*/
@Bean(name = "dataSource1")
public DataSource createDataSource1(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(this.driver);
dataSource.setUrl(this.url);
dataSource.setUsername(this.username);
dataSource.setPassword(this.password);
return dataSource;
}
@Bean(name = "dataSource2")
public DataSource createDataSource2(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(this.driver);
dataSource.setUrl(this.url);
dataSource.setUsername(this.username);
dataSource.setPassword(this.password);
return dataSource;
}
/**
* 创建jdbc模板工具类对象,加入容器。
* @Bean
* 1. 自动把方法返回的对象加入容器
* 2. 如果方法有参数,自动根据参数的类型进行注入;如果类型有多个,根据形参名称注入
*
* @Qualifier
* 1. 一般配置@Autowired一起使用
* 2. 也可以单独使用,修饰在方法的参数上,根据指定的名称注入。
*/
@Bean
public JdbcTemplate createJdbcTemplate(@Qualifier("dataSource1") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
测试
Spring整合Junit
应用
-
为什么要Spring整合Junit? 主要是为了简化测试而已。
-
如果要用spring整合junit,需要先引入spring-test包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
- 纯注解开发,Spring整合Junit测试:
// 指定容器加载类
@RunWith(SpringJUnit4ClassRunner.class)
// 指定注解配置类
@ContextConfiguration(classes = SpringConfiguration.class)
public class App2 {
// 依赖注入,容器中的对象
@Autowired
private AccountService accountService;
@Test
public void find() {
System.out.println(accountService.findAll());
}
}
- XML配置,Spring整合Junit测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class App2 {
@Autowired
private AccountService accountService;
@Test
public void find() {
System.out.println(accountService.findAll());
}
}
动态代理回顾
代理---->Aop----->Spring声明式事务
Aop原理之代理(一)代理的介绍
概念
-
什么是代理?
Proxy,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
-
java中动态代理分为哪两类?
- jdk动态代理,接口代理
- cglib动态代理,子类代理
-
代理的好处
不用修改目标对象, 实现在目标对象的基础上进行扩展。
Aop原理之代理(二)案例需求、环境搭建
案例需求
已经实现了账户的crud,要求在执行账户的crud的时候,自动记录日志。
模拟实现需求
在service方法中增加打印日志输出的代码,默认日志的记录。
@Override
public List<Account> findAll() {
System.out.println("执行AccountServiceImpl类的save()方法开始:");
List<Account> list = accountDao.findAll();
System.out.println("执行AccountServiceImpl类的save()方法结束:");
return list;
}
测试
Aop原理之代理(三)JDK动态代理
介绍
通过JDK动态代理实现对service方法增强,添加自动记录日志的实现。
代码
现在在junit测试中创建目标对象的代理对象,通过代理实现对目标方法添加日志记录的实现。
public class App3_proxy {
@Test
public void find() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取对象 【目标对象】
AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
/**
* 对目标对象accountService,生成代理
* JDK动态代理
* 1、API
* |-- Proxy
* |-- static Objeject newProxyInstance(
* 参数1:类加载器,
* 参数2:目标对象实现的接口类型数组,
* 参数3:事件处理器,当执行代理方法时候会触发事件处理程序,传入当前执行的方法
* )
* 2、原理
* 在运行时期动态生成字节码,通过动态生成的字节码创建对象。
* 原理:class $Proxy123 implements AccountService
* 使用:AccountService proxy = $Proxy123;
*/
AccountService proxy = (AccountService) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 当前执行的方法名称
String methodName = method.getName();
// 当前执行的类全名
String className = method.getDeclaringClass().getName();
try {
System.out.println("执行"+className+"类"+methodName+"()方法,开始:");
// 调用目标对象的方法
Object obj = method.invoke(accountService,args);
System.out.println("执行"+className+"类"+methodName+"()方法,结束。");
return obj;
} catch (IllegalAccessException e) {
e.printStackTrace();
System.out.println("执行"+className+"类"+methodName+"()方法,出现异常。");
return null;
}
}
});
// 执行代理的方法
List<Account> list = proxy.findAll();
System.out.println("list = " + list);
}
}
测试
注意:现在service方法中并没有记录日志,而执行结果有日志输出!
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
Aop原理之代理(四)CGLIB动态代理
分析
-
目标对象: class AccountServiceImpl
-
jdk代理: class Proxy0 implements AccountService
-
cglib子类代理: class $Cglib12345 extends AccountServiceImpl
cglib子类代理: 在运行时期动态生成目标对象的子类,从而对目标对象进行扩展。
-
目前spring-core包中已经包含cglib的功能,所以可以直接使用。老旧spring版本需要额外导入cglib包。
实现
第一步:对目标对象,通过cglib生成代理
public class App4_proxy_cglib {
@Test
public void find() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取对象 【目标对象】
AccountServiceImpl accountServiceImpl =
(AccountServiceImpl) ac.getBean("accountServiceImpl");
/**
* cglib动态代理(子类代理)
*/
AccountServiceImpl proxy = (AccountServiceImpl) Enhancer.create(accountServiceImpl.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 当前执行的方法名称
String methodName = method.getName();
// 当前执行的类全名
String className = method.getDeclaringClass().getName();
try {
System.out.println("执行"+className+"类"+methodName+"()方法,开始:");
// 调用目标对象的方法
Object obj = method.invoke(accountServiceImpl,args);
System.out.println("执行"+className+"类"+methodName+"()方法,结束。");
return obj;
} catch (IllegalAccessException e) {
e.printStackTrace();
System.out.println("执行"+className+"类"+methodName+"()方法,出现异常。");
return null;
}
};
});
System.out.println(proxy.getClass());
// 执行代理的方法
List<Account> list = proxy.findAll();
System.out.println("list = " + list);
}
}
第二步:测试结果
AOP与Java中代理关系
AOP与Java中代理有什么关系?
-
Aop编程中:
- 如果目标对象有实现接口,spring的aop会使用jdk动态代理;
- 如果目标对象没有实现接口,spring的aop会使用cglib子类代理(目标对象不能为final)。
-
使用了Spring的Aop编程,代理就不用自己写了。(已经提供了代理的工厂)
注意:
如果使用jdk动态代理,必须通过接口的引用接收代理对象,不能通过实现类接收。
比如下面的代码会报错:
@Test
public void find() {
// 创建容器
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取对象 【目标对象】
AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
// System.out.println(accountService.findAll());
// 对目标对象生成代理
// 原理: class $Proxy123 implements AccountService
// AccountService a1 = $Proxy123; OK
// AccountServiceImpl a2 = $Proxy123; NOK 类型转换错误 ClassCastException
//AccountService proxy = (AccountService) Proxy.newProxyInstance( 正确的代码
AccountServiceImpl proxy = (AccountServiceImpl) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在不修改原有方法的基础上,扩展输出日志功能");
return method.invoke(accountService,args);
}
});
// 生成的代理类 class com.sun.proxy.$Proxy13
System.out.println(proxy.getClass());
// 调用代理方法
System.out.println(proxy.findAll());
}