概述
- 什么是Spring:
Spring是分层的JavaSE/EE引用full-stack(全站式)轻量级开源框架,以IoC(控制反转)、AOP(面面向切面编程)为内核。 - 发展历程:
1997年,IBM提出了EJB的思想。
1998年,SUN制定了规范标准EJB1.0
2004年,Rod Johnson阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
2017年,发布spring5.0通用版 - Spring优势:
方便解耦,简化开发、AOP支持、使命式事务的支持、方便程序的测试、集成各种优秀的框架、降低了JavaEE API的使用难度。 - 体系结构:
程序的耦合与解耦
耦合: 程序间(类、方法)的依赖关系
解耦:降低程序间的依赖关系,应该做到编译器不依赖,运行时才依赖。
解耦思路:使用反射来创建对象,避免使用new;通过读取配置文件来获取要创建对象的全限定类名。
分别创建:
持久层接口:
public interface AccountDao {
/**
* 模拟保存
*/
void saveAccount();
}
持久层实现类:
public interface AccountDao {
/**
* 模拟保存
*/
void saveAccount();
}
业务层接口:
public interface AccountService {
/**
* 模拟保存
*/
void saveAccount();
}
业务层实现类:
public class AccountServiceImpl implements AccountService {
AccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
模拟view层:
public class Client {
public static void main(String[] args) {
AccountService accountService = new AccountServiceImpl();
accountService.saveAccount();
}
}
这样使用会在视图层调用业务层;业务层调用持久层的方法。如果其中某个类出现缺失等问题,则会在程序编译时报错,耦合性太高了。所以下面使用工厂类来映射对象减少程序间的耦合行。
工厂类:
/** 创建Bean对象的工厂
* bean:在计算机英语中为可重用组件。
*
* JavaBean:用Java语言编写的可重用组件。
*
* 这个类用于创建Service和Dao对象
* 1. 需要一个配置文件来配置service和dao
* 配置内容:唯一表示=全限定类名(key=value)
* 可以是xml也可以是properties
* 2. 读取配置文件中的内容,反射创建对象
*/
public class BeanFactory {
// 定义一个Properties对象
private static Properties properties;
// 定义一个Map容器用于存放要创建的对象
private static Map<String ,Object> beans;
static {
try {
// 实例化对象
properties = new Properties();
// 获取配置对象流(使用类加载器获取)
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
// 加载配置文件
properties.load(inputStream);
// 实例化容器
beans = new HashMap<String, Object>();
// 取出配置文件中所有的kay
Enumeration<Object> keys = properties.keys();
// 遍历枚举
while (keys.hasMoreElements()){
// 取出key
String key = keys.nextElement().toString();
// 根据key获取value
String beanPath = properties.getProperty(key);
// 创建反射对象
Object value = Class.forName(beanPath).newInstance();
beans.put(key,value);
}
} catch (IOException e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化配置文件失败");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 根据bean名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
bean.properties:
accountService=com.springDemo.service.Impl.AccountServiceImpl
accountDao=com.springDemo.dao.Impl.AccountDaoImpl
然后修改业务层实现类和模拟的试图层:
业务层实现类
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
// AccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
模拟试图层:
public class Client {
public static void main(String[] args) {
AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
// AccountService accountService = new AccountServiceImpl();
accountService.saveAccount();
}
}
以达到减少耦合的目的。
IoC(控制反转)
指app和资源之间没有必然的联系,使用工厂类去联系拥有独立的控制。把对象创建的权力交给框架。
用于削减耦合。
spring依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!--把对象的创建交给spring来管理-->
<bean id="accountService" class="com.springDemo.service.Impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.springDemo.dao.Impl.AccountDaoImpl"></bean>
</beans>
模拟试图:
public class Client {
public static void main(String[] args) {
// 获取springIoC核心容器并且获取id对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
// 根据id获取bean对象 下面两种方法都可以
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
AccountService accountService1 = applicationContext.getBean("accountService",AccountService.class);
accountService.saveAccount();
}
}
ApplicationContext下的三个常用实现类:
- ClassPathXmlApplicationContext:
加载类路径下的配置文件,要求配置文件必须在类路径下。 - FileSystemXmlApplicationContext:
加载磁盘任意路径下的配置文件(必须有访问权限)。 - AnnotatiionConfigApplicationContext:
用于读取注解创建容器。
ApplicationContext与BeanFactory:
- ApplicationContext:
在构建核心容器时,采用立即加载方式。只要一读完配置文件就创建配置的对象;单例对象适用。 - BeanFactory:
延迟加载的方式。什么时候根据id获取对象了什么时候创建对象;多例对象使用。已经过时。
Spring创建Bean的三种方式:
- 使用默认构造函数:
使用bean标签且没有除了id和class以外的其它属性。但是如果此类中没有默认构造函数则会创建失败
<bean id="accountService" class="com.springDemo.service.Impl.AccountServiceImpl"></bean>
- 使用普通工厂中的方法创建对象
使用某个类中的方法,并存入spring容器
<bean id="BeanFactory" class="com.springDemo.factory.BeanFactory"></bean>
<bean id="accountService" factory-bean="BeanFactory" factory-method="getAccountService"></bean>
方法:
public AccountService getAccountService(){
return new AccountServiceImpl();
}
- 使用工厂类中的静态方法创建对象:
使用某个类中的静态方法创建对象,并存入spring容器
<bean id="accountService" class="com.springDemo.factory.BeanFactory" factory-method="getStaticAccountService"></bean>
public static AccountService getStaticAccountService(){
return new AccountServiceImpl();
}
Bean的作用范围:
scope属性:
- singletoon:单例的(默认值)
- prototype:多例的
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),不是集群环境就是session
Bean对象的生命周期:
- 单例对象:
当容器创建时产生对象,只要容器还在对象就一直存在,容器销毁对象消失。和容器一样 - 多例对象
使用对象时创建,只要在运行过程中就一直存在,当对象长时间不用,且没有别的对象引用时,有Java的垃圾回收机制回收。
依赖注入(Dependency Injection)
依赖关系的维护就称之为依赖注入。
注入的数据类型:
经常改变的数据不使用注入。
- 基本类型和String
----------------------------往下看---------------------------- - 其它Bean
----------------------------往下看---------------------------- - 复杂类型/集合类型
用于给List结构标签注入的有:
list、array、set
用于给Map结构标签注入的有:
map props
结构相同标签可以互换。
<!--复杂类型的注入-->
<bean id="accountService" class="com.springDemo.service.Impl.AccountServiceImpl">
<!--注入数组-->
<property name="myStrs">
<array>
<value>AAA</value>
<value>AAA</value>
<value>AAA</value>
</array>
</property>
<!--注入list-->
<property name="myList">
<list>
<value>BBB</value>
<value>BBB</value>
<value>BBB</value>
</list>
</property>
<!--注入set-->
<property name="mySet">
<set>
<value>BBB</value>
<value>BBB</value>
<value>BBB</value>
</set>
</property>
<!--注入Map-->
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
<entry key="testA" value="aaa"></entry>
</map>
</property>
<!--注入Props-->
<property name="myProps">
<props>
<prop key="A">aa</prop>
<prop key="A">aa</prop>
<prop key="A">aa</prop>
</props>
</property>
</bean>
注入的方式:
- 使用构造函数提供:
bean标签内包含的constructor-arg标签:
constructor标签内的属性:
type:指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的数据类型。
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引从0开始。
name:用于指定给构造函数中指定的参数赋值(常用)。
value:用于提供基本类型和String类型的数据。
ref:用于其它的Bean类型,就是指在spring的IoC核心容器中出现的Bean对象。
优势:在获取Bean对象时,注入数据是必须的操作,否则对象是无法构建成功的。
弊端:改变了Bean对象的实例化方式,使在创建对象时如果用不到数据也必须要提供值。
demo:
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name,Integer age,Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
<bean id="accountService" class="com.springDemo.service.Impl.AccountServiceImpl">
<constructor-arg name="age" value="1"></constructor-arg>
<constructor-arg name="name" value="Tom"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
- 使用set方法提供 (更常用)
property标签(bean内部):
property标签内的属性:
name:用于指定给构造函数中指定的set方法名称。
value:用于提供基本类型和String类型的数据。
ref:用于其它的Bean类型,就是指在spring的IoC核心容器中出现的Bean对象。
优势:创建对象时没有明确的约束,可以直接使用构造函数。
弊端:如果有某个成员必须有值,则set方法无法保证一定注入。
demo:
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;
}
<!--set注入-->
<bean id="accountService" class="com.springDemo.service.Impl.AccountServiceImpl">
<property name="name" value="text"></property>
<property name="birthday" ref="now"></property>
<property name="age" value="1"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
- 使用注解提供:
另一篇博客详细的写了注解的用法。