初见Sping之Bean
Spring一款优秀的开源框架,现在已经成为了java开发的标配框架,Spring框架提供的主要特性便是IOC(Inversion of control,控制反转)和DI(Dependency Injection,依赖注入)。这两个概念紧密联系,却又有些许不同。
一丶依赖注入和控制反转
(1)Spring 中的控制反转是基于这样的一种场景:
User user =new User();
上述获取对象的方法是直接new出来,对象何时new,在哪里new都是客户端代码直接决定的,这称为硬性编码。
User user =spring.getUser();
上述获取对象的方法是通过一个方法得到的,对象何处创建,创建的对象究竟是User的实例还是User的子类实例等等都是由applicationContex这个容易决定的,想对应的可以称为软编码。从硬性编码到软编码的过程中对象控制权限发生了转移,由客户端的代码转移到了spring,这种过程就是控制反转的含义,就是工厂设计模式的一种体现。
(2)依赖注入
依赖注入是基于控制反转概念上的一种延伸,个人理解是对控制反转这种思想的具体应用。考虑如下情景。
class UserService{
publicUserDao userDao;
}
class UserDao{
}
UserService是一个类,UserDao是其的一个成员变量,在这种情况下Userservice依赖UserDao,UserDao是被依赖类。在spring实现控制反转的概念上,spring需要创建一个Userservice对象,那么对Userservice对象所依赖的UserDao对象进行赋值的过程就是依赖注入,说白了就是一个赋值的过程,只是这个赋值的方法和过程由Spring管理,这点上联系了依赖注入的概念。
二丶Spring在控制反转上的体现
Spring管理Bean对象的创建是IOC的一种体现,本文主要总结Spring创建Bean对象方式。
(1)直接利用构造方法创建Bean对象。
每个对象被创建时都会调用构造方法,此时将这个方法单列出来是因为Spring还有许多绕着弯创建Bean对象的方法,后续会描述。
Bean 对象设置如下:
class User{
public User(){
System.out.println(“user构造方法被调用了”);
}
}
Spring配置文件如下:
<?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-4.3.xsd">
<bean name="user"class ="com.hello.spring.User"/>
</beans>
测试方法如下:
public class Main{
publicstatic void main(String []args){
//获取Spring容器
ApplicationContextcontext = newClassPathXmlApplicationContext("applicationContext.xml");
User user =(User)context.getBean("user");
}
}
输出结果:
图1程序运行结果
(2)利用静态工厂的方法创建Bean对象
方法(1)是Spring直接利用构造方法创建Bean对象,这点从Spring的配置文件中也可以看出来,class属性直接是User类的权限定类型。利用静态工厂创建Spring的方法和上述方法较大不同,下面来看一下代码的组合以及Spring配置文件的改变。
Bean对象:
class Bean1{
publicBean1(){
System.out.println(“Bean1的方法被调用”)
}
public void say(){
System.out.println(“bean1say good afternoon”);
}
}
工厂类:
class BeanFactory{
publicstatic Bean1 getBean(){
System.out.println(“静态工厂方法被调用”)
returnnew Bean1();
}
}
测试代码:
public class Main1{
publi cstatic void main(String []args){
ApplicationContextcontext = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean1 bean1= (Bean1) context.getBean("bean1");
bean1.Say();
}
}
Spring的配置文件:
<bean name =”bean1” class=”Bean.BeanFactory”factory-method=”getBean”></bean>
测试结果:
图2 静态工厂运行结果
从上图的运行结果可以知道,利用这种方式配置Bean时,Spring并不是直接new Bean1(),而是借助工厂类的静态方法创建Bean对象的事例。
(3)利用实例工厂创建Bean对象
Bean对象:
class Bean1{
publicBean1(){
System.out.println(“Bean1的方法被调用”)
}
public void say(){
System.out.println(“bean1say good afternoon”);
}
}
工厂类:
class BeanFactory{
publicBeanFactory(){
System.out.println(“BeanFactory的构造方法被调用”)
}
publicstatic Bean1 getBean(){
System.out.println(“静态工厂方法被调用”)
returnnew Bean1();
}
publicBean1 getBeanByInstance(){
System.out.println(“实例方法创建Bean对象”);
}
}
测试代码:
public class Main1{
publicstatic void main(String []args){
ApplicationContextcontext = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean1 bean1= (Bean1) context.getBean("bean1");
bean1.Say();
}
}
spring配置文件
<bean name =”factory” class=” Bean.BeanFactory”></bean>
<bean name=”bean1” factory-bean=”factory”factory-method=”getBeanByIntance”>
程序运行结果:
图3实例子工厂的运行结果
三丶Spring依赖注入的方式
Spring总共提供了四种依赖注入的方式,分别是基于xml的setter方式,基于xml的构造函数方式,基于注解的方式以及自动装配方式,在这四种装配方式中,使用得最多的方式是基于xml的setter方式以及基于注解的注入方式,这里重点介绍前三种装配方式。
(1)基于xml的setter方式注入
Bean对象:
class Student{
Friend friend;
public Student(){
System.out.println(“student的构造方法被调用”);
}
public void say(){
System.out.println(“studentsay hello”);
}
public void setFriend(Friend friend){
System.out.println(“调用了set方法”);
this. friend = friend;
}
}
class Friend{
publicFriend(){
System.out.println(“friend的构造方法被调用”);
}
}
Spring配置文件:
<Bean name =”friend” class=”Bean.Friend”></Bean>
<Bean name =”stduent” class=”Bean.Student”>
<propertyname =”friend” ref =”friend”></property>
</Bean>
测试类:
public class Main{
public static void main(String [] args){
ApplicationContext context= newClassPathXmlApplicationContext("applicationContext.xml");
Student student= (Student) context.getBean("student");
student.say();
}
}
测试结果:
图4基于XML的set方法注入
观察上述的代码和执行结果,首先可以清晰看到这种方法的特点:被注入的属性必须设置set方法,在spring文件中对依赖属性需要配置property元素,其次可以发现Spring在注入的时候,先创建依赖对象,然后利用无参数构造方法创建Student对象,最后通过set方法将依赖对象friend注入到student对象中。
(2)基于xml的构造方法注入
//Bean对象:
class Student{
Friend friend;
public Student(Friend friend){
System.out.println(“student有参构造函数被调用”);
this.friend = friend;
}
public Student(){
System.out.println(“student的构造方法被调用”);
}
public void say(){
System.out.println(“studentsay hello”);
}
public void setFriend(Friend friend){
System.out.println(“调用了set方法”);
this. friend = friend;
}
}
class Friend{
publicFriend(){
System.out.println(“friend的构造方法被调用”);
}
}
Spring配置文件:
<Bean name =”friend” class=”Bean.Friend”></Bean>
<Bean name =”stduent” class=”Bean.Student”>
<constructor-arg index=”0” ref =”friend”/>
</Bean>
测试类:
public class Main{
public static void main(String [] args){
ApplicationContext context= newClassPathXmlApplicationContext("applicationContext.xml");
Student student= (Student) context.getBean("student");
student.say();
}
}
测试结果:
图 5基于xml的构造函数注入方式
基于构造函数的注入方式的实现原理也很明显,在创建Student对象的时候,利用有参构造函数创建对象,完成属性注入,这种方式就是必须提供带参数的构造函数,spring的配置文件也要修改。
(3)基于注解的注入方式
上述介绍了两种基于xml的注入方式,这两种注入方式都十分的简单,但是当需要配置Bean对象十分对,且依赖关系十分复杂的时候,基于xml方式的注入方式将会使spring的配置文件十分臃肿,不利于后期维护,所以spring提供了一种基于注解的注入方式。
1.利用注解配置userControl对象
@Controller("userControl")
public class UserControl {
@Resource(name="userService")
UserService userService;
publicUserControl(){
System.out.println("UserControl的构造方法被调用了");
}
publicvoid say(){
System.out.println("UserControl say hello");
}
}
Controller声明了这个类交给Spring管理,并且暗示他在javaEE三层架构中处于Control层,Controller注解中的”userControl”相当于Spring配置文件中Bean的name属性。
2.利用注解配置userService对象
@Service("userService")
public class UserService {
@Resource(name="userDao")
privateUserDao userDao;
publicUserService(){
System.out.println("UserService构造方法被调用了");
}
}
Service声明了这个类交给Spring管理,并且暗示他在javaEE三层架构中处于service层,Service注解中的”userService”相当于Spring配置文件中Bean的name属性。
3.利用注解配置userDao对象
@Repository("userDao")
public class UserDao {
publicUserDao(){
System.out.println("UserDao构造方法被调用");
}
}
Repository声明了这个类交给Spring管理,并且暗示他在javaEE三层架构中处于Dao层,Repository注解中的”userDao”相当于Spring配置文件中Bean的name属性。
4.测试代码
public classMain3 {
public staticvoid main(String [] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserControl userControl = (UserControl)context.getBean("userControl");
userControl.say();
}
}
5.spring配置文件
<!—开启注解-->
<context:annotation-config/>
<!—扫描annotaation包下的所有Bean对象-->
<context:component-scanbase-package="annotation" />
6.测试结果
图6 基于注解的注入结果
利用注解配置Bean对象,可以极大的简化xml书写的工作量,但是其表达依赖关系的时候可能不是太直观。