目录
- 01.Spring框架简介
- 02.程序间耦合
- 03.Spring的 IOC 和 DI
- 04.Spring的常用注解
- 05.基于XML的IOC的案例1
- 06.Spring的新注解
- 07.银行转账案例
- 08.面向切面编程 AOP
- 09.JdbcTemplate的基本使用
- 10.Spring中事务控制
===================================================
01.Spring框架简介
01.spring课程四天安排
02.今日课程内容介绍
03.spring概述
04.spring发展历程
05.spring的优势
06.spring的体系结构
===================================================
02.程序间耦合
编写jdbc的工程代码用于分析程序的耦合
<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>com.spring</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>
/**
* 程序的耦合
* 耦合:程序间的依赖关系
* 包括:
* 类之间的依赖
* 方法间的依赖
* 解耦:
* 降低程序间的依赖关系
* 实际开发中:
* 应该做到:编译期不依赖,运行时才依赖。
* 解耦的思路:
* 第一步:使用反射来创建对象,而避免使用new关键字。
* 第二步:通过读取配置文件来获取要创建的对象全限定类名
*
*/
package com.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcDemo {
public static void main(String[] args) throws Exception{
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/easy","root","root");
//3.获取操作数据库的预处理对象
PreparedStatement pstm = conn.prepareStatement("select * from account");
//4.执行SQL,得到结果集
ResultSet rs = pstm.executeQuery();
//5.遍历结果集
while(rs.next()){
System.out.println(rs.getString("name"));
}
//6.释放资源
rs.close();
pstm.close();
conn.close();
}
}
此中DriverManager.registerDriver(new com.mysql.jdbc.Driver());被代码Class.forName("com.mysql.jdbc.Driver");所替换。为什么呢?因为new com.mysql.jdbc.Driver()编译时候需要包mysql-connector-java,否则编译不通过。而Class.forName("com.mysql.jdbc.Driver")编译时候可以没有jdbc包达到解耦,编译期不依赖,运行时才依赖。
编译期依赖
程序的耦合和解耦的思路分析1
曾经代码中的问题分析
package com.bbb.factory.IAccountDao;
public interface IAccountDao{
/**
* 模拟保存账户
*/
void saveAccount();
}
package com.bbb.factory.IAccountDao.impl;
import com.bbb.factory.IAccountDao.IAccountDao;
public class AccountDao implements IAccountDao{
public void saveAccount() {
System.out.println("保存了账户");
}
}
package com.bbb.factory.IAccountService;
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
}
package com.bbb.factory.IAccountService.impl;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService implements IAccountService {
private IAccountDao accountDao = new AccountDao();
public void saveAccount() {
accountDao.saveAccount();
}
}
package com.bbb.factory.client;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService;
public class Client {
public static void main(String[] args) {
IAccountService as = new AccountService();
as.saveAccount();
}
}
可以看到IAccountService as = new AccountService();还有private IAccountDao accountDao = new AccountDao();导致了耦合问题,为了降低程序间的依赖关系,应该对代码进行改造。
编写工厂类和配置文件
工厂模式解耦
accountService=com.bbb.factory.IAccountService.impl.AccountService2
accountDao=com.bbb.factory.IAccountDao.impl.AccountDao
package com.bbb.factory.BeanFactory;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
private static Properties props;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据Bean的名称获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
package com.bbb.factory.IAccountService.impl;
import com.bbb.factory.BeanFactory.BeanFactory;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService2 implements IAccountService {
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
public void saveAccount() {
accountDao.saveAccount();
}
}
package com.bbb.factory.client;
import com.bbb.factory.BeanFactory.BeanFactory;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService;
public class Client2 {
public static void main(String[] args) {
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
as.saveAccount();
}
}
运行结果:
com.bbb.factory.IAccountService.impl.AccountService2
com.bbb.factory.IAccountDao.impl.AccountDao
保存了账户
分析工厂模式中的问题并改造
package com.bbb.factory.client;
import com.bbb.factory.BeanFactory.BeanFactory;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService;
public class Client3 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(as);
}
}
}
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
com.bbb.factory.IAccountService.impl.AccountService2@7852e922
com.bbb.factory.IAccountService.impl.AccountService2@4e25154f
com.bbb.factory.IAccountService.impl.AccountService2@70dea4e
com.bbb.factory.IAccountService.impl.AccountService2@5c647e05
上面的代码是多例的
工厂模式解耦的升级版
package com.bbb.factory.BeanFactory;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory2 {
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory2.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
package com.bbb.factory.client;
import com.bbb.factory.BeanFactory.BeanFactory2;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService;
public class Client4 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
IAccountService as = (IAccountService) BeanFactory2.getBean("accountService");
System.out.println(as);
}
}
}
运行结果:
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
com.bbb.factory.IAccountService.impl.AccountService2@6d06d69c
===================================================
03.Spring的 IOC 和 DI
1 ioc的概念和作用
2 spring中的Ioc前期准备
3 spring基于XML的IOC环境搭建和入门
<?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">
<!--把对象的创建交给spring来管理-->
<bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService3"></bean>
<bean id="accountDao" class="com.bbb.factory.IAccountDao.impl.AccountDao"></bean>
</beans>
package com.bbb.factory.IAccountService.impl;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService3 implements IAccountService {
private IAccountDao accountDao;
public void saveAccount() {
accountDao.saveAccount();
}
}
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client5 {
public static void main(String[] args) {
// 1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2.根据id获取Bean对象
IAccountService as = (IAccountService) ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(adao);
as.saveAccount();
}
}
运行结果:
十一月 02, 2020 5:17:54 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5a2e4553: startup date [Mon Nov 02 17:17:54 CST 2020]; root of context hierarchy
十一月 02, 2020 5:17:54 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [bean.xml]
com.bbb.factory.IAccountService.impl.AccountService3@475530b9
com.bbb.factory.IAccountDao.impl.AccountDao@1d057a39
Exception in thread "main" java.lang.NullPointerException
at com.bbb.factory.IAccountService.impl.AccountService3.saveAccount(AccountService3.java:13)
at com.bbb.factory.client.Client5.main(Client5.java:21)
4 ApplicationContext的三个实现类
5 BeanFactory和ApplicationContext的区别
package com.itheima.ui;
import com.itheima.dao.IAccountDao;
import com.itheima.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
/**
* 获取spring的Ioc核心容器,并根据id获取对象
*
* ApplicationContext的三个常用实现类:
* ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
* FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
*
* AnnotationConfigApplicationContext:它是用于读取注解创建容器的,是明天的内容。
*
* 核心容器的两个接口引发出的问题:
* ApplicationContext: 单例对象适用 采用此接口
* 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
*
* BeanFactory: 多例对象使用
* 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// ApplicationContext ac = new FileSystemXmlApplicationContext("C:\Users\zhy\Desktop\bean.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(adao);
as.saveAccount();
//--------BeanFactory----------
// Resource resource = new ClassPathResource("bean.xml");
// BeanFactory factory = new XmlBeanFactory(resource);
// IAccountService as = (IAccountService)factory.getBean("accountService");
// System.out.println(as);
}
}
判断对象是否创建看构造方法。
6 spring中bean的细节之三种创建Bean对象的方式
使用默认构造函数创建
package com.bbb.factory.IAccountService.impl;
import java.util.concurrent.SynchronousQueue;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService4 implements IAccountService {
public AccountService4(String name){
System.out.println("AccountService4 创建了");
}
public void saveAccount() {
System.out.println("AccountService4.saveAccount()");
}
}
<?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的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
-->
<bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService4"></bean>
</beans>
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client6 {
public static void main(String[] args) {
// 1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
// 2.根据id获取Bean对象
IAccountService as = (IAccountService) ac.getBean("accountService");
as.saveAccount();
}
}
运行结果:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'accountService' defined in class path resource [bean2.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.bbb.factory.IAccountService.impl.AccountService4]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.bbb.factory.IAccountService.impl.AccountService4.<init>()
因为com.bbb.factory.IAccountService.impl.AccountService4中没有默认构造函数。
更改后:
package com.bbb.factory.IAccountService.impl;
import java.util.concurrent.SynchronousQueue;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService4 implements IAccountService {
public AccountService4(){
System.out.println("AccountService4 创建了");
}
public void saveAccount() {
System.out.println("AccountService4.saveAccount()");
}
}
运行结果:
信息: Loading XML bean definitions from class path resource [bean2.xml]
AccountService4 创建了
AccountService4.saveAccount()
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
package com.ccc.factory.BeanFactory;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService4;
/**
* 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
*/
public class InstanceFactory {
public IAccountService getAccountService(){
return new AccountService4();
}
}
package com.ccc.factory.BeanFactory;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService4;
/**
* 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
*/
public class StaticFactory {
public static IAccountService getAccountService(){
return new AccountService4();
}
}
<?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的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。 <bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService4"></bean> -->
<!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器) -->
<bean id="instanceFactory" class="com.ccc.factory.BeanFactory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory"
factory-method="getAccountService"></bean>
</beans>
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client7 {
public static void main(String[] args) {
// 1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
// 2.根据id获取Bean对象
IAccountService as = (IAccountService) ac.getBean("accountService");
as.saveAccount();
}
}
运行结果:
信息: Loading XML bean definitions from class path resource [bean2.xml]
AccountService4 创建了
AccountService4.saveAccount()
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
package com.ccc.factory.BeanFactory;
import com.bbb.factory.IAccountService.IAccountService;
import com.bbb.factory.IAccountService.impl.AccountService4;
/**
* 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
*/
public class StaticFactory {
public static IAccountService getAccountService(){
return new AccountService4();
}
}
<?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的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。 <bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService4"></bean> -->
<!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.ccc.factory.BeanFactory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory"
factory-method="getAccountService"></bean>
-->
<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
-->
<bean id="accountService" class="com.ccc.factory.BeanFactory.StaticFactory" factory-method="getAccountService"></bean>
</beans>
信息: Loading XML bean definitions from class path resource [bean2.xml]
AccountService4 创建了
AccountService4.saveAccount()
singleton单例对象的销毁
package com.bbb.factory.IAccountService.impl;
import java.util.concurrent.SynchronousQueue;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class AccountService5 implements IAccountService {
public AccountService5(){
System.out.println("AccountService4 创建了");
}
public void saveAccount() {
System.out.println("AccountService4.saveAccount()");
}
public void init(){
System.out.println("对象初始化了。。。");
}
public void destroy(){
System.out.println("对象销毁了。。。");
}
}
<?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">
<!--把对象的创建交给spring来管理-->
<!--spring对bean的管理细节
1.创建bean的三种方式
2.bean对象的作用范围
3.bean对象的生命周期
-->
<!--创建Bean的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService4"></bean> -->
<!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.ccc.factory.BeanFactory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory"
factory-method="getAccountService"></bean>
-->
<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
<bean id="accountService" class="com.ccc.factory.BeanFactory.StaticFactory" factory-method="getAccountService"></bean>
-->
<!-- bean的作用范围调整
bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的
singleton:单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
-->
<!-- bean对象的生命周期
单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着。
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
-->
<bean id="accountService" class="com.bbb.factory.IAccountService.impl.AccountService5" scope="singleton" init-method="init" destroy-method="destroy"></bean>
</beans>
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client8 {
public static void main(String[] args) {
// 1.获取核心容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
// 2.根据id获取Bean对象
IAccountService as1 = (IAccountService) ac.getBean("accountService");
IAccountService as2 = (IAccountService) ac.getBean("accountService");
System.out.println(as1==as2);
//手动关闭容器
ac.close();
}
}
运行结果:
信息: Loading XML bean definitions from class path resource [bean2.xml]
AccountService4 创建了
对象初始化了。。。
true
十一月 02, 2020 6:22:55 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@5a2e4553: startup date [Mon Nov 02 18:22:55 CST 2020]; root of context hierarchy
对象销毁了。。。
多例对象的销毁
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
scope="prototype" init-method="init" destroy-method="destroy"></bean>
单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着。
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
7 spring中bean的细节之作用范围
8 spring中bean的细节之生命周期
9 spring的依赖注入
10 构造函数注入
构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值 常用的
=============以上三个用于指定给构造函数中哪个参数赋值===========
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
package com.ddd.service.impl;
import java.util.Date;
import com.ddd.service.IAccountService;
public class AccountService implements IAccountService {
//如果是经常变化的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public AccountService(String name,Integer age,Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveAccount(){
System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
}
}
<?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="accountService" class="com.ddd.service.impl.AccountService">
<constructor-arg name="name" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
</beans>
package com.ddd.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ddd.service.IAccountService;
public class Client {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean3.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
as.saveAccount();
}
}
运行结果:
信息: Loading XML bean definitions from class path resource [bean3.xml]
service中的saveAccount方法执行了。。。泰斯特,18,Mon Nov 02 18:46:04 CST 2020
11 set方法注入
set方法注入 更常用的方式
涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
package com.ddd.service.impl;
import java.util.Date;
import com.ddd.service.IAccountService;
public class AccountService2 implements IAccountService {
//如果是经常变化的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveAccount(){
System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
}
}
<?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="accountService" class="com.ddd.service.impl.AccountService">
<constructor-arg name="name" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
<bean id="accountService2" class="com.ddd.service.impl.AccountService2">
<property name="name" value="TEST" ></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
</beans>
package com.ddd.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ddd.service.IAccountService;
public class Client2 {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean3.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService2");
as.saveAccount();
}
}
运行结果:
信息: Loading XML bean definitions from class path resource [bean3.xml]
service中的saveAccount方法执行了。。。TEST,21,Mon Nov 02 18:54:56 CST 2020
注意:
<property name="name" value="TEST" ></property>中的name=""属性取决于class AccountService2中的set方法:去掉set后面小写。
12 注入集合数据
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import com.ddd.service.IAccountService;
public class AccountServiceImpl3 implements IAccountService{
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
public void saveAccount(){
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}
<?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="accountService" class="com.ddd.service.impl.AccountService">
<constructor-arg name="name" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
<bean id="accountService2" class="com.ddd.service.impl.AccountService2">
<property name="name" value="TEST"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="accountService3" class="com.ddd.service.impl.AccountServiceImpl3">
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
</bean>
</beans>
package com.ddd.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ddd.service.IAccountService;
public class Client3 {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean3.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService3");
as.saveAccount();
}
}
运行结果:
[AAA, BBB, CCC]
[AAA, BBB, CCC]
[AAA, BBB, CCC]
{testA=aaa, testB=BBB}
{testD=ddd, testC=ccc}
复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:
list array set
用于个Map结构集合注入的标签:
map props
结构相同,标签可以互换
13 课程知识梳理
源码:hello
===================================================
04.Spring的常用注解
1 今日课程内容介绍
2 常用IOC注解按照作用分类
* 用于创建对象的
* 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
* Controller:一般用在表现层
* Service:一般用在业务层
* Repository:一般用在持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
*
*
* 用于注入数据的
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
* 属性:
* value:用于指定注入bean的id。
* Resource
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id。
* 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
* 另外,集合类型的注入只能通过XML来实现。
*
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
*
* 用于改变作用范围的
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* Scope
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围的取值。常用取值:singleton prototype
*
* 和生命周期相关 了解
* 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
* PreDestroy
* 作用:用于指定销毁方法
* PostConstruct
* 作用:用于指定初始化方法
3 用于创建的Component注解
<?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">
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为
context名称空间和约束中-->
<context:component-scan base-package="com.bbb.factory"></context:component-scan>
</beans>
package com.bbb.factory.IAccountService.impl;
import org.springframework.stereotype.Component;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
/**
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
*/
@Component("accountService")
public class AccountService implements IAccountService {
private IAccountDao accountDao;
public AccountService() {
System.out.println("AccountService()创建了");
}
public void saveAccount() {
accountDao.saveAccount();
}
}
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
System.out.println(as);
}
}
信息: Loading XML bean definitions from class path resource [bean.xml]
AccountService()创建了
com.bbb.factory.IAccountService.impl.AccountService@68ceda24
4 由Component衍生的注解
package com.bbb.factory.IAccountDao.impl;
import org.springframework.stereotype.Repository;
import com.bbb.factory.IAccountDao.IAccountDao;
@Repository("accountDao")
public class AccountDao implements IAccountDao{
public void saveAccount() {
System.out.println("保存了账户");
}
}
package com.bbb.factory.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountService.IAccountService;
public class Client {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
IAccountDao ad = (IAccountDao)ac.getBean("accountDao");
System.out.println(as);
System.out.println(ad);
}
}
信息: Loading XML bean definitions from class path resource [bean.xml]
AccountService()创建了
com.bbb.factory.IAccountService.impl.AccountService@5a4041cc
com.bbb.factory.IAccountDao.impl.AccountDao@15b3e5b
5 自动按照类型注入
自动注入原理.png
package com.bbb.factory.IAccountService.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
/**
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
*/
@Service("accountService")
public class AccountService implements IAccountService {
//作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
@Autowired
private IAccountDao accountDao;
public AccountService() {
System.out.println("AccountService()创建了");
}
public void saveAccount() {
accountDao.saveAccount();
}
}
如果Ioc容器中有多个类型匹配时.png
![](.assets/ 如果Ioc容器中有多个类型匹配时.png)
package com.bbb.factory.IAccountDao.impl;
import org.springframework.stereotype.Repository;
import com.bbb.factory.IAccountDao.IAccountDao;
@Repository("accountDao1")
public class AccountDao implements IAccountDao{
public void saveAccount() {
System.out.println("保存了账户accountDao()");
}
}
package com.bbb.factory.IAccountDao.impl;
import org.springframework.stereotype.Repository;
import com.bbb.factory.IAccountDao.IAccountDao;
@Repository("accountDao2")
public class AccountDao2 implements IAccountDao{
public void saveAccount() {
System.out.println("保存了账户accountDao2()");
}
}
package com.bbb.factory.IAccountService.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.bbb.factory.IAccountDao.IAccountDao;
import com.bbb.factory.IAccountDao.impl.AccountDao;
import com.bbb.factory.IAccountService.IAccountService;
/**
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
*/
@Service("accountService")
public class AccountService implements IAccountService {
//作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
@Autowired
private IAccountDao accountDao;
public AccountService() {
System.out.println("AccountService()创建了");
}
public void saveAccount() {
accountDao.saveAccount();
}
}
@Autowired
private IAccountDao accountDao
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时:
1.容器中bean对象类型和要注入的变量类型IAccountDao匹配
2.要注入的变量名称accountDao匹配容器中accountDao1和accountDao2
6 用于注入数据的注解
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
* 属性:
* value:用于指定注入bean的id。
* Resource
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id。
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
7 改变作用范围以及和生命周期相关的注解
package com.itheima.service.impl;
import com.itheima.dao.IAccountDao;
import com.itheima.service.IAccountService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* 账户的业务层实现类
*
* 曾经XML的配置:
* <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
* scope="" init-method="" destroy-method="">
* <property name="" value="" | ref=""></property>
* </bean>
*
* 用于创建对象的
* 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
* Controller:一般用在表现层
* Service:一般用在业务层
* Repository:一般用在持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
*
*
* 用于注入数据的
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
* 属性:
* value:用于指定注入bean的id。
* Resource
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id。
* 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
* 另外,集合类型的注入只能通过XML来实现。
*
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
*
* 用于改变作用范围的
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* Scope
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围的取值。常用取值:singleton prototype
*
* 和生命周期相关 了解
* 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
* PreDestroy
* 作用:用于指定销毁方法
* PostConstruct
* 作用:用于指定初始化方法
*/
@Service("accountService")
//@Scope("prototype")
public class AccountServiceImpl implements IAccountService {
// @Autowired
// @Qualifier("accountDao1")
@Resource(name = "accountDao2")
private IAccountDao accountDao = null;
@PostConstruct
public void init(){
System.out.println("初始化方法执行了");
}
@PreDestroy
public void destroy(){
System.out.println("销毁方法执行了");
}
public void saveAccount(){
accountDao.saveAccount();
}
}
源码:helloNano
===================================================
05.基于XML的IOC的案例1
1 测试基于XML的IOC案例
源码:day02_eesy_02account_xmlioc
范例:
package com.itheima.test;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* 使用Junit单元测试:测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
private IAccountService as;
@Test
public void testFindAll() {
//3.执行方法
List<Account> accounts = as.findAllAccount();
for(Account account : accounts){
System.out.println(account);
}
}
@Test
public void testFindOne() {
//3.执行方法
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave() {
Account account = new Account();
account.setName("test");
account.setMoney(12345f);
//3.执行方法
as.saveAccount(account);
}
@Test
public void testUpdate() {
//3.执行方法
Account account = as.findAccountById(4);
account.setMoney(23456f);
as.updateAccount(account);
}
@Test
public void testDelete() {
//3.执行方法
as.deleteAccount(4);
}
}
2 基于XML的IOC的案例-编写spring的Ioc配置
3 基于XML的IOC的案例-案例准备
4 注解IOC案例-把自己编写的类使用注解配置
===================================================
06.Spring的新注解
1 spring的新注解-Configuration和ComponentScan
2 spring的新注解-Bean
3 AnnotationConfigApplicationContext的使用
4 spring的新注解-Import
5 spring的新注解-PropertySource
6 Qualifier注解的另一种用法
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
/**
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 我们使用此注解就等同于在xml中配置了:
* <context:component-scan base-package="com.itheima"></context:component-scan>
* Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id。当不写时,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
* 查找的方式和Autowired注解的作用是一样的
* Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码。
* 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
* PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径。
* 关键字:classpath,表示类路径下
*/
//@Configuration
@ComponentScan("com.itheima")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
/**
* 和spring连接数据库相关的配置类
*/
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;
/**
* 用于创建一个QueryRunner对象
* @param dataSource
* @return
*/
@Bean(name="runner")
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return
*/
@Bean(name="ds2")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Bean(name="ds1")
public DataSource createDataSource1(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy02");
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
源码:app
7 spring整合junit问题分析
8 spring整合junit完成
===================================================
07.银行转账案例
1 今日课程内容介绍
2 案例中添加转账方法并演示事务问题
3 分析事务的问题并编写ConnectionUtils
4 编写事务管理工具类并分析连接和线程解绑
5 编写业务层和持久层事务控制代码并配置spring的ioc
6 测试转账并分析案例中的问题
7 代理的分析
8 基于接口的动态代理回顾
基于接口的动态代理
/**
* 一个生产者
*/
public class Producer implements IProducer{
/**
* 销售
* @param money
*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱:"+money);
}
/**
* 售后
* @param money
*/
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱:"+money);
}
}
/**
* 对生产厂家要求的接口
*/
public interface IProducer {
/**
* 销售
* @param money
*/
public void saleProduct(float money);
/**
* 售后
* @param money
*/
public void afterService(float money);
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 模拟一个消费者
*/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
/**
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理:
* 涉及的类:Proxy
* 提供者:JDK官方
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* ClassLoader:类加载器
* 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
* Class[]:字节码数组
* 它是用于让代理对象和被代理对象有相同方法。固定写法。
* InvocationHandler:用于提供增强的代码
* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
* 此接口的实现类都是谁用谁写。
*/
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
}
}
源码:day03_eesy_02proxy
9 基于子类的动态代理
/**
* 一个生产者
*/
public class Producer {
/**
* 销售
* @param money
*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱:"+money);
}
/**
* 售后
* @param money
*/
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱:"+money);
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 模拟一个消费者
*/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
/**
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于子类的动态代理:
* 涉及的类:Enhancer
* 提供者:第三方cglib库
* 如何创建代理对象:
* 使用Enhancer类中的create方法
* 创建代理对象的要求:
* 被代理类不能是最终类
* create方法的参数:
* Class:字节码
* 它是用于指定被代理对象的字节码。
*
* Callback:用于提供增强的代码
* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
* 此接口的实现类都是谁用谁写。
* 我们一般写的都是该接口的子接口实现类:MethodInterceptor
*/
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行北地阿里对象的任何方法都会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param methodProxy :当前执行方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(12000f);
}
}
10 使用动态代理实现事务控制
源码:day03_eesy_01account
===================================================
08.面向切面编程 AOP
1 AOP的概念
2 spring中的aop术语和细节
3 spring基于XML的AOP-编写必要的代码
4 spring基于XML的AOP-配置步骤
<?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>com.itheima</groupId>
<artifactId>day03_eesy_03springAOP</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
</project>
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService{
@Override
public void saveAccount() {
System.out.println("执行了保存");
}
@Override
public void updateAccount(int i) {
System.out.println("执行了更新"+i);
}
@Override
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
public class Logger {
/**
* 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
*/
public void printLog(){
System.out.println("Logger类中的pringLog方法开始记录日志了。。。");
}
}
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置srping的Ioc,把service对象配置进来-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!--spring中基于XML的AOP配置步骤
1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的Id。
4、在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
* *.*.*.*.AccountServiceImpl.saveAccount())
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* com.itheima.service.impl.*.*(..)
-->
<!-- 配置Logger类 -->
<bean id="logger" class="com.itheima.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<aop:before method="printLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象
IAccountService as = (IAccountService)ac.getBean("accountService");
//3.执行方法
as.saveAccount();
as.updateAccount(1);
as.deleteAccount();
}
}
5 切入点表达式的写法
6 四种常用通知类型
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置srping的Ioc,把service对象配置进来-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!-- 配置Logger类 -->
<bean id="logger" class="com.itheima.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用。
它还可以写在aop:aspect外面,此时就变成了所有切面可用
-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置前置通知:在切入点方法执行之前执行
<aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>-->
<!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>-->
<!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
<!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
<!-- 配置环绕通知 详细的注释请看Logger类中-->
<aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
四种常用通知类型.png
7 通用化切入点表达式
8 spring中的环绕通知
9 spring基于注解的AOP配置
10 总结和作业安排
===================================================
09.JdbcTemplate的基本使用
1 今日课程内容介绍
2 JdbcTemplate的概述和入门
3 JdbcTemplate在Dao中的使用
4 JdbcTemplate的CRUD操作
5 JdbcTemplate在spring的ioc中使用
6 JdbcDaoSupport的使用以及Dao的两种编写方式
===================================================