Re:从零开始的Spring入门之IoC(二)

文章目录

    • 一、Spring核心
      • 1.IoC
      • 2.Aop
    • 二、Spring IoC
      • 1.BeanFactory与ApplicationContext
        • 1)Java环境下启动IoC容器
        • 2)Web环境下启动IoC容器
        • 3)web环境下获取容器
      • 2.Bean
        • 1)Bean的实例化
        • 2)Bean的作用域和生命周期
        • 3)Bean的标签属性
        • 4)Bean的依赖注入
      • 3.xml与注解
        • 1)xml方式
        • 2)注解方式和xml混合
        • 3)依赖注入的实现方式
      • 4.纯注解方式
      • 5.lazy-Init 延迟加载

一、Spring核心

1.IoC

IoC:Inversion of Control 控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

其中最常见的方式叫做 依赖注入(Dependency Injection)。

  • IOC(Inversion of Control),把创建对象的控制权从程序员手里交给Spring来管理。也就是说以后创建对象我们不用再new了,而是通过编写配置文件来告知容器去帮我们创建哪些对象,创建出来的对象放到Spring容器中来进行管理调度.降低了对象之间的耦合度。
  • DI(Dependency Injection),在实际应用中,对象之间是存在彼此依赖的。在运行的过程中我们要把对象赋值给另一个对象,彼此之间互相调用来完成相应的功能。也就是说把一个对象注入给另一个对象。

2.Aop

AOP:Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程。

在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

二、Spring IoC

Re:从零开始的Spring入门之IoC(二)_第1张图片

1.BeanFactory与ApplicationContext

BeanFactory 是Spring框架中IoC容器的顶层接口,它只是用来定义⼀些基础功能,定义⼀些基础规范,而 ApplicationContext 是它的⼀个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。

通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接口,比BeanFactory要拥有更多的功能,比如说国际化⽀持和资源访问(xml,java配置类)等等。

1)Java环境下启动IoC容器
  • ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
@Test
public void testSpringHello(){
	// 通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	    
    //获取Bean实例
	AccountDao dao = context.getBean("accountDao",AccountDaoImpl.class);
    //AccountDao dao = (AccountDaoImpl)context.getBean("accountDao");
	System.out.println(dao);
    
    context.close();
}

<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 id="accountDao" class="com.lagou.dao.impl.AccountDaoImpl" scope="singleton"/>
  
beans>
2)Web环境下启动IoC容器
  • 从xml启动容器

<web-app>
  
  <display-name>Archetype Created Web Applicationdisplay-name>
  
  
  <context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>classpath:applicationContext.xmlparam-value>
  context-param>
  
  
  <listener>
    <listenerclass>org.springframework.web.context.ContextLoaderListenerlistenerclass>
  listener>
  
web-app>
  • 从配置类启动容器
@Configuration // 表明当前类是一个配置类
@ComponentScan({"com.lagou"}) //开启注解扫描
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
	//...
}

<web-app>
  
  <display-name>Archetype Created Web Applicationdisplay-name>
  
  
  <context-param>
    <param-name>contextClassparam-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContextparam-value>
  context-param>
  
  
  <context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>com.lagou.config.SpringConfigparam-value>
  context-param>
  
  
  <listener>
    <listenerclass>org.springframework.web.context.ContextLoaderListenerlistenerclass>
  listener>
web-app>
3)web环境下获取容器
Override
public void init() throws ServletException {
    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    accountService = (AccountService) webApplicationContext.getBean("accountService");
}

2.Bean

xml 文件头


<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 ">

  
beans>
1)Bean的实例化
  • ⽅式⼀:使⽤⽆参构造函数

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


<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"/>
  • ⽅式⼆:使⽤静态方法创建

在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程中会做很多额外的操作。此时会提供⼀个创建对象的方法,恰好这个方法是 static 修饰的⽅法,即是此种情况。

例如,我们在做 Jdbc 操作时,会⽤到 java.sql.Connection接口的实现类,如果是mysql数据库,那么用的就是 JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因为我们要注册驱动,还要提供 URL 和凭证信息, 用 DriverManager.getConnection 方法来获取连接。

那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤此种⽅式配置。


<bean id="accountService" class="com.lagou.factory.BeanFactory1" factory-method="getAccountService"/>
public class BeanFactory1 {
	public static AccountService getAccountService() {
		return new AccountServiceImpl();
	}
}
  • ⽅式三:使用实例化方法创建

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

在早期开发的项⽬中,工厂类中的⽅法有可能是静态的,也有可能是非静态⽅法,当是非静态方法时,即可采用下面的配置⽅式。


<bean id="beanFactory" class="com.lagou.factory.BeanFactory2"/>
<bean id="accountService" factory-bean="beanFactory" factorymethod="getAccountService"/>
public class BeanFactory2 {
	public AccountService getAccountService() {
		return new AccountServiceImpl();
	}
}
2)Bean的作用域和生命周期
  • 作用域

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

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

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

<!--配置service对象-->
<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl" scope="singleton"/>
  • 不同作用域的生命周期

单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。

多例模式:prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

3)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时起作用。
4)Bean的依赖注入
@Data
public class Student {
    private Long id;
    private String name;
    private String sex;
    private String address;
}
  • 构造函数注⼊
