Spring核心之IoC的两种实现方式

前言

在刚接触java的时候,映像最深刻的一句话就是:需要使用对象的时候,就直接new一个对象即可。但是,像对于Mybatis中的核心对象SqlSessionFactory、工作流引擎Activity中的核心对象ProcessEngine,还有我们的业务逻辑组件、数据库操作组件等等,只要核心对象一创建,就应该在应用执行期间都存在,且不应该在应用期间重复创建。我们当然可以用单例模式处理,但是当大量对象充斥着我们的应用,还有各个核心对象之间的依赖关系,管理起来就很头疼。所以利用Spring的IoC来管理就可以为我们减少这些问题。

IoC(Inversion of Control,IoC),控制反转,它更是一个容器,用来管理我们应用中核心对象的容器,这些核心对象在Spring中称为Spring Bean(简称Bean)。这个容器具备着以下两个功能:

1、通过描述管理Bean,包括发布和获取Bean;

2、通过描述完成Bean之前的依赖关系。

何为控制反转?在我们刚接触java的时候,如果要在A对象里面使用B对象,就应该是在A中new一个B对象,这种过程是一个正向的,那么什么是反向呢?就是A类不再主动去获取B,而是被动等待,等待IoC容器获取一个B的实例,然后反向的注入到A类中。曾经有一个很美妙的形容来形容这种关系:叫好莱坞原则(Don't call me, I will call you,不要呼叫我,等着我呼叫你),基于这一原则,提出了一个更为准确的概念叫依赖注入(DI,Dependency Injection)。所以,控制反转(IoC)和依赖注入(DI)是对同一件事情的不同描述,从某个方面讲,DI就是描述着IoC的过程。

在IoC容器中,所有的bean都有BeanFactory来创建、配置、管理,它采用了经典的工厂模式。它是一个顶级容器接口。BeanFactory接口里面的主要有以下方法(可自行点进BeanFactory源码查看):

方法 说明

Object getBean(String name) throws BeansException;

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

多个getBean的方法,可以通过Bean的name(by name),也可以通过bean的类型(by type)。
boolean containsBean(String name); 是否包含Bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

Bean是否是单例,这里需要注意的是,默认情况下,

Bean都是以单例形式存在的,与其相反的是原型模式的Bean,

当我们创建了一个原型模式下的Bean,I

oC容器就会创建一个新的Bean返回给调用者,这与Bean的作用域有关。

boolean isPrototype(String name) throws NoSuchBeanDefinitionException; Bean是否是原型,也就是"多例"

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

是否类型匹配
Class<?> getType(String name) throws NoSuchBeanDefinitionException; 获取Bean的类型
String[] getAliases(String name); 获取Bean的别名

 

在Spring的发展中,在BeanFactory的基础上,还设计了一个更为高级的接口,ApplicationContext,它通过继承上级接口进而继承BeanFactory,它在BeanFactory的基础上,扩展了消息国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、应用事件接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver)。那么,BeanFactory有的方法ApplicationContext自然也会有,后面的示例就直接用ApplicationContext进行。

ApplicationContext接口有以下3个常用实现类,可以实例化任何一个类来创建Spring的ApplicationContext容器。

ApplicationContext实现类 说明
ClassPathXmlApplicationContext 它可以从当前类路径中检索配置文件并装载它来创建容器的实例
FileSystemXmlApplicationContext 它和ClassPathXmlApplicationContext唯一的区别在于读取配置文件的方式,它不在从类路径下获取配置文件,它可以通过指定位置的方式读取配置文件,还可以获取类路径之外的资源
AnnotationConfigApplicationContext 它一般在使用注解方式配置Bean的时候,通过指定配置类的方式创建容器实例

 

环境配置

pom文件(这里只使用IoC,所以不用引入Spring的其它组件,这篇文章讨论的是实现IoC的xml和注解形式,所以直接引入最高版本)

<!-- spring -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-core</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-context</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-beans</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<!-- 日志 -->
	<dependency>
	    <groupId>org.apache.logging.log4j</groupId>
	    <artifactId>log4j-core</artifactId>
	    <version>2.11.1</version>
	</dependency>
	<dependency>
	    <groupId>log4j</groupId>
	    <artifactId>log4j</artifactId>
	    <version>1.2.17</version>
	</dependency>
	<dependency>
	    <groupId>org.apache.logging.log4j</groupId>
	    <artifactId>log4j-api</artifactId>
	    <version>2.11.1</version>
	</dependency>
	<dependency>
	    <groupId>commons-logging</groupId>
	    <artifactId>commons-logging</artifactId>
	    <version>1.2</version>
	</dependency>

