Spring之IOC和DI的具体使用

Spring IOC 的具体应用

1. BeanFactory与ApplicationContext区别

BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,⽽ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝⽐BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等

image-20210325111104947

2. IOC 容器的启动

Java SE环境下启动IoC容器

ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)

FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件

AnnotationConfigApplicationContext:纯注解模式下启动Spring容器

  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        TransferService transferService = (TransferService)applicationContext.getBean("transferService");

Web环境下启动IoC容器

Tomcat 启动会执行xml 文件 执行监听器 启动Spring的ioc容器 加载applicationContextxml文件

  • 从xml启动容器

    <!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--配置Spring ioc容器的配置⽂件-->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
     <!-- 使用监听器配置Spring的ioc容器-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    </web-app>
    

    调用 WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());

  • 纯注解模式下从配置类启动容器

    <!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
    <context-param>
    <param-name>contextClass</param-name>
    <paramvalue>
    org.springframework.web.context.support.AnnotationConfigWebAppli
    cationContext</param-value>
    </context-param>
    <!--配置启动类的全限定类名-->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.lagou.edu.SpringConfig</param-value>
    </context-param>
    <!--使⽤监听器启动Spring的IOC容器-->
    <listener>
    <listenerclass>
    org.springframework.web.context.ContextLoaderListener</listenerclass>
    </listener>
    </web-app>
    

    image-20210325145543874

3. xml实例化bean的三种方式

  • 调用无参构造器(常用推荐)

    在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。

    <bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
    </bean>
    
  • 使⽤静态⽅法创建

    我们自己使用工厂模式创建出一个static 方法 里面 new 一个对象, 此时并不会创建该class类的对象而是我们指定的

    该静态方法的返回的对象放入ioc的容器当中。

      //静态方法创建对象
        public static User getAdmin(){
             
              return new User(1,"admin");
        }
    
    <!--使⽤静态⽅法创建对象的配置⽅式-->
    <bean id="userAdmin" class="com.lagou.factory.BeanFactory"
    factory-method="getAdmin"></bean>
    
  • 使⽤实例化⽅法创建

    此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是类中的⼀个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。

    <!--使⽤实例⽅法创建对象的配置⽅式-->
    <bean id="beanFactory"
    class="com.lagou.factory.instancemethod.BeanFactory"></bean>
    <bean id="transferService" factory-bean="beanFactory" factorymethod="
    getTransferService"></bean>
    

4. bean的作用范围和生命周期

  • 作用范围

    在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改变作⽤范围。作⽤范围官⽅提供的说明如下图:

    image-20210326104453911

    在上图中提供的这些选项中,我们实际开发中⽤到最多的作⽤范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:

    <!--配置service对象-->
    <bean id="transferService"
    class="com.lagou.service.impl.TransferServiceImpl" scope="singleton">
    </bean>
    
  • 生命周期

    单例模式:singleton

    对象出⽣:当创建容器时,对象就被创建了。

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

    对象死亡:当销毁容器时,对象就被销毁了。

    ⼀句话总结:单例模式的bean对象⽣命周期与容器相同。

    多例模式:prototype

    对象出⽣:当使⽤对象时,创建新的对象实例。

    对象活着:只要对象在使⽤中,就⼀直活着。

    对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。

    ⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

5. Bean标签属性

在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话说,如果⼀个对象想让spring管理,在XML的配置中都需要使⽤此标签配置,Bean标签的属性如下:

id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。

class属性:⽤于指定创建Bean对象的全限定类名。

name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。

factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。

factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。

scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。

init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。

destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。

6. DI 依赖注⼊的xml配置

1. 按照注⼊的⽅式分类

构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。

set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)

2. 按照注⼊的数据类型分类

基本类型和String

注⼊的数据类型是基本类型或者是字符串类型的数据。

其他Bean类型

注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。

复杂类型(集合类型)

注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。

3. 依赖注⼊的配置具体实现

