Spring学习1--IoC 控制反转

Spring 学习1(Spring 5.0.2)

IoC 控制反转

例一:

public class AccountServiceImpl implements IAccountService {  
    private IAccountDao accountDao = new AccountDaoImpl(); 
}

​ 上面的代码表示:业务层里调用持久层,同时现在的业务层在依赖持久层的接口和实现类。如果此时没有创建持久层的实现类,编译将不能通过。这种编译期依赖关系,可以通过代码优化解决。

例二:

要使用 JDBC 操作,先要注册驱动,可以用 DriverManager 的 registerDriver 方法或使用 Class.forName 方法

public class JdbcTest{
    public static void main(String[] args){
    //1.注册驱动   
	//DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 
	Class.forName("com.mysql.jdbc.Driver"); 
	//2.获取连接   
	//3.获取预处理 sql 语句对象   
	//4.获取结果集   
	//5.遍历结果集 
    }
}

​ 这里没有使用 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); ,因为这里的类依赖了数据库的具体驱动类(这里是MySQL驱动),如果日后更换了其他数据库(如Oracle),就要修改源码来使用新的数据库驱动,这显然是不合适的。

使用了反射来注册数据库驱动

Class.forName("com.mysql.jdbc.Driver"); //此时forName()里的只是一个字符串

​ 这样类中就不再依赖具体的数据库驱动类,就是你现在没有MySQL的驱动jar包,还是可以编译的。当然,你既然没有相应的jar包程序是不能运行的了。

​ 如果日后更换了其他数据库,还是要改源码,即修改这行字符串,解决方法就是使用配置文件来进行配置。

工厂模式解耦

​ 在实际开发中,可以把三层结构的对象使用配置文件进行配置,当服务器应用加载时,让一个类中的方法,通过读取配置文件,把这些对象创建并且存起来。在后面要用到这些对象的时候,就直接拿来用。


解释控制反转

​ 上面提到的把对象存起来,那就要用一个集合来存,使用 Map 这种键值对的形式来存。

​ 应用加载时,创建一个 Map,用来存放三层对象。

​ 这个 Map 称它为容器

工厂负责给我们从容器中获取指定对象,这样我们获取对象的方式就改变了。

​ 之前:想要获取对象就用 new 的方式,这是主动

​ 之后:去问工厂要,让工厂为我们查找或者创建对象。这是被动

​ 这种被动接收的方式获取读写的思想就是 IoC 控制反转,是 Spring 框架的核心之一,作用减少程序的耦合。

创建普通 java 项目演示,手动导入 Spring 的 jar 包

在这里插入图片描述

例一:账户的业务层和持久层

public interface IAccountService {  //业务层接口
 	//保存账户  
    void saveAccount(); 
}
public class AccountServiceImpl implements IAccountService { 		//业务层实现类
 //此处的依赖关系有待解决
 private IAccountDao accountDao = new AccountDaoImpl();
 
 	@Override  
    public void saveAccount() {
        accountDao.saveAccount(); 
 	} 
} 
 
public interface IAccountDao {  //持久层接口
    void saveAccount(); //保存账户 
}
public class AccountDaoImpl implements IAccountDao {
    //持久层实现类
    @Override  
    public void saveAccount() {   
        System.out.println("保存了账户");  
    } 
}

在 src 目录下创建一个 xml 文件,这里是bean.xml(名字随便起,但不可以有中文),创建后添加约束并配置 service 和 dao :

<?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 创建对象,并且存入 Ioc 容器之中     id 属性:对象的唯一标识。      
 	class 属性:指定要创建对象的全限定类名 -->
    <!-- 配置 service -->
    <bean id="accountService" class="com.rgb3.service.AccountServiceImpl"> </bean>
    <!-- 配置 dao -->
    <bean id="accountDao" class="com.rgb3.dao.AccountDaoImpl"></bean>
</beans>

这里可能会遇到的情况
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试一下看看配置是否成功

public class NoteDemoApp {
    public static void main(String[] args) {
        //1.使用 ApplicationContext 接口,就是在获取 spring 容器   
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");   
        //2.根据 bean 的 id 获取对象   
        IAccountService aService = (IAccountService) ac.getBean("accountService");   
        System.out.println("业务层的对象:"+aService);   

        IAccountDao aDao = (IAccountDao) ac.getBean("accountDao");   
        System.out.println("dao层的对象:"+aDao);
    }
}

在这里插入图片描述

成功。上面几行红的警告网上查了下是log4j没有输出日志产生的警告。解决办法:在src文件夹下建立一个名为log4.properties的文件即可。

log4.properties 文件内容:

log4j.rootLogger=DEBUG , stdout

log4j.logger.org.mybatis=DEBUG

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n


Spring 中工厂的类结构图:

在这里插入图片描述

BeanFactory 和 ApplicationContext 的区别

BeanFactory 是 Spring 容器中的顶层接口。 ApplicationContext 是它的接口。

BeanFactory 和 ApplicationContext 的区别: 创建对象的时间点不一样。

ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

BeanFactory:什么时候用什么时候创建对象。

ApplicationContext 接口的实现类

ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件 推荐用这种

FileSystemXmlApplicationContext:它是从硬盘路径上加载配置文件,配置文件可以在硬盘的任意位置。

AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 Spring 容器,它用来读取注解。


bean.xml配置文件里面的 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:指定类中销毁方法名称。

bean 的作用范围和生命周期

单例对象:scope=“singleton”

一个应用只有一个对象的实例。它的作用范围就是整个引用。

