SpringIOC

什么是SpringIOC?

SpringIOC(Inversion of Control)是一个容器,也就是我们通常所说的控制反转。IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由Spring来管理这些,实现解耦

ApplicationContext的实现类

Spring ApplicationContext 容器

Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文。

Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface 接口中定义。

ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。

最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext: 该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。

**ClassPathXmlApplicationContext:**该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService ss = ctx.getBean(StudentService.class);

AnnotationConfigApplicationContext:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
  
  	public static void main(String[] args) {
      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
      MyService myService = ctx.getBean(MyService.class);
      myService.doStuff();
		}
}

WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">   
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>
* id属性代表该bean在Spring容器中的唯一标识
* class属性指定该bean的类型,需要指定类的全名
Annotation配置方式

注解配置的优缺点:

  • 优点:

    配置简单。由于Java类中已经包含很上下文信息,所有在Java类上直接加注解可以省略很多属性。

  • 缺点:

    对代码有侵入性,如果改了是基于注解的配置信息改变了,需要重新编译、打包

注解的要先与xml配置方式注入,这也就意味着如果你在注解和xml中都配置了某项配置,那么xml中的配置会覆盖掉注解中的配置。

开启注解支持

我们可以通过下面两种方式开启注解扫描支持(非web工程推荐这种方式,因为这样配置后注解配置的bean和xml中配置的bean都可以加载进来)

  • 在xml配置文件中通过context命名空间中的annotation-config标签开启注解配置
<?xml version="1.0" encoding="UTF-8"?>
<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
        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:annotation-config/>
  	<!-- 开启注解支持,同时指定扫描的包路径(指定了这个就不用指定上面的) -->
  	<context:component-scan base-package="com.john.spring" />

</beans>
  • 通过注解的方式开启注解配置支持
@Configuration
@ComponentScan("com.john.spring")
public class AppConfig  {
    
  	@Bean
    public StudentDao studentDao() {
       return new StudentDao();
    }
  	// 其他bean配置....
      
    public static void main(String[] args) {
      // 如果在非web工程中使用这种方式开启注解支持,需要使用下面的方式初始化ioc容器,否则@ComponentScan注解会被忽略 
      ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

一个用@Configuration注解标注的类就相当于一个xml配置文件,我们可以给其添加一个@ComponentScan注解来开启注解扫描支持,同时指定扫描包根路径

Spring Bean
Bean的定义

bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的

Bean的属性
属性描述
class这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg它是用来注入依赖关系的
properties它是用来注入依赖关系的
autowiring mode它是用来注入依赖关系的
lazy-initialization mode延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。
Bean 的作用域
作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境
singleton 作用域:

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
    <!-- collaborators and configuration for this bean go here -->
</bean>
prototype 作用域:

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。
Prototype作用域的bean会导致在每次都会创建一个新的bean实例。
Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
简单示例:

<!-- A bean definition with singleton scope -->
	<bean id="..." class="..." scope="prototype">
<!-- collaborators and configuration for this bean go here -->
</bean>
Bean 的生命周期

懒加载
IOC容器默认在启动的时候会初始化配置文件中的所有的bean,当不是我们的代码出错而是容器加载出错可以快速提示,帮助我们排除代码错误,这叫做快速失败,大部分情况下不用修改,但特殊的时候我们不需要在容器启动时就加载bean对象,比如项目中的某个功能不常用,我们就不需要项目启动时加载这么多资源,会降低项目时效性造成浪费,需要设置这个bean对象懒加载,就是只有在调用它的时候(getBean)再加载它,具体做法是给这个bean对象加上lazy-init属性,属性值为true,默认是false,一般不写,如下:

 <bean id="studentDao" class="com.lanou3g.spring.dao.StudentDaoImpl" lazy-init="true"/>

Bean的生命周期可以表达为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁
init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。
示例:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
   public void init(){
      System.out.println("Bean is going through init.");
   }
   public void destroy(){
      System.out.println("Bean will destroy now.");
   }
}

XML
下面是 init 和 destroy 方法必需的配置文件 Beans.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-3.0.xsd">

   <bean id="helloWorld" 
       class="com.tutorialspoint.HelloWorld"
       init-method="init" destroy-method="destroy">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

下面是 MainApp.java 文件的内容。在这里,你需要注册一个在 AbstractApplicationContext 类中声明的关闭 hook 的 registerShutdownHook() 方法。它将确保正常关闭,并且调用相关的 destroy 方法。

package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      context.registerShutdownHook();
   }
}

运行结果

Bean is going through init.
Your Message : Hello World!
Bean will destroy now.

默认生命周期
如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。框架使用 元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示:

<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-3.0.xsd"
    default-init-method="init" 
    default-destroy-method="destroy">

   <bean id="blogService" class="com.something.BlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
  
  	<!--
 			其他的bean ...
		-->
  
</beans>

注解
给方法添加@PostConstruct、@PreDestroy注解
Spring 2.5以上支持

package com.john.spring.lifecycle;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 通过注解,指定bean的生命周期方法
 */
@Slf4j(topic = "LifeCycleWithAnnotation")
public class LifeCycleWithAnnotation {

    private String sname;

    @PreDestroy
    public void destroy() throws Exception {
        log.debug("lifecycle method: destroy called");
    }

    @PostConstruct
    public void init() throws Exception {
        log.debug("lifecycle method: init called");
    }

    public String getSname() {
        return sname;
    public void setSname(String sname) {
        log.debug("setSname: " + sname);
        this.sname = sname;
    }
}

XML配置

<bean class="com.john.spring.lifecycle.LifeCycleWithXML" init-method="init" 
destroy-method="destroy" p:sname="张三" />

输出结果

13:05:27.070 [main] DEBUG LifeCycleWithAnnotation - setSname: 张三
13:05:27.072 [main] DEBUG LifeCycleWithAnnotation - lifecycle method: init called
13:05:27.142 [main] DEBUG LifeCycleWithAnnotation - lifecycle method: destroy called
实例化Beans

通过构造方法实例化

这种方式是最常用的方式,适合绝大多数的javabean,因为我们的java类无需继承任何父类或实现任何接口。但是我们通常需要提供一个无参的或者有参的构造方法。

<!-- 无参构造 -->
<bean id="exampleBean" class="examples.ExampleBean"/>
<!-- 有参构造方法 -->
<bean name="anotherExample" class="examples.ExampleBeanTwo">
	<constructor-arg name="thingTwo" value="参数为非引用类型"/>
    <!-- 参数为引用类型 -->
    <constructor-arg name="thingThree" ref="exampleBean"/>
</bean>

通过静态工厂方法实例化

<bean id="clientService" class="examples.ClientService" factory-method="createInstance">
	<!-- 如果工厂方法需要参数,通过此标签传参 -->
  <constructor-arg name="cname" value="TestService" />
</bean>

对象工厂方法实例化

<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<!-- the bean to be created via the factory bean -->
<bean id="accountService"
    factory-bean="serviceLocator"
      factory-method="createAccountServiceInstance">

  	<!-- 如果工厂方法需要参数,通过此标签传参 -->
  	<!-- <constructor-arg name="cname" value="TestService" /> -->
  
</bean>
```java
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

  	private static AccountService accountService = new AccountServiceImpl();
  
    public ClientService createClientServiceInstance() {
        return clientService;
    }
  
  	public AccountService createAccountServiceInstance() {
        return accountService;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值