Spring(一)
控制反转
Inversion of Control,缩写为IoC,把创建对象的权利交给框架,是框架的重要特征,它包括依赖注入和依赖查找.
工厂模式解耦代码
/**
*BeanFactory:一个创建Bean对象的工厂:
*
* Bean:在计算机英语中,有可重用组件的含义
*JavaBean: 用java语言编写的可重用组件
* javabean != 实体类
* javabenan 的范围远大于实体类
*
*创建service和dao对象:
* 第一:需要一个配置文件来配置service 和 dao
* 配置的内容:唯一标识 = 全限定类名 (key=value) 去获取这个全限定类名
* 第二:通过读取配置文件中配置的内容,反射创建对象
* 配置文件可以是 xml 也可以是 properties
*
* Properties(Java.util.Properties),
* 该类主要用于读取Java的配置文件,
* 不同的编程语言有自己所支持的配置文件,
* 配置文件中很多变量是经常改变的,为了方便用户的配置,
* 能让用户够脱离程序本身去修改相关的变量设置。
*
*/
//去读取 resources中的 bean.properties文件
/*public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//使用静态代码块为Properties对象赋值
static {
//实例化对象
props = new Properties();
//获取properties文件的流对象 用类加载器去读这个文件
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
props.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
*//**
* 根据Bean的名称获取bean对象
*
* @param beanName
* @return
*//*
public static Object getBean(String beanName) {
Object bean = null;
String beanPath = props.getProperty(beanName);
try {
*//**
* 用反射的方式来创建对象
* 并且因为newInstance() 每次都会调用默认构造函数,
* 导致每次都初始化创建对象,每次创建不同对象它是多例的
* 因此需要解决(如下)!!!!
*//*
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}*/
//去读取 resources中的 bean.properties文件
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();//获得一个枚举类的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的名称获取bean对象
*此时已变成了单例的,都是同一个对象
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
return beans.get(beanName);
}
}
总结
/** -------------总结----------------------------
* AccountServiceImpl as = new AccountServiceImpl();
* AccountServiceImpl as = (AccountServiceImpl) BeanFactory.getBean("accountService");
* 这是两种截然不同的创建对象的方式
* 第一种 是 new app就是直接主动去找资源
* 第二种是app通过联系工厂 去找资源 ,工厂去控制资源,并且工厂为app提供资源
*而这就是IOC思想(控制反转)
*/
现在使用Spring 来进行解耦
1.1 什么是Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。
简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
1.2 Spring的优点
- 方便解耦,简化开发 (高内聚低耦合)
Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理
spring工厂是用于生成bean - AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能 - 声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程 - 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序 - 方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持 - 降低JavaEE API的使用难度
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
1.3 Spring的体系结构
入门案例
配置文件
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 把对象的创建交给spring来管理-->
<bean id= "accountService" class= "x.xss.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="x.xss.dao.impl.AccountDaoImpl"></bean>
</beans>
表现层
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
/**
* 获取spring的Ioc容器,并根据id获取对象
*
* ApplicationContext的三个常用实现类:
*
* ClassPathXmlApplicationContext : 它可以加载类路径下的配置文件,
* 要求配置文件必须在类路径下。
* 不在的话,加载不了。也就无法获
* 取核心容器对象
*
* FileSystemXmlApplicationContext : 它可以加载磁盘任意路径下的配置文件
* (必须有访问权限)
* AnnotationConfigApplicationContext : 它是用于读取注解创建容器的。
*
* 核心容器的两个接口引发的问题 bean对象的创建默认是单例的
* 1, ApplicationContext: 单例对象适应 (实际开发中更多使用此接口)
* 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。
* 也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
*
*,2, BeanFactory; 多例对象适用
* 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。
* 也就是说,什么时候根据id获取对象了,就什么时候才创建真正的对象。
*
*
* @param args
*/
public static void main(String[] args) {
//1,获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\2019\\IdeaProjects\\day722_03spring\\src\\main\\resources\\bean.xml");
//2,根据id获取Bean对象
//两种方式 ,强转,或者是输入字节码文件强转
AccountService as = (AccountService) ac.getBean("accountService");
AccountDao adao = ac.getBean("accountDao", AccountDao.class);
System.out.println(as);
System.out.println(adao);
// as.saveAccount();
/*
//------- BeanFactory 创建对象,创建的bean是多例的----------
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
AccountService as = (AccountService)factory.getBean("accountService");
System.out.println(as);
*/
}
}
spring 中工厂的类结构图
BeanFactory 和 ApplicationContext 的区别
BeanFactory 才是 Spring 容器中的顶层接口。 ApplicationContext 是它的子接口。 BeanFactory 和 ApplicationContext 的区别: 创建对象的时间点不一样。 ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。 BeanFactory:什么使用什么时候创建对象。
ApplicationContext 接口的实现类
ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种 FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。 AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
Spring对Bean 的管理细节
bean 标签
作用:
用于配置对象让 spring 来创建的。
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
属性:
id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。
singleton :默认值,单例的.
prototype :多例的.
request : WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
session : WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 globalSession 相当于 session.
init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称。
1,创建bean的三种方式
方式一 :使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建
/*配置文件中
*/
<bean id= "accountService" class= "x.xss.service.impl.AccountServiceImpl"></bean>
//实现类中
public class AccountServiceImpl implements AccountService {
//默认构造函数
public AccountServiceImpl() {
System.out.println("对象被创建了");
}
}
方式二 别人写好的类,存在jar包中
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
/**
* 模拟一个工厂类(此类可能存在于jar包中,
* 我们无法通过修改源码的方式来提供默认构造函数
*/
public class InstanceFactory {
public AccountService getAccountServive(){
return new AccountServiceImpl();
}
}
<!-- 此种方式是:
先把工厂的创建交给 spring 来管理。
然后在使用工厂的 bean 来调用里面的方法
factory-bean 属性:用于指定实例工厂 bean 的 id。
factory-method 属性:用于指定实例工厂中创建对象的方法。
-->
<bean id="instanceFactory" class="x.xss.factory.InstanceFactory"></bean>
<!--accountService的创建不再自己创建了,而是去使用 nstanceFactory,所以加上下面的配置-->
<bean id="accountService"
factory-bean="instanceFactory" <--找到此类中的-->
factory-method="getAccountServive"> <--此方法-->
</bean>
方式三 别人写好的类,存在jar包中
使用工厂中的静态方式创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
/**
* 模拟一个工厂类(此类可能存在于jar包中,
* 我们无法通过修改源码的方式来提供默认构造函数
* 这里是静态的方法
*/
public class StaticFactory {
public static AccountService getAccountServive(){
return new AccountServiceImpl();
}
}
<!--使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的静态方法-->
<bean id="accountService"
class="x.xss.factory.StaticFactory"
factory-method="getAccountServive">
</bean>
2,bean对象的作用范围
默认是单例的
bean标签的scope属性;
作用: 用于指定bean的作用范围
取值:
singleton : 单例的(也是默认值)(常用)
prototype : 多例的 (常用)
request : 作用于web应用的 请求 范围
session : 作用于web应用的 会话 范围
global-session: 作用于 集群环境的会话范围(全局会话范围)
,当不是集群环境时,就是session
<bean id= "accountService" class= "x.xss.service.impl.AccountServiceImpl"
scope="singleton" init-method="init" destroy-method="destory"></bean>
3,bean对象的生命周期
单例对象
出生 容器创建时,对象出生
活着
死亡 容器销毁时,对象消亡
总结:单例对象的生命周期与容器相同
多例对象
出生 当使用对象时,spring框架会创建
活着 对象在使用过程中 bean就一直存在
死亡 当对象长时间不用,且没有别的对象引用时,
·有java的垃圾回收器回收
spring 的依赖注入
依赖注入:
Dependency Injection
IOC的作用:
降低程序间的耦合(依赖关系)
依赖关系的管理:
以后都交给了spring维护
在当前类中需要用到其他类的对象,由spring来提供,我们只需在配置文件中说明
依赖关系的维护:
称之为:依赖注入
依赖注入:
能注入的数据有三类
基本类型和String
其它bean类型(在配置文件中或者注解配置过的bean)
复杂类型/集合类型
注入的方式:有三种
第一种:使用构造函数提供
第二种: 使用set方法提供
第三种: 使用注解提供
1,构造函数的注入
使用标签: constructor-arg
标签出现的位置: bean 标签的内部
标签中的属性 :
type :用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型(不能独立实现注入的功能)
index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值。参数索引的位置从0开始。
name: 用于指定给构造函数中指定名称的参数赋值 (常用的)
=以上三个用于指定给构造函数中的哪个参数赋值===
value: 用于提供基本类型和String类型的数据
ref : 用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象。
例如出现过的bean对象:
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
劣势:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,
也还是要提供。
<bean id= "accountService" class= "x.xss.service.impl.AccountServiceImpl">
<constructor-arg name="aaa" value="小明"></constructor-arg>
<constructor-arg name="bbb" value="18"></constructor-arg>
<constructor-arg name="ccc" ref="now"></constructor-arg>
</bean>
<!--配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements AccountService {
//如果是经常变化的数据,并不适用于注入的方式
private String aaa;
private Integer bbb;
private Date ccc;
//构造方法
public AccountServiceImpl(String aaa, Integer bbb, Date ccc) {
this.aaa = aaa;
this.bbb = bbb;
this.ccc = ccc;
}
public void saveAccount() {
System.out.println("service中的saveAccount方法被执行了..."+ aaa + "," + bbb + "," + ccc);
}
}
2, set方法注入
涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象时有可能set方法没有执行。
<bean id= "accountService2" class= "x.xss.service.impl.AccountServiceImpl2">
<property name="aaa" value="小红"></property>
<property name="bbb" value="15"></property>
<property name="ccc" ref="now"></property>
</bean>
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl2 implements AccountService {
//如果是经常变化的数据,并不适用于注入的方式
private String aaa;
private Integer bbb;
private Date ccc;
//set方法
public void setAaa(String aaa) {
this.aaa = aaa;
}
public void setBbb(Integer bbb) {
this.bbb = bbb;
}
public void setCcc(Date ccc) {
this.ccc = ccc;
}
public void saveAccount() {
System.out.println("service中的saveAccount方法被执行了..."+ aaa + "," + bbb + "," + ccc);
}
}
3,复杂类型的注入/集合类型的注入(实际开发中常用!!!)
用于给List结构集合注入的标签:
list array set
用于个Map结构集合注入的标签:
map props
结构相同,标签可以互换
<bean id="accountService3" class="x.xss.service.impl.AccountServiceImpl3">
<property name="a">
<set>
<value>数组1</value>
<value>数组2</value>
<value>数组3</value>
</set>
</property>
<property name="b">
<array>
<value>List1</value>
<value>List2</value>
<value>List3</value>
</array>
</property>
<property name="c">
<set>
<value>Set1</value>
<value>Set2</value>
<value>Set3</value>
</set>
</property>
<property name="d">
<props>
<prop key="map1">m111</prop>
<prop key="map2">m222</prop>
</props>
</property>
<property name="e">
<map>
<entry key="写法1" value="Properties1111"></entry>
<entry key="写法2">
<value>Properties2222</value>
</entry>
</map>
</property>
</bean>
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl3 implements AccountService {
private String[] a;
private List<String> b;
private Set<String> c;
private Map<String,String>d;
private Properties e;
//set 方法
public void setA(String[] a) {
this.a = a;
}
public void setB(List<String> b) {
this.b = b;
}
public void setC(Set<String> c) {
this.c = c;
}
public void setD(Map<String, String> d) {
this.d = d;
}
public void setE(Properties e) {
this.e = e;
}
public void saveAccount() {
System.out.println(Arrays.toString(a));
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
}
g,String>d;
private Properties e;
//set 方法
public void setA(String[] a) {
this.a = a;
}
public void setB(List<String> b) {
this.b = b;
}
public void setC(Set<String> c) {
this.c = c;
}
public void setD(Map<String, String> d) {
this.d = d;
}
public void setE(Properties e) {
this.e = e;
}
public void saveAccount() {
System.out.println(Arrays.toString(a));
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
}