生命周期:

  • 对象创建:当应用加载,创建容器时,对象就被创建了。

  • 对象活着:只要容器在,对象一直活着。

  • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象:scope=“prototype”

每次访问对象时,都会重新创建对象实例。

生命周期:

  • 对象创建:当使用对象时,创建新的对象实例。

  • 对象活着:只要对象在使用中,就一直活着。

  • 对象死亡:当对象长时间不用时,被 java 的垃圾回收机制回收了。

实例化 bean 的3种方式
  1. 用默认无参构造函数

    默认情况下:它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败

    <bean id="accountService" class="com.rgb3.service.AccountServiceImpl"/> 
    
  2. 创建一个工厂类,然后用工厂的静态方法创建对象。

    public class StaticFactory {   //模拟一个工厂
        public static IAccountService createAccountService(){   
            return new AccountServiceImpl();  
        } 
    } 
    
    <bean id="accountService"  
       class="com.rgb3.factory.StaticFactory"     factory-method="createAccountService"></bean>
    
  3. Spring管理工厂,先创建工厂实例对象,再调用工厂的方法创建对象。

    public class InstanceFactory {   //模拟一个工厂
        public IAccountService createAccountService(){   
            return new AccountServiceImpl();  
            } 
    }
    
   <bean id="instancFactory" class="com.rgb3.factory.InstanceFactory"></bean>  
   <bean id="accountService"      factory-bean="instancFactory"      factory-method="createAccountService"></bean> 

先把工厂的创建交给 Spring 来管理。

然后再用工厂的 bean 来调用它里面的方法

factory-bean 属性:用于指定实例工厂 bean 的 id。

factory-method 属性:用于指定实例工厂中创建对象的方法。


Spring 的依赖注入

依赖注入是 IoC 控制反转的具体实现。

​ 在写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。 IoC 解耦只是降低他们的依赖关系,但不会消除。如:业务层仍会调用持久层的方法。 这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。就是让Spring 把持久层对象传入业务层,而不用我们自己去获取。

构造函数注入

​ 用类中的构造函数,给成员变量赋值。但是,赋值的操作不是我们自己做的,而是通过配置的方式,让 Spring 来帮我们注入。

public class AccountServiceImpl implements IAccountService {    
    
    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; 
    } 
 
 @Override 
    public void saveAccount() {   
     System.out.println(name+","+age+","+birthday);   
 	} 
} 
 

bean.xml

<bean id="accountService" class="com.rgb3.service.impl.AccountServiceImpl"> 
 	<constructor-arg name="name" value="张三"></constructor-arg>  
    <constructor-arg name="age" value="20"></constructor-arg> 
 	<constructor-arg name="birthday" ref="now"></constructor-arg> 
</bean> 
 
<bean id="now" class="java.util.Date"></bean> 

constructor-arg 标签 属性:

  • index:指定参数在构造函数参数列表的索引位置
  • type:指定参数在构造函数中的数据类型
  • name:指定参数在构造函数中的名称
  • value:它赋的值是基本数据类型和 String 类型
  • ref:它赋的值是其他 bean 类型,必须是在配置文件中配置过的 bean

在这里插入图片描述

set 方法注入

在类中提供需要注入成员的 set 方法,实际开发中,此种方式用的较多。

public class AccountServiceImpl 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;  
    } 
 
 	@Override  
    public void saveAccount() {   	
        System.out.println(name+","+age+","+birthday);   	
    } 
} 

bean.xml

<bean id="accountService" class="com.rgb3.service.impl.AccountServiceImpl">   
	<property name="name" value="mike"></property> 
	<property name="age" value="21"></property>   	
    <property name="birthday" ref="now"></property> 
</bean> 
<bean id="now" class="java.util.Date"></bean> 

property 标签 属性:

  • name:找的是类中 set 方法后面的部分
  • ref:给属性赋值是其他 bean 类型的
  • value:给属性赋值是基本数据类型和 string 类型的
    在这里插入图片描述
用 p 名称空间注入数据,本质上还是调用 set 方法
public class AccountServiceImpl 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;  
    } 
 
 @Override  
    public void saveAccount() {   	
        System.out.println(name+","+age+","+birthday);   	
    } 
}

bean.xml

<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:p="http://www.springframework.org/schema/p"     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.rgb3.service.impl.AccountServiceImpl"     
       p:name="jay" 
       p:age="21" 
       p:birthday-ref="now"/>
    
    <bean id="now" class="java.util.Date"></bean>
</beans> 

在这里插入图片描述

注入集合

给类里的集合传值,用的也是 set 方法注入。以 List , Set , Map , Properties 为例。

public class AccountServiceImpl 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;
    }

    @Override
    public void saveAccount() {
        System.out.println("myStrs: "+Arrays.toString(myStrs));
        System.out.println("myList: "+myList);
        System.out.println("mySet: "+mySet);
        System.out.println("myMap: "+myMap);
        System.out.println("myProps: "+myProps);
    }
} 

List 结构的: array,list,set

Map 结构的 map,entry,props,prop

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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.rgb3.service.impl.AccountServiceImpl">
        <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
        <!--注入数组数据-->
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <!-- 注入 list 集合数据 -->
        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <!-- 注入 set 集合数据 -->
        <property name="mySet">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
        <!-- 注入 Map 数据 -->
        <property name="myMap">
            <props>
                <prop key="testA">aaa</prop>
                <prop key="testB">bbb</prop>
            </props>
        </property>
        <!-- 注入 properties 数据 -->
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <!--两种方式-->
                <entry key="testB">
                    <value>bbb</value>
                </entry>
            </map>
        </property>
    </bean>
</beans>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值