Spring 6(二)【IOC(2)https://developer.aliyun.com/article/1532217
1.11、基于 XML 的自动装配
所谓自动装配其实就是为了减少我们配置 Spring 配置文件的工作量,比较一个 JavaBean 如果有很多属性的话,我们自己一个一个去配置添加属性太复杂了,所以就有了自动装配这个概念。
自动装配有两种方式:通过属性类型来自动装配(byType),通过属性名来自动装配(byName)。
- byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值
- 如果在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值 null
- 如果在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException
- byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值
这里我们写一个测试模拟我们开发的一个的场景,controller 负责响应通过 service 来实现,service 会调用 dao 层来实现持久化。
(1)编写 Service 层代码:
package com.lyh.study.service; public interface UserService { void addUser(); }
package com.lyh.study.service; import com.lyh.study.dao.UserDao; public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void addUser() { System.out.println("addUser() 方法执行"); userDao.addUser(); } }
(2)编写 Dao 层代码 :
package com.lyh.study.dao; public interface UserDao { void addUser(); }
package com.lyh.study.dao; public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("添加成功"); } }
(3)编写 controller 层代码:
package com.lyh.study.controller; import com.lyh.study.service.UserService; public class UserController { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public void addUser(){ userService.addUser(); } }
1.11.1、byType 自动装配
<bean id="userController" class="com.lyh.study.controller.UserController" autowire="byType"/> <bean id="userService" class="com.lyh.study.service.UserServiceImpl" autowire="byType"/> <bean id="userDao" class="com.lyh.study.dao.UserDaoImpl"/>
1.11.2、byName 自动装配
<bean id="userController" class="com.lyh.study.controller.UserController" autowire="byName"/> <bean id="userService" class="com.lyh.study.service.UserServiceImpl" autowire="byName"/> <bean id="userDao" class="com.lyh.study.dao.UserDaoImpl"/>
测试:
@Test public void testAutoWireByType(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); UserController userController = ac.getBean(UserController.class); userController.addUser(); }
运行结果:
addUser() 方法执行 添加成功
2、基于注解管理 Bean
除了上面的直接手动配置 Spring 配置文件以外,我们实际用的更多应该就是使用注解注入了。
2.1、开启扫描组件
Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。
也就是在 Spring 配置文件中加这么一行:
<context:component-scan base-package="com.lyh.study"/>
注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。
2.1.1、默认扫描方式
<context:component-scan base-package="com.lyh.study"/>
也就是我们上面演示的,它会扫描 com.lyh.study 包下所有被 @Component 注解标注的类并帮我们注册到 IOC 容器中管理。
2.1.2、指定要排除的组件
标签
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 --> <!-- use-default-filters属性:取值false表示关闭默认扫描规则 --> <context:component-scan base-package="com.lyh.study" use-default-filters="false"> <!-- type:设置包含的依据 type="annotation",根据注解排除,expression中设置要排除的注解的全类名 type="assignable",根据类型排除,expression中设置要排除的类型的全类名 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="assignable" expression="com.lyh.study.controller.UserController"/> </context:component-scan>
2.1.3、仅扫描指定组件
标签
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 --> <!-- use-default-filters属性:取值false表示关闭默认扫描规则 --> <context:component-scan base-package="com.lyh.study" use-default-filters="false"> <!-- type:设置排除的依据 type="annotation",根据注解排除,expression中设置要排除的注解的全类名 type="assignable",根据类型排除,expression中设置要排除的类型的全类名 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="assignable" expression="com.lyh.study.controller.UserController"/> </context:component-scan>
2.2、使用注解定义 Bean
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。
注解 | 说明 |
@Component | 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Service | 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
2.3、@Autowired 注入
单独使用 @Autowired注解时,默认根据类型装配(byType)。
2.3.1、属性注入
上面 2.1 中我们的 UserController 中有一个属性是 UserService 类型的对象,而 UserService 这个类当中也有一个类型为 UserDao 的属性;所以我们当时必须提供 setter 方法,因为它是是通过 setter 方法进行注入的;但是现在使用注解开发我们就可以省去 setter 方法:
package com.lyh.study.service; import com.lyh.study.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public void addUser() { System.out.println("addUser() 方法执行"); userDao.addUser(); } }
import org.springframework.stereotype.Repository; @Repository public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("添加成功"); } }
package com.lyh.study.controller; import com.lyh.study.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { @Autowired private UserService userService; public void addUser(){ userService.addUser(); } }
这次我们使用了注解 @Service、Repository、Controller 分别标注了我们的 UserController、UserDao 和 UserController ,而且即使它们的 Bean 中包含了一些属性,我们并没有提供 setter 方法,因为使用注解开发时,不需要给 Bean 提供 setter 方法。
测试:
@Test public void testAutoWireByType(){ ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); UserController userController = ac.getBean("userController",UserController.class); userController.addUser(); }
运行结果:
addUser() 方法执行 添加成功
2.3.2、set 注入
上面的属性注入中,我们把 @Autowired 这个注解标注在了属性上,这种方式不需要我们实现属性的 setter 方法;而 set 注入是直接把 @Autowired 这个注解标注在方法上:
package com.lyh.study.controller; import com.lyh.study.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } public void addUser(){ userService.addUser(); } }
package com.lyh.study.service; import com.lyh.study.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void addUser() { System.out.println("addUser() 方法执行"); userDao.addUser(); } }
这里不再测试运行结果了,效果和属性注入是一样的。个人推荐这种方式,尽量养成一个给属性添加 setter 方法的好习惯,而且属性注入 Idea 会给一个警告提示,虽然也用起来没问题,但是强迫症实在受不了。
Spring 6(二)【IOC(4)https://developer.aliyun.com/article/1532221