构造函数注⼊

就是利⽤构造函数实现对类成员的赋值。它的使⽤要求是,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹配。同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。该注入方式无法解决Spring的循环依赖问题

/**
 * @author lane
 * @date 2021年03月26日 上午10:23
 */
public class User {

    private int id;
    private String username;
    private Account account;

    public User(int id, String username, Account account) {
        this.id = id;
        this.username = username;
        this.account = account;
    }
}
 <!-- 构造方法注入-->
    <bean id="user" class="com.lagou.edu.pojo.User" >
        <constructor-arg name="id" value="1"></constructor-arg>
        <constructor-arg name="username" value="lane"></constructor-arg>
        <constructor-arg name="account" ref="account"></constructor-arg>
    </bean>

在使⽤构造函数注⼊时,涉及的标签是constructor-arg ,该标签有如下属性:

name:⽤于给构造函数中指定名称的参数赋值。

index:⽤于给构造函数中指定索引位置的参数赋值。

value:⽤于指定基本类型或者String类型的数据。

ref:⽤于指定其他Bean类型的数据。写的是其他bean的唯⼀标识。

set⽅法注⼊

利⽤字段的set⽅法实现赋值的注⼊⽅式。此种⽅式在实际开发中是使⽤最多的注⼊⽅式。

在使⽤set⽅法注⼊时,需要使⽤property 标签,该标签属性如下:

name:指定注⼊时调⽤的set⽅法名称。(注:不包含set这三个字⺟,druid连接池指定属性名称)

value:指定注⼊的数据。它⽀持基本类型和String类型。

ref:指定注⼊的数据。它⽀持其他bean类型。写的是其他bean的唯⼀标识。

复杂数据类型注⼊ ⾸先,解释⼀下复杂类型数据,它指的是集合类型数据。集合分为两类,⼀类是List结构(数组结构),⼀类是Map接⼝(键值对) 。

/**
 * @author lane
 * @date 2021年03月26日 上午10:23
 */
public class User {

    private int id;
    private String username;
    private Account account;
    private String[] myArray;
    private List<String> myList ;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProperties;
    public void setMyArray(String[] myArray) {
        this.myArray = myArray;
    }

    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 setMyProperties(Properties myProperties) {
        this.myProperties = myProperties;
    }

   public void setId(int id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAccount(Account account) {
        this.account = account;
    }
   
    <!--set方法注入-->
    <bean id="user3" class="com.lagou.edu.pojo.User">
        <!--set方法注入简单类型-->
        <property name="id" value="1"></property>
        <property name="username" value="lane"></property>
        <property name="account" ref="account"></property>
        <!--set方法注入复杂类型-->
        <property name="myArray">
            <array>
                <value>good</value>
                <value>good</value>
                <value>study</value>
            </array>
        </property>
        <property name="myList">
            <list>
                <value>day</value>
                <value>day</value>
                <value>up</value>
            </list>
        </property>
        <property name="mySet">
            <set>
                <value>start</value>
                <value>study</value>
            </set>
        </property>
        <property name="myMap">
            <map>
                <entry key="i" value="study"></entry>
                <entry key="you" value="study"></entry>
            </map>
        </property>
        <property name="myProperties">
            <props>
                <prop key="we">study</prop>
            </props>
        </property>
    </bean>
User{id=1, username='lane', account=Account{cardNo='6227', name='建设银行', money=10000}, myArray=[good, good, study], myList=[day, day, up], mySet=[start, study], myMap={i=study, you=study}, myProperties={we=study}}

注意

在List结构的集合数据注⼊时, array , list , set 这三个标签通⽤,另外注值的value 标签内部可以直接写值,也可以使⽤bean 标签配置⼀个对象,或者⽤ref 标签引⽤⼀个已经配合的bean的唯⼀标识。

在Map结构的集合数据注⼊时, map 标签使⽤entry ⼦标签实现数据注⼊, entry 标签可以使⽤key和value属性指定存⼊map中的数据。使⽤value-ref属性指定已经配置好的bean的引⽤。同时entry 标签中也可以使⽤ref 标签,但是不能使⽤bean 标签。⽽property 标签中不能使⽤ref 或者bean 标签引⽤对象

7. xml与注解相结合模式

1. 具体情况

1)实际企业开发中,纯xml模式使⽤已经很少了

2)引⼊注解功能,不需要引⼊额外的jar

