---------------------------------------------------依赖注入的含义------------------
在上一篇中提到过,spring的主要目的是简化开发和松散耦合。依赖注入技术就是为了松散耦合而出现的。
在旧的编程观念中,A类如果需要B类的实例,就需要通过new关键字新建一个B类的实例。这种编程方式需要依赖类确定被依赖类的具体实现类。
如果使用spring的依赖注入技术,则依赖类只需要声明自己需要某个类的实例,而不需要确定被依赖类的具体实现类,具体的实现类由spring框架通过配置文件来确定。
使用spring的依赖注入技术可以松散耦合。当需要更换被依赖类的具体实现类时,只需要创建一个新的实现类,然后把spring配置文件中之前配置的实现类改为新的实现类即可,而不需要对依赖类做改动。
由此也可以看出,spring的依赖注入(DI)实际上是把被依赖类的具体实现类的确定权交给了spring框架,而不再是由依赖类来确定。因此,依赖注入也被称为控制反转。
---------------------------------------------------依赖注入发生的时机------------------
那么依赖注入发生在什么时候呢?是发生在项目启动的过程中的。
当我们启动项目时,spring会进行容器的初始化,spring会扫描整个项目,如果发现spring的xml配置文件、spring的注入注解、基于java的配置注解,就会实例化相关的类,这些类包括controller、service、dao等。这个过程中包含依赖注入。实例化得到的Bean将由spring容器管理。
我以前一直以为当页面发生事件请求controller时,spring才去实例化对应的controller类。现在看来,我之前的想法是错误的。controller类早在项目启动的过程中,就已经实例化了。当事件请求controller时,spring容器会在容器中找到对应controller的一个实例,然后执行对应的方法。
---------------------------------------------------依赖注入的步骤和方式------------------
总的看来,依赖注入的实现实际上分为三个步骤。第一是解析spring配置文件中配置的bean和java代码中的注解的bean。第二是实例化配置的所有bean。第三是注入依赖的bean。
注入方式共有三种,包括注解扫描注入、java配置注入、XML注入。
---------------------------------------------------分界线------------------
第一,注解扫描注入。
在java代码中注解bean,会用到spring的三个注解,分别是@ComponetScan、@Component和@Autowired。
@ComponentScan表示被注解的类所在包及其子包会被列入扫描范围。如果想把这个配置类和应用代码分开,则可以在一个单独的包中建立这个配置类,在这个类上使用@ComponentScan(basePackages="packageName")或者@ComponentScan(basePackages={"packageName1", "packageName2"})来指定扫描范围。为了代码的友好性,也可以使用@ComponentScan(basePackageClasses={A.class, B.class}),其含义为将这些类所在包及其子包列入扫描范围。
--->>>如果是使用配置文件来开启组件扫描,可以使用:<context:componet-scan base-package=""/>来指定。有人可能要问,既然可以使用注解配置,为什么还需要配置文件呢?因为如果依赖的是第三方工具类,我们是无法在第三方工具类上加@Component注解的,所以只能通过配置文件添加依赖了,配置文件只需指定第三方工具类的路径即可。
@Component是用来注解类的,用@Component注解一个类,就告诉了spring要在容器中创建这个类的实例并管理它,此时创建的实例的id为小写首字母的类名。如果要指定特定的实例名称,可以使用@Component("beanName")。
其实,@Component是通用的注解,一般我们为了提高可读性,会使用@Controller、@Service、@Repository等具体的注解。在项目启动过程中,spring容器会进行初始化。初始化时如果发现@Controller注解,就会实例化这个controller类。其他注解也是如此。
@Autowired是用来注解属性、构造方法、set方法、或一般带参方法的,告诉spring为这个属性或方法注入一个符合要求的实例。
---------------------------------------------------分界线------------------
第二,java配置注入。
package com.zaoren.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.zaoren.controller.UserController; import com.zaoren.service.UserService; @Configuration public class BeanConfiguration { @Bean(name="uService") public UserService userService() { System.out.println("create userService"); return new UserService(); } @Bean public UserController userController(UserService uService) { System.out.println("create userController"); UserController uController = new UserController(uService); return uController; } } ---------------------------------------------------------------------- import org.springframework.web.bind.annotation.RequestMethod; import com.zaoren.service.UserService; @RequestMapping(value="user") public class UserController { private UserService uService; @RequestMapping(value="buy",method=RequestMethod.GET) public void buy() { System.out.println("--------->>>buy a book."); uService.searchBook(); } public UserController(UserService uService){ this.uService = uService; } }
spring初始化容器时会扫描项目文件,当发现类被@Configuration注解时,就知道这个类是用来装配实例的,然后才会扫描具体的方法并创建实例。如果没有@Configuration注解,则@Bean注解将不会起作用,即不会执行此文件中创建实例的方法。
@Bean注解告诉了spring框架,这个方法要返回一个bean,要求spring框架按照这个方法的逻辑创建实例,并纳入spring上下文中进行管理。
其实@Configuration有些类似于xml配置方式中的<beans>标签,@Bean类似于xml配置方式中的<bean>标签。
通过@Bean创建的实例的id默认是方法名(此处即aClass)。如果想要自定义实例id,可以这样指定id:@Bean(name=“myBean”)。