<bean id="student" class="com.example.pojo.Student">    
    <constructor-arg name="id" value="01"/>
    <constructor-arg name="name" value="李白"/>
    <constructor-arg name="sex" value=""/>
    <constructor-arg name="address" value="长沙"/>
bean>
  • set方法注入
<bean id="student4" class="com.examplepojo.Student">
  	<property name="id" value="01"/>
  	<property name="name" value="李白"/>
   	<property name="sex" value=""/>
	  <property name="phone" ref="phone2"/>
bean>
  • 复杂类型注入
@Data
public class Student {
    private Long id;
    private String name;
    private String sex;    
    private Phone phone;

    private String[] hobbies;
    private List<Book> books;
    private Map<String,Girl> girls;
    private Set<Dog> dog;
    private Properties props;
}
<bean id="student" class="com.example.pojo.Student">
  <property name="id" value="1">property>
  <property name="name" value="李白">property>
  <property name="sex" value="">property>
  <property name="phone" ref="phone">property>
  
  <property name="hobbies">
    <array>
      <value>足球value>
      <value>篮球value>
      <value>网球value>
    array>
  property>
  
  <property name="books">
    <list>
      <ref bean="b1">ref>
      <ref bean="b2">ref>
    list>
  property>
  
  <property name="girls">
    <map>
      <entry key="小红" value-ref="girl1">entry>
      <entry key="小绿" value-ref="girl2">entry>
    map>
  property>
  
  <property name="dog">
    <set>
      <ref bean="dog1">ref>
      <ref bean="dog2">ref>
    set>
  property>
  
  <property name="props">
    <props>
      <prop key="username">张三prop>
      <prop key="password">123456prop>
    props>
  property>
bean> 

3.xml与注解

  1. 实际企业开发中,纯xml模式使用已经很少了
  2. 引入注解功能,不需要引⼊额外的jar
  3. xml+注解结合模式,xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始
  4. 哪些bean的定义写在xml中,哪些bean的定义使⽤注解
    第三⽅jar中的bean定义在xml,⽐如Druid数据库连接池
    ⾃⼰开发的bean定义使⽤注解
1)xml方式

<bean id="accountService" class="com.lagou.service.impl.accountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
bean>


<bean id="accountDao" class="com.lagou.dao.impl.accountDaoImpl">
    <property name="qr" ref="queryRunner"/>
bean>


<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
bean>


<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/day0925"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
bean>


<bean id="connectionUtils" class="com.lagou.util.ConnectionUtils"/>


<bean id="transactionManager" class="com.lagou.util.TransactionManager">
    <property name="ConnectionUtils" ref="connectionUtils"/>
bean>


<bean id="proxyFactory" class="com.lagou.util.ProxyFactory">
    <property name="TransactionManager" ref="transactionManager"/>
bean>
    @org.junit.Test
    public void service() throws Exception {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
        int transfer = accountService.transfer("6029621011000", "6029621011001", 1);
        System.out.println(transfer);
    }
2)注解方式和xml混合
  • @Controller 修饰Controller的

  • @Service 修饰Service层的

  • @Repository 修饰Dao层

  • @Component 修饰类的 除了上面这些注解修饰的类以外


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://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"/>

    
    <context:property-placeholder location="classpath:jdbc.properties"/>	

	    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>

    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    bean>

beans>
3)依赖注入的实现方式
  • @Autowired
    为Spring提供的注解,采取的策略为按照类型注⼊

  • @Qualifier
    找容器中指定id名的对象来进行注入

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class AccountServiceImpl implements AccountService {

	@Autowired
	@Qualifier("accountDao")
    private AccountDao accountDao;
    
}

4.纯注解方式

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

对应注解

  • @Configuration 注解,表名当前类是⼀个配置类
  • @ComponentScan 注解,替代 context:component-scan
  • @PropertySource,引⼊外部属性配置⽂件
  • @Import 引⼊其他配置类
  • @Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
  • @Bean 将⽅法返回对象加⼊ SpringIOC 容器
@Configuration //表明当前类是一个配置类
@ComponentScan({"com.lagou"}) //开启注解扫描
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {

    @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("dataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return  druidDataSource;
    }

    @Bean //未指定 bean 的名称,默认采用的是 "方法名" + "首字母小写"的配置方式
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}
    @Test
    public void datasource() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = applicationContext.getBean("dataSource", DataSource.class);
        System.out.println(dataSource.getConnection());
    }

5.lazy-Init 延迟加载

ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前 实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。


<bean id="lazyResult" class="com.lagou.pojo.Result" lazy-init="true" />

lazy-init=“false”,⽴即加载,表示在spring启动时,立刻进行实例化。 如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean 设置为延迟实例化。

public class IoCTest {

    //测试bean的lazy-init属性
    @Test
    public void testBeanLazy(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Object lazyResult = applicationContext.getBean("lazyResult");
        System.out.println(lazyResult);
        applicationContext.close();
    }
}

如果⼀个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化。

  • 应⽤场景

(1)开启延迟加载⼀定程度提⾼容器启动和运转性能该bean默认的设置为:
(2)对于不常使⽤的 Bean 设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean 就占用资源

你可能感兴趣的:(java,spring,ioc,bean)