3)xml+注解结合xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始

4)一般原则 第三⽅jar中的bean定义在xml,⽐如德鲁伊数据库连接池。⾃⼰开发的bean定义使⽤注解模式。

2. xml中标签与注解的对应(IoC)

**标签@Component(“accountDao”)**注解加在类上bean的id属性内容直接配置在注解后⾯如果不配置,默认定义个这个bean的id为类

的类名⾸字⺟⼩写;另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,这四个注解的⽤法完全⼀样,只是为了更清晰的区分⽽已

标签的scope属性@Scope(“prototype”),默认单例,注解加在类上

标签的initmethod属性@PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法

标签的destorymethod属性@PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法

3. DI 依赖注⼊的注解实现⽅式

@Autowired按照类型注⼊(推荐使⽤)

@Autowired为Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired。

@Autowired采取的策略为按照类型注⼊。spring容器中找到类型为Account的类,然后将其注⼊进来。这样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况,这个时候我们需要配合着@Qualifier使⽤。@Qualifier告诉Spring具体去装配哪个对象。

 
public class User {
		private int id;
    private String username;
    @Autowired
    @Qualifier("account")
    private Account account;
}

@Resource 默认按照 ByName ⾃动注⼊

@Resource 注解由 J2EE 提供,需要导⼊包 javax.annotation.Resource。

如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。

如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。

如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。

如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;

@Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包javax.annotation-api.不建议使用。

public class TransferService {
@Resource
private AccountDao accountDao;
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(type="TeacherDao")
private TeacherDao teacherDao;
@Resource(name="manDao",type="ManDao")
private ManDao manDao;
}
4.开启注解包扫描

引入spring 的xmlns:context

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd" >

配置扫描包路径

<!-- 开启注解扫描,指定扫描的包路径-->
    <context:component-scan base-package="com.lagou.edu"></context:component-scan>
<!-- 引入外部资源文件,一般数据库连接信息放在jdbc.properties文件里-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

8. 纯注解模式

改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动。

1. 转移xml文件内容到Spring配置类里面,删除xml

@Configuration 注解,表名当前类是⼀个配置类

@ComponentScan 注解,替代 context:component-scan

@PropertySource,引⼊外部属性配置⽂件

@Import 引⼊其他配置类

@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息

@Bean 将⽅法返回对象加⼊ SpringIOC 容器

/**
 * 纯注解模式需要配置一个Spring的配置类,这样applicationContext.xml就可以删除了
 * @author lane
 * @date 2021年03月26日 下午2:43
 */
@Configuration //指定该类是一个Spring 的配置类
@ComponentScan({"com.lagou.edu"}) //配置ioc的注解扫描 参数为数组
@PropertySource({"classpath:jdbc.properties"}) //引入外部的properties文件
//@Import() //可以把其它配置类加载到这个里面,只需要启动这一个就可以了
public class SpringConfig {
    // 通过jdbc.properties 赋值
    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("$(jdbc.password)")
    private String password;

    //自定义bean放入ioc容器中
    @Bean
    public DataSource creatAnnotationDataSource(){
        DruidDataSource druidDataSource =  new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return   druidDataSource;
    }


}

2. 启动spring ioc 容器

Java SE模式下

 ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user2 = (User) applicationContext2.getBean("user");

Java web 下

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
  
<!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<paramvalue>
org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext</param-value>
</context-param>
  
<!--指定配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lagou.edu.SpringConfig</param-value>
</context-param>
  
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listenerclass>
org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值