Spring 管理 Bean-IOC--基于注解配置 bean
● 基本介绍
基于注解的方式配置 bean, 主要是项目开发中的组件,比如 Controller、Service、和 Dao.
● 组件注解的形式有
1. @Component 表示当前注解标识的是一个组件
2. @Controller 表示当前注解标识的是一个控制器,通常用于 Servlet
3. @Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类
4. @Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类
代码演示--了解注解
创建UserDao UserService UserAction MyComponent类来方便了解
UserDao
@Repository
public class UserDao { }
UserService
@Service
public class UserService { }
UserAction
@Controller
public class UserAction { }
MyComponent
@Component
public class MyComponent { }
配置 beans.xml
1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
2. base-package 指定要扫描的包
3. 含义是当spring容器创建/初始化时,就会扫描com.spring.component包
下的所有的 有注解 @Controller / @Service / @Respository / @Component类将其实例化,生成对象,放入到ioc容器
4. resource-pattern="User*.class" 表示只扫描com.spring.component 和它的子包下User打头的类
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--配置容器要扫描的包 1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器 2. base-package 指定要扫描的包 3. 含义是当spring容器创建/初始化时,就会扫描com.wyxedu.spring.component包 下的所有的 有注解 @Controller / @Service / @Respository / @Component类 将其实例化,生成对象,放入到ioc容器 4. resource-pattern="User*.class" 表示只扫描com.wyxedu.spring.component 和它的子包下的User打头的类 --> <!--<context:component-scan base-package="com.spring.component"/>--> <!-- 需求:如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定 1. context:exclude-filter 指定要排除哪些类 2. type 指定排除方式 annotation表示按照注解来排除 3. expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径 --> <!--<context:component-scan base-package="com.spring.component">--> <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>--> <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>--> <!--</context:component-scan>--> <!-- 需求:如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter 1. use-default-filters="false" 表示不使用默认的过滤机制/扫描机制 2. context:include-filter 表示要去扫描哪些类 3. type="annotation" 按照注解方式来扫描/过滤 4. expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径 --> <context:component-scan base-package="com.spring.component" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
注意
如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定
1. context:exclude-filter 指定要排除哪些类
2. type 指定排除方式 annotation表示按照注解来排除
3. expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径
如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter
1. use-default-filters="false" 表示不使用默认的过滤机制/扫描机制
2. context:include-filter 表示要去扫描哪些类
3. type="annotation" 按照注解方式来扫描/过滤
4. expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径
测试
//通过注解来配置Bean @Test public void setBeanByAnnotation() { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml"); UserDao userDao = ioc.getBean(UserDao.class); //在默认情况下, 注解标识的类创建对象后,在容器中,id 为类名的首字母小写 UserDao userDao1 = ioc.getBean("userDao", UserDao.class); System.out.println("userDao1=" + userDao1); UserService userService = ioc.getBean(UserService.class); UserAction userAction = ioc.getBean(UserAction.class); MyComponent myComponent = ioc.getBean(MyComponent.class); System.out.println("userDao=" + userDao); System.out.println("userService=" + userService); System.out.println("userAction=" + userAction); System.out.println("myComponent=" + myComponent); System.out.println("ok"); }
注意事项和细节说明
1. 需要导入 spring-aop-5.3.8.jar , 别忘了
2. 必须在 Spring 配置文件中指定"自动扫描的包",IOC 容器才能够检测到当前项目中哪些类被标识了注解, 注意到导入 context 名称空间
可以使用通配符 * 来指定 ,比如 com.spring.* 表示
提问: com.spring.component 会不会去扫描它的子包?
答:会的
Spring 的 IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控制器。
注解的名称是用于程序员自己识别当前标识的是什么组件。
其它的@Service @Repository 也是一样的道理 [也就是说 spring 的 IOC 容器只要检查到注解就会生成对象,
但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的]
默认情况:标记注解后,类名首字母小写作为 id 的值。也可以使用注解的 value 属性 指定 id 值,
并且 value 可以省略。
@Controller(value="userAction01")
@Controller("userAction01")
自动装配
基本说明
1. 基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource
2. @AutoWired 的规则说明
1) 在 IOC 容器中查找待装配的组件的类型,如果有唯一的 bean 匹配,则使用该 bean 装配
2) 如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作为 id 值再进行查找, 找到就装配,找不到就抛异常
3. @Resource 的规则说明
1) @Resource 有两个属性是比较重要的,分是 name 和 type,Spring 将@Resource 注解的name 属性解析为 bean 的名字,
而 type 属性则解析为 bean 的类型.所以如果使用 name 属性,则使用 byName 的自动注入策略,
而使用 type 属性时则使用 byType 自动注入策略
2) 如果@Resource 没有指定 name 和 type ,
则先使用byName注入策略, 如果匹配不上,再使用 byType 策略, 如果都不成功,就会报错
4. 建议,不管是@Autowired 还是 @Resource 都保证属性名是规范的写法就可以注入.
应用实例需求
1. 以 Action/Service/Dao 几个组件来进行演示
2. 这里就演示 UserAction 和 UserService 的两级自动组装
UserService
@Service public class UserService { public void hi(){ System.out.println("UserService hi()~"); } }
UserAction
@Controller public class UserAction { //xml配置 ref //说明 @Autowired //1)在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配(按照类型),则使用该bean装配 //2)如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值再进行查找, // 找到就装配,找不到就抛异常 //说明 @Resource //1) @Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字, // 而type属性则解析为bean的类型.所以如果使用name属性,则使用byName的自动注入策略, // 而使用type属性时则使用byType自动注入策略 // 比如@Resource(name = "userService") 表示装配 id=userService对对象 // 比如@Resource(type = UserService.class) 表示按照UserService.class类型进行装配, 这时要求容器中,只能有一个这样类型的对象 //2) 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略, // 如果匹配不上, 再使用byType策略, 如果都不成功,就会报错 //================================= //说明: @Autowired + @Qualifier(value = "userService02") 组合也可以完成指定 name/id 来进行自动装配 //指定id进行组装, 也可以使用@Autowired 和 @Qualifier(value = "userService02") // 这时,是装配的 id=userService02 , 需要两个注解都需要写上 @Resource private UserService userService; public void sayOk() { System.out.println("UserAction 的sayOk()"); System.out.println("userAction 装配的 userService属性=" + userService); userService.hi(); } }
配置.xml
<context:component-scan base-package="com.spring.component"/> <!--配置两个UserService对象--> <bean class="com.wyxedu.spring.component.UserService" id="userService200"/> <bean class="com.wyxedu.spring.component.UserService" id="userService300"/>
测试
public void setProByAutowired() { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); UserService userService = ioc.getBean("userService", UserService.class); System.out.println("ioc容器中的userService=" + userService); UserService userService200 = ioc.getBean("userService200", UserService.class); System.out.println("ioc容器中的userService200=" + userService200); UserAction userAction = ioc.getBean("userAction", UserAction.class); //System.out.println("userAction=" + userAction); userAction.sayOk(); }
注意事项和细节说明
注意事项和说明
xml配置 ref
@Autowired
1)在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配(按照类型),则使用该bean装配
2)如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值再进行查找,
找到就装配,找不到就抛异常
@Resource
1) @Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,
而type属性则解析为bean的类型.所以如果使用name属性,则使用byName的自动注入策略,
而使用type属性时则使用byType自动注入策略
比如@Resource(name = "userService") 表示装配 id=userService对对象
比如@Resource(type = UserService.class) 表示按照UserService.class类型进行装配, 这时要求容器中,只能有一个这样类型的对象
2) 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略,如果匹配不上, 再使用byType策略, 如果都不成功,就会报错
说明:
@Autowired + @Qualifier(value = "userService02") 组合也可以完成指定 name/id 来进行自动装配指定id进行组装, 也可以使用@Autowired 和 @Qualifier(value = "userService02")
这时,是装配的 id=userService02 , 需要两个注解都需要写上
泛型依赖注入
● 基本说明
1. 为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的注入机制
2. 在继承关系复杂情况下,泛型依赖注入就会有很大的优越性
各个类关系图
传统方法是将 PhoneDao /BookDao 自动装配到 BookService/PhoneSerive 中,当这 种继承关系多时,就比较麻烦,可以使用 spring 提供的泛型依赖注入
创建Book类
public class Book {}
创建Phone类
public class Phone{}
创建BaseDao类
public abstract void save();
创建BookDao类
@Repository public class BookDao extends BaseDao<Book>{ @Override public void save() { System.out.println("BookDao 的 save().."); } }
创建PhoneDao类
@Repository public class PhoneDao extends BaseDao<Phone>{ @Override public void save() { System.out.println("PhoneDao save()"); } }
创建BaseService类
public class BaseService<T> { @Autowired private BaseDao<T> baseDao; public void save() { baseDao.save(); } }
创建BookService类
@Service public class BookService extends BaseService<Book>{ //并没有写属性 }
创建PhoneService类
@Service public class PhoneService extends BaseService<Phone>{ }
修改xml配置
<context:component-scan base-package="com.spring.depinjection"/>
测试
@Test public void setProByDependencyInjection() { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans07.xml"); PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class); phoneService.save(); System.out.println("ok"); }