日志

#全局的日志配置
log4j.rootLogger=ERROR, stdout
#自定义日志

#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

利用spring的插件生成一个spring的配置文件,项目结构如下(没有插件的可参考这篇安装)

利用XML形式实现IoC

1、简单的示例

<bean id="user" class="com.zepal.spring.bean.User"/>
public class User {
	
}
public class Test {

	public static void main(String[] args) {
		//这里还可以直接使用顶级接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按类型获取by type
		User user1 = context.getBean(User.class);
		//按名称获取by name(在配置文件中,我们将bean声明为了user)
		User user2 = (User) context.getBean("user");
		System.out.println(user1 == user2);
		System.out.println("是否单例 :" + context.isSingleton("user"));
		System.out.println("是否原型 :" + context.isPrototype("user"));
	}
}
true
是否单例 :true
是否原型 :false

说明:在调用getBean()方法之前,容器不会实例化任何对象,只有在需要使用User对象的时候,才会创建Bean,有点懒加载的意思,这使它更适合于物力资源受限制的应用程序,尤其是内存限制的环境。Spring中Bean的生命周期包括实例化Bean、初始化Bean、使用Bean、销毁Bean。

2、依赖注入

实现依赖注入有三种方式

接口注入:基于接口将调用与实现分离。这种依赖注入的方式必须实现容器所规定的接口,使程序代码和容器的API绑定在一起,这不是理想的依赖注入方式,所以Spring也不支持这种方式;

Setter注入:基于JavaBean的set()方法为属性赋值。在实际开发中得到了最广泛的应用。

public class User {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
}

构造器注入:基于构造方法为属性赋值,容器通过调用类的构造方法,将其所需的依赖关系注入其中。

public class User {
	
	private String name;
	
	public User(String name) {
		super();
		this.name = name;
	}
}

在Spring中,无论使用哪种容器,都要从配置文件中读取JavaBean的信息,再根据自定义信息去创建JavaBean的实例对象并注入其依赖属性,常用的比如整合Mybatis、Redis等。由此可见,Bean的配置也是针对配置文件进行的。

--setter注入

JavaBean对象

/**
 * 假设这是我们项目中的mybatis
 * */
public class Mybatis {

	private String driver;
	
	private String url;
	
	private String userName;
	
	private String passWord;

	//......
}

