spring的体系结构
IoC 的概念和作用
程序的耦合和解耦
/**
* 程序的耦合
* 耦合:程序间的依赖关系
* 包括:
* 类之间的依赖
* 方法间的依赖
*
* 解耦:降低程序间的依赖关系
*
* 实际开发中:
* 应该做到,编译期不依赖,运行时才依赖
*
* 解耦思路:
* 第一步:使用反射来创建对象,而避免使用new关键字
* 第二步:通过读取配置文件来获取要创建对象的全限定类名
*/
public class JdbcDemo1 {
public static void main(String[] args) throws Exception {
//1 注册驱动 --- 如果没有mysql的驱动则不能运行
// DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //依赖具体驱动类
//通过反射来注册驱动的
Class.forName("com.mysql.jdbc.Driver"); //仅是一个字符串
......
}
如果使用原来的方式创建项目的三层模块,那么在表现层需要调用业务层service的impl对象,业务层要调用持久层dao的impl对象,需要new对象,程序之间耦合性高,我们可以使用反射来创建对象。
//bean.properties
accountService=com.itheima.service.impl.AccountServiceImpl
accountDao=com.itheima.dao.impl.AccountDaoImpl
工厂模式解耦
/**
* 创建Bean对象的工厂
*
* Bean:可重用组件的含义
* JavaBean:用java语言编写的可重用组件
* javaBean > 实体类
*
* 它就是创建我们的service和dao对象的
*
* 第一个:需要一个配置文件来配置我们的service和dao
* 配置的内容:唯一标志=全限定类名(key=value)
* 第二个:通过读取配置文件中配置的内容,反射创建对象
*
* 配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取Properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
} catch (IOException 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;
}
}
public class Client {
public static void main(String[] args) {
//在需要调用对象的地方,使用工厂获取对象即可,如果没有这个实现类,编译不报错,运行时保存。
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
as.saveAccount();
}
}
使用map存储对象,使得每次调用对象为同一对象,单例
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个map,用于存放我们要创建的对象,我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取Properties文件的流对象
InputStream in = BeanFactory.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);
}
}
使用 spring 的 IOC 解决程序耦合
- 概念
- 控制反转(Inversion of Control)是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。
- 通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中
- spring基于xml的ioc环境搭建和入门
//pom.xml 导入spring坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
//bean.xml 类似之前的bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--把对象的创建交给spring来管理-->
<!--id:唯一标识 class:创建对象的全限定类名-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
</beans>
public class Client {
/**
* 获取spring的Ioc核心容器,并根据id获取对象
* @param args
*/
/*
与工厂模式里做的操作相同,只是读取配置文件,创建对象,存入map中过程,spring都封装了
*/
public static void main(String[] args) {
//1 获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2 根据id获取bean对象
IAccountService as = (IAccountService)ac.getBean("accountService"); //两种方式 1强转
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class); //2传入class字节码
System.out.println(as); //com.itheima.service.impl.AccountServiceImpl@26be92ad
System.out.println(adao); //com.itheima.dao.impl.AccountDaoImpl@4c70fda8
}
}
- ApplicationContext的三个实现类
public class Client {
/**
* ApplicationContext的三个常用实现类
* ClassPathXmlApplicationContext,可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在则加载不了(更常用)
* FileSystemXmlApplicationContext,可以加载磁盘任意路径下的配置文件(必须有访问权限)
* AnnotationConfigApplicationContext,用于读取注解创建容器的
*/
public static void main(String[] args) {
//1 获取核心容器对象
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\JavaCode\\spring\\day01_03spring\\src\\main\\resources\\bean.xml");
//2 根据id获取bean对象
IAccountService as = (IAccountService)ac.getBean("accountService"); //两种方式 1强转
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class); //2传入class字节码
System.out.println(as); //com.itheima.service.impl.AccountServiceImpl@26be92ad
System.out.println(adao); //com.itheima.dao.impl.AccountDaoImpl@4c70fda8
}
}
- BeanFactory 和 ApplicationContext 的区别
- ApplicationContext:单例对象适用(多采用此接口)
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式,也就是说,只要一读取完配置文件就马上创建配置文件中配置的对象 - BeanFactory:多例对象适用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才真正创建对象
//------------BeanFactory----------------
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
IAccountService as = (IAccountService) factory.getBean("accountService");
System.out.println(as);
bean细节
- 创建bean的三种方式
- 普通配置,实现类是我们自己写的,并且提供了默认构造方法
<!--第一种方式:使用默认构造函数创建
在spring的配置文件中,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
- 无默认构造方法的创建方式
/**
* 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
*/
public class InstanceFactory {
public IAccountService getAccountService(){
return new AccountServiceImpl();
}
}
<!--第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器) -->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
- 如果工厂类中获取bean对象的方法是静态的
public class StaticFactory {
public static IAccountService getAccountService(){
return new AccountServiceImpl();
}
}
<!--第三种方式,使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
<bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
测试:三种创建bean对象方法创建得到对象效果都是一样的
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");
as.saveAccount();
}
}
- bean对象的作用范围
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="singleton"></bean>
<!--bean的作用范围调整
作用:用于指定bean的作用范围
取值:常用的是singleton和prototype
singleton:单例的(默认值)
prototype:多例的.
request:作用于WEB应用的请求范围,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
session:作用于WEB应用的会话范围,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
global-session:作用于集群环境的会话范围(全局会话范围),当不上集群环境时,它就是session
-->
- bean的生命周期
-
单例对象:
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期与容器相同 -
多例对象:
出生:当我们使用对象时,spring为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
spring 的依赖注入
- IOC的作用: 降低程序间的耦合(依赖关系)
依赖关系的管理,以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
依赖关系的维护, 就称之为依赖注入。 - 依赖注入(Dependency Injection):
1. 能注入的数据有三类:
基本类型和String
其他bean类型(在配置文件中或者注解配置过的bean)
复杂类型/集合类型
2. 注入的方式有三种:
第一种:使用构造函数提供
第二种:使用set方式提供
第三种:使用注解提供
- 构造函数注入
实际开发中,除非不得已,不会采用这种注入方式。
bean.xml
<!--构造函数注入
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引位置从0开始
name:用于指定给构造函数中指定名称的参数赋值(常用)
=====================用于指定给构造函数中哪个参数赋值========================
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,它指的是在spring的Ioc核心容器中出现过的bean对象,配置文件中配置过的对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象是无法创建的
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="泡泡"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
<!--ref引用配置的now日期对象-->
</bean>
<!--配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
public class AccountServiceImpl implements IAccountService {
//经常变化的数据,并不适应于注入的方式
private String name;
private Integer age;
private Date birthday;
//无默认构造函数,不可以使用第一种创建bean对象方式
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
} ......
}
- set方法注入
public class AccountServiceImpl2 implements IAccountService {
//经常变化的数据,并不适应于注入的方式
private String name;
private Integer age;
private Date birthday;
//set方法注入
//只需要set方法
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
......
}
<!--配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
<!--set按方法注入(常用)
涉及的标签:property
出现的位置:bean标签的内部
标签的属性:
name:用于指定注入时所调用的set方法名称
value:它能赋的值是基本数据类型和String类型的数据
ref:它能赋的值是其他 bean 类型,它指的是在spring的Ioc核心容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象时有可能set方法无没有执行
-->
<bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
<property name="name" value="text"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
- 复杂类型 / 集合类型的注入
public class AccountServiceImpl3 implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
...提供set方法注入
}
<!--复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:
list array set
用于给Map结构集合注入的标签:
map props
结构相同,标签可以互换
-->
<bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</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>