SpringBean实例化的方法

依赖关系注入使用配置的方式,而不是写在固定的代码里,从而实现系统的解耦。构造器注入和设置值注入是我们在开发中比较常见的两种注入方法。Spring官方推荐的是使用构造器注入的方法,而且对于一些第三方没有暴露setter方法,就只能 使用构造器进行注入了。本文以一个用户服务类(UserService)和用户的数据持久类(UserDao)为例来进行依赖注入的介绍。

1. 构造函数注入

在Bean的配置中,使用constructor-arg的字元素配置依赖的对象,对应的Bean类需要具备对应参数的构造函数。容器反射调用带参数的构造函数进行依赖对象的初始化。

public class UserDao{
	public void add(User user){
		// 方法体是简单打印日志
		System.out.println("Insert User to DB Table :" + user.getName());
	} 
}

在UserService中定义一个带有UserDao参数的构造函数,并在add()方法中调用Userdao的添加用户方法

public class UserService{
	private UserDao userDao;
	public UserService(UserDao userDao){   //构造器初始userDao
		this.userDao = userDao;
	}
	// 服务类方法,调用userDao的对应方法
	public void add(String userName){
		User user = new User();
		user.setId(userName);
		userDao.add(user);
	}
}

到这里,和一般类的定义并没有什么区别。如果在另一个类中需要调用UserService的add()方法,传统的开发方式是先新建一个Userdao实例,再将这个实例作为参数新建一个UserService实力。使用Spring容器,UserDao的依赖直接在XML中配置,不需要在代码中显式的进行传递。

在Spring配置文件增加UserDao和UserService的配置,其中,在UserService增加constructor-arg配置子元素对应到构造函数的参数,在ref子元素的bean属性中设置依赖bean的id。XML配置如下:

<!--用户数据访问对象-->
<bean id = "userDao" class = "cn.ssm.UserDao" />
<!--用户服务-->
<bean id = "userService" class = "cn.ssm.UserService">
	<!--构造参数注入-->
	<constructor-arg ref = "userDao" />
</bean>

注意:userDao和userService在配置文件中的先后顺序并没有特定要求

2. 设置值注入

设置值注入使用的是属性的setter方法来注入依赖对象。使用setter方法设置依赖也是传统开发中较为常见的方式,使用Spring容器注入也不需要显式地调用setter方法,仅需要进行配置,由容器完成注入。

public class UserService{
	private UserDao userDao;
	//用户数据访问对象属性的setter方法
	public void setUserDao{
		this.userDao = userDao;
	}
	// 服务类方法,调用userDao的对应方法
	public void add(String userName){
		User user = new User();
		user.setName(userName);
		userDao.add(user);
	}
}

在配置文件中使用<bean>元素的property子元素注入依赖对象,name对应bean类中的属性名,ref设置为依赖bean的id。

<bean id="userService" class = "cn.ssm.UserService">
	<!--属性注入依赖-->
	<property name = "userDao" ref = "userDao" />
</bean>

由容器负责类对象的创建并对这些对象的生命周期进行管理,对象的依赖关系基于配置的方式进行注入,可以很大程度降低系统的耦合度。总结:容器实例Bean,配置要先行,依赖控制转,系统耦合散

3. Bean更多实例化的方法

3.1 静态内部类

内部类是在类的内部定义的另一个类,静态内部类是内部类的一种,是使用static关键字修饰的内部类,可以通过外部类名.静态内部类名的方式进行调用。

public class OutClass{
	static class InnerClass{
		public void innnerMethod(){
			System.out.println("This is InnerClass's InnerMethod");
		}
	}
}

调用方式如下:

OutClass.InnerClass inner = new OutClass.InnnerClass();
inner.innerMethod();

如果要使用Spring容器来初始化和管理该内部类的实例,则可以在配置文件中做如下配置:

<bean id = "innnerObject" class = "cn.ssm.OutClass $InnerClass" />

与代码使用.连接外部类和内部类不同,Bean的配置使用$来进行连接。

3.2 静态工厂法

静态工厂方法使用该类的一个静态方法返回该类的唯一静态对象。静态对象在整个应用中是唯一的,在类加载的时候产生使用“类名.静态方法”的方式引用,速度相对比较快。对于高频使用且全局唯一的某些类对象,比如配置类的实例,就可以使用这种方式。

public class StaticFactoryService{
	public static StaticFactoryService = new StaticFactoryService();
	//静态方法返回该类的静态实例
	public static StaticFactoryService getInstance(){
		return service;
	}
}

使用spring配置时,除了需要指定Bean的id和class属性之外,还要使用factory-method属性制定获取静态对象的方法。

<bean id = "staticFactoryService" class = "cn.ssm.StaticFactoryService" factory-method = "getInstace" /><!--静态方法的Bean定义-->

注意:使用静态工厂方法配置Bean也可以配置Bean的constructor-arg元素进行构造函数参数的输入。

3.3 实例工厂方法

相对于以上静态工厂方法同一个类中获取该类的静态实例,使用另一个类作为工厂类的获取目标类的状况更为常见。将目标类实例的创建放在另一个工厂类的方法中,通过配置工厂类的Bean实例和方法得到目标类实例。

下面以Foo和InstanceFactory为例,Foo是目标类,InstanceFactory是获取Foo的工厂类。

// 普通的JavaBean类
public class Foo{}

// 工厂类InstanceFactory对应如下
public class InstanceFactory{
	public static Foo foo = new Foo();
	public Foo getFooInstance(){   // 工厂方法
		return foo;
	}  
}

在配置文件中,需要配置工厂类的Bean和目标类。工厂类的Bean按一般配置即可,目标类的Bean配置不需要class属性,但需要使用factory-bean属性指定工厂类bean的id、使用factory-method属性指定获取对象实例的工厂类的方法。

<bean id = "instanceFactory" class = "cn.ssm.InstanceFactory" />
<!--工厂类bean-->
<bean id = "foo" factory-bean = "instaceFactory" factory-method = "getFooInstance" />

在现实状况中,工厂类一般用来对多个不同的目标类实例化,对应不同的实例化方法,相应的只需要增加对应的配置。

<bean id = "bar" factory-bean = "instaceFactory" factory-method = "getBarInstance" />

对比静态工厂方法和实例工厂方法,静态工厂方法获取实例的方法是静态的;实例工厂方法使用专门的工厂类获取实例,这个获取实例的方法可以不是静态的。一般而言,应用程序本身直接使用类名进行配置的方式较为直观和简单,以上方式主要是在整合第三方提供的获取特定对象的场景中使用。