配置Bean(注意:在JavaBean的属性是List集合类型或数组时,需要使用<list>标签为其每一个元素赋值

<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<property name="driver" value="驱动"/>
		<property name="url" value="连接地址"/>
		<property name="userName" value="用户名"/>
		<property name="passWord" value="密码"/>
</bean>
public class Test {

	public static void main(String[] args) {
		//这里还可以直接使用顶级接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按类型获取by type
		Mybatis mybatis = context.getBean(Mybatis.class);
		System.out.println(mybatis.getDriver());
		System.out.println(mybatis.getUrl());
		System.out.println(mybatis.getUserName());
		System.out.println(mybatis.getPassWord());
		//这里初始化Mybatis之后,在实际开发中就相当于初始化了SqlSessionFactory,
		//就可以直接使用openSession()的方法直接开始盘CRUD了。
	}
}
驱动
连接地址
用户名
密码

--构造器注入

其它不变,修改配置文件。注意:这里要和构造方法的参数顺序一致,否则会产生异常。<constructor-arg>标签提供index和type属性可以自定义顺序,为构造方法入参,index是索引,type是参数类型。

<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<constructor-arg value="驱动"/>
		<constructor-arg value="连接地址"/>
		<constructor-arg value="用户名"/>
		<constructor-arg value="密码"/>
	</bean>

运行结果一致

驱动
连接地址
用户名
密码

--引用其它的bean,通过ref属性。(比如,我们的数据库信息是直接加载进Spring的context作用域中,在配置SqlSessionFactory的Bean的时候,是引用了这个bean)

public class User {
	
	private Mybatis mybatis;
    
    //....
}
<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<constructor-arg value="驱动"/>
		<constructor-arg value="连接地址"/>
		<constructor-arg value="用户名"/>
		<constructor-arg value="密码"/>
	</bean>
	
	<bean id="user" class="com.zepal.spring.bean.User">
		<property name="mybatis" ref="mybatis"/>
	</bean>
//这里还可以直接使用顶级接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按类型获取by type
		User user = context.getBean(User.class);
		System.out.println(user.toString());
User [mybatis=Mybatis [driver=驱动, url=连接地址, userName=用户名, passWord=密码]]

--匿名内部类的注入形式。匿名内部类的形式可以不指定内部类的id

<!-- 在School类中定义Student类 -->
	<bean id="school" class="com.zepal.spring.bean.School">
		<property name="student" >
			<bean class="com.zepal.spring.bean.Student"/>
		</property>
	</bean>

3、自动装配

--按bean的名称装配

说明:<bean>标签的byname属性以属性名称区分自动装配。在容器中寻找与JavaBean的属性名称相同的bean,并将其自动装配到JavaBean中

public class User {
	
	private Mybatis mybatis;

	//省略set方法
	
	public void printMybatisInfo() {
		System.out.println(mybatis.getDriver());
		System.out.println(mybatis.getUrl());
		System.out.println(mybatis.getUserName());
		System.out.println(mybatis.getPassWord());
	}
}
<bean autowire="byName" id="user" class="com.zepal.spring.bean.User"/>
public static void main(String[] args) {
		//这里还可以直接使用顶级接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按类型获取by type
		User user = context.getBean(User.class); 
		user.printMybatisInfo();
	}
驱动
连接地址
用户名
密码

--按bean的类型装配

<bean autowire="byType" id="user" class="com.zepal.spring.bean.User"/>

执行同样的测试代码,会有相同的结果。

在Spring中还有3种不太常用的自动装配方式。

装配方式 说明
no属性 这是autowire采用的默认值,它采用自动装配。必须使用ref直接引用其它的bean。这样可以增加代码的可读性,并且不易出错
constructor属性 通过构造方法的参数类型自动装配。此类型会使容器自动寻找与JavaBean的构造方法的参数类型相同的bean,并注入到需要自动装配的bean中,它与byType类型存在相同的无法识别自动装配的情况
autodetect属性 首先会使用constructor方式来自动装配,然后使用byType方式,当然此类型也存在与byType和constructor相同的异常情况。建议在使用自动装配时把容易出现问题的bean使用手动装配注入依赖属性。

特别注意:a、按bean的名称(byName)自动装配有可能存在类型不匹配的错误。如果在配置文件中定了与需要自动装配bean的属性名称相同而类型不同的bean,那么它可能会错误的注入不同类型的Bean。

b、按类型装配(byType),假如某一接口有多个实现类,在配置文件中,为其每个实现类都配置了Bean,byType无法自动识别到底哪一个才是需要的bean。要解决此问题,要么通过手动装配,或者使用消除歧义的方式。

public interface IPersonService {

	void work();
}
public class Teacher implements IPersonService {

	private String workTime;
	
	public String getWorkTime() {
		return workTime;
	}

	public void setWorkTime(String workTime) {
		this.workTime = workTime;
	}

	@Override
	public void work() {
		System.out.println("教师的工作是教书育人,教书"+this.workTime);
		
	}
}
public class Programmer implements IPersonService {

	private String workTime;
	
	public String getWorkTime() {
		return workTime;
	}

	public void setWorkTime(String workTime) {
		this.workTime = workTime;
	}

	@Override
	public void work() {
		System.out.println("程序员的工作是搬砖,搬砖"+this.workTime);
	}
}
<bean id="teacher" class="com.zepal.spring.bean.Teacher">
		<property name="workTime" value="7105"/>
	</bean>
	<bean id="programmer" class="com.zepal.spring.bean.Programmer">
		<property name="workTime" value="996"/>
	</bean>
//这里还可以直接使用顶级接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		IPersonService iPersonService = context.getBean(IPersonService.class);
		System.out.println("和谐社会离不开这些的辛勤工作...");
		iPersonService.work();
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zepal.spring.bean.IPersonService' available: expected single matching bean but found 2: teacher,programmer

出现这个问题的原因就是,IPersonService接口有两个实现类,将其作为bean的时候,不知道该使用哪一个了。

<bean primary="true" id="teacher" class="com.zepal.spring.bean.Teacher">
		<property name="workTime" value="7105"/>
	</bean>

我们在teacher的bean上加上属性primary="true",将某一个实现类标记为主bean,再运行

和谐社会离不开这些的辛勤工作...
教师的工作是教书育人,教书7105

或者我们去掉primary="true",byName获取,同样的效果。

ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		IPersonService iPersonService = (IPersonService) context.getBean("teacher");
		System.out.println("和谐社会离不开这些的辛勤工作...");
		iPersonService.work();
和谐社会离不开这些的辛勤工作...
教师的工作是教书育人,教书7105

4、bean的作用域

a.在没有注解之前,Spring中Bean的定义和Bean相互之间的依赖关系都是通过配置XML文件中的元数据来实现的,Spring中的Bean就是由容器初始化、装配及管理的对象,除此之外,bean和程序中的其它类并没有什么区别

<bean>的常用属性

属性名 说明
id 代表Bean的实例对象,在Bean实例化后可以通过id来引用其实例对象。
name 代表Bean的实例对象名称
class Bean的类名,全路径,必须属性
scope 设置bean的作用域,有4个属性值singleton(单例)、prototype(原型)、session(web中session的作用域)、request(request的作用域相同)
autowire 自动装配功能
primary 接受Boolean类型,用于消除多个bean的歧义,标记主bean
lazy-init 是否延迟注入,默认情况下,容器只有在使用Bean的时候才会加载,和Mybatis懒加载一个意思
init-method 指定bean的初始化方法
destroy-method 指定bean被回收之前调用的销毁方法
depends-on 用于保证在depends指定的Bean被实例化之后,再实例化自身的bean

简单测试一下作用域:

<bean scope="prototype" id="mybatis" class="com.zepal.spring.bean.Mybatis"/>
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		System.out.println(context.isSingleton("mybatis"));
		System.out.println(context.isPrototype("mybatis"));
false
true

说明:一旦Bean被设置为prototype模式(非单例),每次引用Bean都会创建一个新的对象,容器则不再拥有当前返回对象的引用,容器将实例对象的生命周期交给请求方管理,所以在客户端代码中必须使用bean的后置处理器清楚prototype作用域的bean。一般来说,对有所状态的bean应该设置为prototype作用域,对无状态的bean则应该使用singleton作用域,常见的,DAO层不会被配置成prototype,因为一个典型的DAO不会持有任何会话状态,像session这种的,带有一定状态的,则不能是singleton。

XML配置Bean的形式完结了,平时在项目中都是用@Autowired获取Bean,那是因为一起动项目就去加载了配置文件,上面的示例都是通过手动加载的形式。

使用注解形式实现IoC

pom依赖和XML一样,因为用注解,所以不再需要配置文件

1、实现一个简单的Bean

public class User {

	private String name;
	
	private String age;
	
	private String gender;

	//省略set/get和构造方法
}
@Configuration
public class AppConfig {

	@Bean(name="user")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
}
public class Test {

	ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		//User user = context.getBean(User.class);
		User user = (User) context.getBean("user");
		System.out.println(user.toString());
		System.out.println("是否单例 :" + context.isSingleton("user"));
		System.out.println("是否原型 :" + context.isPrototype("user"));
}
User [name=zepal, age=18, gender=男]
是否单例 :true
是否原型 :false

说明:@Configuration指定当前类是一个配置类,类似于XML中的applicationContext.xml配置文件。@Bean类似于<bean>标签,指定返回的java类装配到IoC容器中,一般都会返回Bean的类型。name属性指定bean的名称,类似id属性,如果没有这项属性,会默认将"initUser"方法名作为bean的名称,在byName获取Bean的时候需要注意。

在注解下,很多属性被独立出来,比如设置作用域是单独的注解@Scope。示例:

@Bean(name="user")
	@Scope("prototype")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
System.out.println(user.toString());
System.out.println("是否单例 :" + context.isSingleton("user"));
System.out.println("是否原型 :" + context.isPrototype("user"));

---运行结果---

User [name=zepal, age=18, gender=男]
是否单例 :false
是否原型 :true

@Bean的常用属性都和<bean>标签中的类似,可以通过提示查看,再次不赘述了。

@Scope的作用域比起XML多了两种,application(整个web的上下文环境,即整个生命周期)、globalSession(一个全局的session,一个Bean定义对应一个session)。

2、通过扫描装配Bean

一般使用@Component+@ComponentScan组合。

@Component用于标记哪个类被扫描进IoC,@ComponentScan则是标明采用哪种策略去扫描装配Bean。类似于XML中的<context:component-scan>标签。

@Component可以指定Bean的名称,如果不指定,则按照类型首字母小写作为Bean的名称,byName获取的时候注意。@ComponentScan默认扫描当前包或其子包,所以一般在springboot项目中,所有包都是启动包的子包,@SpringBootApplication启动注解,在1.5之后包含了@ComponentScan注解,所以在SpringBoot中不单独指定。所以像我们的服务层用的@Service注解才会被扫描注册为Bean,@Service里也涵盖了@Component注解。

@ComponentScan在指定扫面包的时候可以采用通配符,还有一些其他功能比如过滤器,指定不扫描哪些包等等。

示例如下:

@Component("student")
public class Student {

	@Value("张三")
	private String name;
	
	@Value("18")
	private String age;

	//省略其它方法
}
@ComponentScan(basePackages= {"com.zepal.spring.bean"})
@Configuration
public class AppConfig {

	@Bean(name="user")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		Student student = context.getBean(Student.class);
		System.out.println(student.toString());

---运行结果---
Student [name=张三, age=18]

3、消除bean配置的歧义性

在下面的示例中,我们很容易利用依赖注入

public class Dog {

	private String breed;//品种
	
	private String age;
	
	//省略set/get和构造方法
}
public class Person {

	private String name;
	
	@Autowired
	private Dog dog;
	
	//省略set/get和构造方法
}
    @Bean(name="dog")
	public Dog initDog() {
		Dog dog = new Dog();
		dog.setBreed("哈士奇");
		dog.setAge("3");
		return dog;
	}
	
	@Bean(name="person")
	public Person initPerson() {
		Person p = new Person();
		p.setName("张三");
		return p;
	}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.getName() + "有一条" + person.getDog().getAge() + "岁的" + person.getDog().getBreed());

----执行结果----

张三有一条3岁的哈士奇

假如我们注入的是一个接口,且接口有两个或以上的实现类,就会出现IoC不知道该使用哪一个Bean了。

这种场景一般出现在,在我们的service层中,假如服务接口出现多个实现类,如果不作处理,在启动项目扫描时就会出现错误

public interface IAnimalService {

    //这里还可以搞其他事情
}
public class Dog implements IAnimalService {

	private String breed;//品种
	
	private String age;
	
	//省略set/get和构造方法
}
public class Cat implements IAnimalService {

	private String breed;
	
	private String age;
	
	//省略set/get和构造方法
}
public class Person {

	private String name;
	
	@Autowired
	private IAnimalService iAnimalService;
	
	//省略set/get和其它构造方法
}
    @Bean(name="dog")
	public Dog initDog() {
		Dog dog = new Dog();
		dog.setBreed("哈士奇");
		dog.setAge("3");
		return dog;
	}
	
	@Bean(name="cat")
	public Cat initCat() {
		Cat cat = new Cat();
		cat.setBreed("波斯猫");
		cat.setAge("4");
		return cat;
	}
	
	@Bean(name="person")
	public Person initPerson() {
		Person p = new Person();
		p.setName("张三");
		return p;
	}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

----执行结果----

.....expected single matching bean but found 2: dog,cat

从控制台提示的错误信息可以看到,有dog和cat两个Bean,IoC不知道该注入哪一个了,也就是产生歧义了。

利用@Primary和@Qualifier消除这个问题。

--@Primary,这里我们将Cat声明为主Bean,

    @Bean(name="cat")
	@Primary
	public Cat initCat() {
		Cat cat = new Cat();
		cat.setBreed("波斯猫");
		cat.setAge("4");
		return cat;
	}

运行结果

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

---运行结果---

Person [name=张三, iAnimalService=Cat [breed=波斯猫, age=4]]

--@Qualifier,这里去掉@Primary,在注入的时候,声明@Qualifier

注意:@Qualifier是按Bean的名称(byName)去寻找Bean的,所以,在配置Bean的时候需要给定一个名称,如果使用IoC默认生成的名称需要注意。这个注解经常出现在service层和controller层的组合中。@Service("xxx"),然后在Controller层注入服务层接口的时候利用@Qualifier声明服务层Bean的名称

@Autowired
@Qualifier("cat")
private IAnimalService iAnimalService;
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

---运行结果---

Person [name=张三, iAnimalService=Cat [breed=波斯猫, age=4]]

@Qualifier和@Autowired还可以用于形参当中,可根据自身需求而定,比如

public Person(String name,@Autowired @Qualifier("cat") IAnimalService iAnimalService) {
		super();
		this.name = name;
		this.iAnimalService = iAnimalService;
	}

4、生命周期和作用域

在XML形式中,修改Bean的默认加载过程,可以使用属性lazy-init,在注解中有一个独立的注解@Lazy(value=true),它只接受boolean类型的参数,如果使用@ComponentScan去扫面注解,它有一个属性lazyInit可以配置。同样的,在@Bean注解中,有initMethod和destroyMethod属性可以指定Bean初始化的方法和销毁的方法。

Bean的作用域在IoC注解的开头已经说明过了,这里就不重复累赘了。


此篇完结

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章