程序的耦合:
-
耦合:程序间的依赖关系。包括:
-
类之间的依赖
-
方法间的依赖
-
-
解耦:降低程序间的依赖关系
-
实际开发:
-
应该做到,编译期不依赖,运行时才依赖
-
-
解耦思路:
-
第一步:使用反射来创建对象,而避免使用new关键字。
-
第二步:通过读取配置文件来获取要创建的对象全限定类名。
-
1.1、bean工厂
自己建立一个简单的beanfactory(bean工厂)
-
BeanFactory
/**
* 一个创建Bean对象的工厂
*
* Bean:在计算机英语中,有可重用组件的含义。
* JavaBean:用java语言编写的可重用组件。
* javvabean >> 实体类(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 = new FileInputStream(""); //这是要输入具体地址,一般要采用相对地址,也就是下面这个
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
}catch (Exception e){
System.out.println("导入错误");
}
}
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance(); //每次都会调用默认构造函数创建对象
}catch (Exception e){
System.out.println("初始化错误");
}
return bean;
}
}
-
UserMapper接口(相当与UserDao)
public interface UserMapper {
void queryUser();
}UserMapperImpl(相当与UserDaoImpl)
public class UserMapperImpl implements UserMapper {
public void queryUser() {
System.out.println("查询");
}
} -
UserService接口
public interface UserService {
void queryUser();
}UserServiceImpl
public class UserServiceImpl implements UserService {
//private UserMapper userMapper = new UserMapperImpl();
private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
public void queryUser() {
userMapper.queryUser();
}
} -
Client(测试类)
public class Client {
public static void main(String[] args) {
//UserService userService = new UserServiceImpl();
UserService userService = (UserService)BeanFactory.getBean("UserService");
userService.queryUser();
}
} -
bean.properties
UserService=com.gzk.service.UserServiceImpl
UserMapper=com.gzk.mapper.UserMapperImpl -
运行Client
1.2、单例,原型
我们的1.1中的bean工厂是单例的还是原型的呢?
有代码来解释:
public class UserServiceImpl implements UserService {
private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
private int i = 1;
public void queryUser() {
userMapper.queryUser();
System.out.println(i++);
}
}
public class Client {
public static void main(String[] args) {
for(int i=0;i<5;i++) {
UserService userService = (UserService) BeanFactory.getBean("UserService");
System.out.println(userService);
userService.queryUser();
}
}
}
由代码测试可得,我们这个工厂是原型的
原型(也就是说,每调用一次就new一个对象【一般用在存在线程安全问题的情况下使用】)
单例(也就是说,无论调用多少次都只有一个实例对象【只被创建你一次,对象只会初始化一侧】)
对象被创建多次,执行效率就会比较低,所以我们一般情况采用单例
所以我们要将这个工厂改造成单例的
/**
* 一个创建Bean对象的工厂
*
* Bean:在计算机英语中,有可重用组件的含义。
* JavaBean:用java语言编写的可重用组件。
* javvabean >> 实体类(javabean远远大于实体类)
* 他就是创建我们的service和dao对象的。
*
* 第一个:需要一个配置文件来配置我们的service和dao
* 配置的内容:唯一标识=全限定类名(key=value)
* 第二个:通过读取配置文件中配置内容,反射创建对象
*
* 我的配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//创建一个HashMap容器
private static Map<String ,Object> beans;
//使用静态代码块位properties对象赋值
static{
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
//InputStream in = new FileInputStream(""); //这是要输入具体地址,一般要采用相对地址,也就是下面这个
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){
System.out.println("错误");
}
}
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
1.3、IOC控制反转
传统方式
IOC方式
控制反转(Inversion of Control,英文缩写IOC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专业术语,它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
除main函数之外,其他代码都在【1.1、bean工厂】中。
Client
public class Client {
/**
* 获取spring的IOC核心容器,并根据id获取对象
*
* ApplicationContext的三个常用实现类
* ClassPathXmlApplicationContext --> 它可以加载路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了【相对路径】
* FileSystem.XmlApplicationContext --> 它可以加载磁盘任意路径下的配置文件(必须要有访问权限)【绝对路径】
* AnnotationConfigApplicationContext --> 它是用于读取注解创建容器的
*
* 核心容器的两个接口引发的问题
* ApplicationContext:
* 它在构建核心容器时,创建对象采取的策略时采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。(单例时)
* 它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。(原型时)
* BeanFactory:
* 它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
//ApplicationContext context = new FileSystemXmlApplicationContext("D:\\idea-workspace\\my-site\\spring02-study\\spring-01\\src\\main\\resources\\bean.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id查找对应的bean实例
//UserService userService = (UserService) context.getBean("userService");
UserService userService = context.getBean("userService", UserService.class);
userService.queryUser();
}
}
1.4、bean的三种创建方式
1.5、bean对象的作用范围
bean标签的scopc属性:
-
作用:用于指定bean的作用范围
-
取值:
-
singleton:单例的(默认值)
-
prototype:原型的
-
request:作用于web应用的请求范围
-
session:作用于web应用的会话范围
-
global-session:作用于集群环境的会话范围【当有多台服务器时(全局会话范围)】,当不是集群环境时,它就是session
-
<bean id="userService" class="com.gzk.service.UserServiceImpl" scope="prototype"/>
bean对象的生命周期
-
单例对象:
-
出生:当容器创建时对象出生
-
活着:只要容器还在,对象一直活着
-
死亡:容器销毁,对象消亡
-
总结:单例对象的生命周期和容器相同
-
-
原型对象(多例):
-
出生:当我们使用对象时spring框架为我们创建
-
活着:对象只要是在使用过程中就一直活着
-
死亡:当对象长时间不用,且没有别的对象引用时,由Java垃圾回收器回收
-
public class AccountServiceImpl implements AccountService {
public AccountServiceImpl(){
System.out.println("AccountServiceImpl的无参构造函数");
}
public void init(){
System.out.println("对象初始化!");
}
public void getAccountService(){
System.out.println("方法执行中");
}
public void destroy(){
System.out.println("对象销毁!");
}
}
public interface AccountService {
void init();
void getAccountService();
void destroy();
}
public class Client {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService = null;
for(int i=0;i<5;i++) {
accountService = context.getBean("accountService", AccountService.class);
System.out.println(accountService);
accountService.getAccountService();
System.out.println();
}
}
<bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
单例:
原型:
1.6、spring的依赖注入
依赖注入:Dependency Injection
IOC的作用:
-
降低程序间的耦合(依赖关系)
依赖关系的管理:
-
以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
依赖注入:
-
能注入的数据:有三类
-
基本类型和String
-
其他bean类型(在配置文件中或者注解配置过的bean)
-
复杂类型/集合类型
-
-
注入的方式:有三种
-
使用构造函数
-
使用set方法
-
使用注解
-
1.6.1、构造函数注入:
-
使用的标签:constructor-arg
-
标签出现的位置:bean标签的内部
-
标签中的属性
-
type:用于指定要注入大的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
-
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
-
name:用于指定给构造函数中指定名称的参数赋值 =====以上三个用于指定给构造函数中某些参数赋值=====
-
value:用于提供基本类型和String类型的数据
-
ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象
-
-
优势:
-
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
-
<bean id="constructorService" class="com.gzk.service.ConstructorService">
<constructor-arg index="0" value="111"/>
<constructor-arg index="1" value="张三"/>
<constructor-arg index="2" ref="dateTime"/>
bean>
<bean id="dateTime" class="java.util.Date"/>
1.6.2、set方法注入
-
使用的标签:property
-
标签出现的位置:bean标签的内部
-
标签中的属性
-
name:用于指定注入时所调用的set方法
-
value:用于提供基本类型和String类型的数据
-
ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象
-
-
优势
-
创建对象时,没有明确的限制
-
-
ss
-
<bean id="constructorService2" class="com.gzk.service.ConstructorService2">
<property name="id" value="111"/>
<property name="name" value="张三"/>
<property name="dateTime" ref="dateTime"/>
bean>
<bean id="dateTime" class="java.util.Date"/>
-
复杂类型注入/集合类型注入
-
用于给List结构【set
,List ,String[]】集合注入的标签: -
list array set【三者可以混用,无明确规定】
-
-
用于给Map结构【Map
,Properties】集合注入的标签: -
map props【二者可以混用,无明确规定】
-
-
public class SetService {
private String name;
private String[] stringList;
private List<String> list;
private Set<String> set;
private Map<String ,Object> map;
private Properties properties;
public void setName(String name) {
this.name = name;
}
public void setStringList(String[] stringList) {
this.stringList = stringList;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
<bean id="setService" class="com.gzk.service.SetService">
<property name="name" value="张三"/>
<property name="stringList">
<list>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
list>
property>
<property name="list">
<array>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
array>
property>
<property name="set">
<set>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
set>
property>
<property name="map">
<props>
<prop key="1">DDDprop>
<prop key="2">EEEprop>
<prop key="3">FFFprop>
props>
property>
<property name="properties">
<map>
<entry key="1" value="DDD"/>
<entry key="2">
<value>EEE