文章目录
- 【README】
- 【1】基于xml配置文件版本的自动绑定
- 【1.1】基于xml配置文件版本的自动绑定代码示例
- 【2】基于注解版本的自动绑定
- 【2.1】根据类型匹配的注解版自动绑定@Autowired
- 【2.2】根据名称匹配的注解版自动绑定@Autowired+@Qualifier
- 【2.2.1】 示例代码
- 【2.3】关于@Primary注解的补充
- 【2.3】关于context:annotation-config元素
- 【2.4】JSR250注解
- 【2.4.1】 @Resource注解标注依赖注入关系代码
- 【2.4.2】@PostConstruct 与 @PreDestroy注解
- 【2.5】 \<context:component-scan>开启spring组件扫描
- 【2.5.1】开启组件扫描的自动绑定代码示例
- 【3】小结
- 【补充】 JSR250
【README】
本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;
1)spring自动绑定对象间依赖关系: 指的是不用明确指定每一个bean与被依赖bean之间的依赖关系,太繁琐; 而是通过一种约定方式来指定依赖关系,如通过名称,bean类型,构造器等约定方式来指定; 通过名称或类型等约定方式指定bean之间的依赖关系称自动绑定 ;
2)spring容器自动绑定对象间依赖关系的方式有2种,如下:
- 方式1: 基于xml配置文件版本的自动绑定,自动绑定策略通过设置 beans元素的 default-autowire属性 或 bean元素的autowire属性来配置;
- 方式2: 基于java注解版本的自动配置;
【1】基于xml配置文件版本的自动绑定
1)基于xml配置文件版本的自动绑定,自动绑定策略通过设置 beans元素的 default-autowire属性 或 bean元素的autowire属性来配置 ;
2)default-autowire属性值或autowire属性值的可选项:
- no: 不开启自动绑定;(默认不开启)
- byname: 通过bean名称来自动绑定; 针对 bean property 的自动绑定,要有setter方法设置property属性;
- byType: 通过bean类型来自动绑定;针对 bean property 的自动绑定, 要有setter方法设置property属性;
- constructor: 通过构造器来自动绑定;针对构造方法参数的类型进行自动绑定;显然,constructor实际上是byType类型的自动绑定(但不需要setter方法);
- autodetect: 通过自动检测来自动绑定;是byType与constructor的结合体; 如果对象拥有默认无参构造器,则优先考虑byType的自动绑定模式(因为构造器没有参数,也就没法使用构造器绑定);如果对象有有参构造器,则使用constructor自动绑定; 如果通过构造器注入依赖关系后还有其他属性没有绑定,最后容器还是会使用byType对剩下的属性进行自动绑定;
3)自动绑定补充:
- 手工明确指定的绑定关系 优先级高于 自动绑定模式;
- 自动绑定可以应用到除开原生类型 ,string类型及 Classes类型(包括三者的数组)以外的对象类型 ;
【1.1】基于xml配置文件版本的自动绑定代码示例
1)应用场景: 对于DDD领域驱动设计,其有4层,包括表现层,应用层,领域层,基础设施层;
-
表现层: 本文用main方法表示;
-
应用层: 本文用 appService 表示;采用构造器constructor绑定; 底层使用setter注入;
-
领域层: 本文用 DomainService 表示;采用byType绑定; 底层使用setter注入;
-
基础设置层:本文用dao表示;采用byName绑定, 有setter方法;
-
防腐层support: 在业务模型与技术模型之间,即应用层或领域层与基础设施层之间(**作用:**技术模型底层做改造,仅防腐层改动代码,上游的应用层与领域层不修改代码);
【AutoWireXmlConfMain】xml配置文件自动绑定main
public class AutoWireXmlConfMain {
public static void main(String[] args) {
ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirebasedxml.xml");
container.getBean(RoomBookAppService.class).book("基于xml配置文件的自动绑定房间01", "张三");
}
}
【beans0601autowirebasedxml.xml】 beans元素的default-autowire属性设置为byName, bean元素的autowire属性设置为 constructor,byType, byName ;
<?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.xsd"
default-autowire="byName">
<bean id="roomBookAppService" class="com.tom.springnote.chapter05.chapter0504.RoomBookAppService" autowire="constructor" />
<bean id="roomBookDomainService" class="com.tom.springnote.chapter05.chapter0504.RoomBookDomainService" autowire="byType" />
<bean id="roomBookSupportImpl" class="com.tom.springnote.chapter05.chapter0504.RoomBookSupportImpl" autowire="byName" />
<bean id="roomBookDAO" class="com.tom.springnote.chapter05.chapter0504.RoomBookDAO" />
</beans>
【RoomBookAppService】应用层, 通过构造器绑定
public class RoomBookAppService {
private RoomBookDomainService roomBookDomainService;
public RoomBookAppService(RoomBookDomainService roomBookDomainService) {
this.roomBookDomainService = roomBookDomainService;
}
public void book(String roomId, String customer) {
roomBookDomainService.book(roomId, customer);
}
}
【RoomBookDomainService】领域层,通过byType绑定,必须要有setter方法;( 属性名称叫roomBookSupport,但xml中配置的bean名称为roomBookSupportImpl,通过byName无法绑定 )
public class RoomBookDomainService {
private IRoomBookSupport roomBookSupport;
public void book(String roomId, String customer) {
roomBookSupport.saveRoomBookInf(roomId, customer);
}
public void setRoomBookSupport(IRoomBookSupport roomBookSupport) {
this.roomBookSupport = roomBookSupport;
}
}
【IRoomBookSupport】防腐层接口
public interface IRoomBookSupport {
void saveRoomBookInf(String roomId, String customer);
}
【RoomBookSupportImpl】防腐层实现类 ; 通过byName绑定 ;必须要有setter方法 ;
public class RoomBookSupportImpl implements IRoomBookSupport {
private RoomBookDAO roomBookDAO;
@Override
public void saveRoomBookInf(String roomId, String customer) {
roomBookDAO.insertRoomBook(roomId, customer);
}
public void setRoomBookDAO(RoomBookDAO roomBookDAO) {
this.roomBookDAO = roomBookDAO;
}
}
【RoomBookDAO】基础设施层; 无需绑定对象依赖关系(因为没有依赖),仅注册bean到spring容器即可;
public class RoomBookDAO {
public void insertRoomBook(String roomId, String customer) {
System.out.printf("RoomBookDAO: 插入订房信息成功:roomId=[%s], customer=[%s]\n", roomId, customer);
}
}
【打印日志】
RoomBookDAO: 插入订房信息成功:roomId=[基于xml配置文件的自动绑定房间01], customer=[张三]
【2】基于注解版本的自动绑定
【2.1】根据类型匹配的注解版自动绑定@Autowired
1)@Autowired是基于注解版本的自动绑定的核心注解, spring容器根据@Autowired标注的位置为当前类注入依赖;
2)@Autowired注解的标注位置:
- 标注类属性property;优先使用 byName自动绑定,若绑定失败,则使用 byType 自动绑定;
- 标注构造方法定义;使用byType绑定;即根据构造器参数类型,来决定把什么样的依赖对象注入给当前对象;
- 标注普通方法定义; @Autowired不仅标注setter方法,还可以标注任意名称的方法,只要该方法定义了被注入的参数;
3)案例场景: 房间预订服务;
【AutoWireBasedAnnotationMain】基于注解的自动绑定main
public class AutoWireBasedAnnotationMain {
public static void main(String[] args) {
ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirebasedannotation.xml");
container.getBean(AnnotationBookAppService.class).book("基于注解的自动绑定房间01", "张三");
}
}
【beans0601autowirebasedannotation.xml】 若使用 @Autowired,必须注册 AutowiredAnnotationBeanPostProcessor
<?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.xsd">
<!-- 注册AutowiredAnnotationBeanPostProcessor, 用于检查是否有@Autowired标注的位置以便注入依赖 【若使用 @Autowired,必须注册该BeanPostProcessor】 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<!-- 注册bean,依赖关系由 @Autowired 标注,spring容器根据标注位置绑定依赖 -->
<bean id="roomBookAppService" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookAppService" />
<bean id="roomBookDomainService" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookDomainService" />
<bean id="roomBookSupportImpl" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookSupportImpl" />
<bean id="roomDAO" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomDAO" />
<bean id="hotelDAO" class="com.tom.springnote.chapter06.autowirebaseannotation.HotelDAO" />
</beans>
【RoomBookAppService】 应用层:@Autowired标注构造器 (byType的自动绑定 )
public class RoomBookAppService {
private RoomBookDomainService bookDomainService;
@Autowired
public RoomBookAppService(RoomBookDomainService bookDomainService) {
this.bookDomainService = bookDomainService;
}
public void book(String roomId, String customer) {
bookDomainService.book(roomId, customer);
}
}
【RoomBookDomainService】 领域层: @Autowired标注属性 (byType的自动绑定 )
public class RoomBookDomainService {
@Autowired
private IRoomBookSupport bookSupport;
public void book(String roomId, String customer) {
bookSupport.saveRoomBookInf(roomId, customer);
}
public void setBookSupport(IRoomBookSupport bookSupport) {
this.bookSupport = bookSupport;
}
}
【IRoomBookSupport】防腐层接口
public interface IRoomBookSupport {
void saveRoomBookInf(String roomId, String customer);
}
【RoomBookSupportImpl】防腐层:.@Autowired标注setter方法 ,标注普通方法 (byType的自动绑定 )
public class RoomBookSupportImpl implements IRoomBookSupport {
private RoomDAO bookDAO;
private HotelDAO hotelDAO;
@Override
public void saveRoomBookInf(String roomId, String customer) {
if (!hotelDAO.IfAuthLegal(customer)) {
System.out.println("权限校验失败");
return;
}
System.out.println("权限校验成功");
bookDAO.insertRoomBook(roomId, customer);
}
@Autowired
public void setBookDAO(RoomDAO bookDAO) {
this.bookDAO = bookDAO;
}
@Autowired
public void injectHotelDAO(HotelDAO hotelDAO) {
this.hotelDAO = hotelDAO;
}
}
【HotelDAO】
public class HotelDAO {
public boolean IfAuthLegal(String customer) {
System.out.printf("HotelDAO#IfAuthLegal(): [%s]权限校验合法", customer);
return true;
}
}
【RoomDAO】
public class RoomDAO {
public void insertRoomBook(String roomId, String customer) {
System.out.printf("RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[%s], customer=[%s]\n", roomId, customer);
}
}
【打印日志】
HotelDAO#IfAuthLegal(): [张三]权限校验合法权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[基于注解的自动绑定房间01], customer=[张三]
【2.2】根据名称匹配的注解版自动绑定@Autowired+@Qualifier
1)背景: 若一个接口有2个或多个实现类,如实例A与B ; 而@Autowired 注解是通过byType自动绑定,无法确认注入实例A还是实例B;
2)解决方法: 引入 @Qualifier注解, 解决@Autowired无法注入有多个类型匹配的bean的问题;
- @Qualifier注解:实际上是 byName自动绑定注解版,指定注入给定name的bean;
- @Qualifier注解 只能与 @Autowired结合使用,不能单独使用;
【2.2.1】 示例代码
业务场景: 酒店预订助手接口有2个实现类, 包括 万达酒店预定助手实现类, 希尔顿酒店预定助手实现类; 通过 @Qualifier注解指定bean名称并注入name匹配的bean;
【AutowireQualifierAnnotationMain】基于@Autowired与@Qualifier注解自动绑定main
public class AutowireQualifierAnnotationMain {
public static void main(String[] args) {
ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirequlifierannotation.xml");
container.getBean(AutowireQualifierBookAppService.class).book("@Autowired与@Qualifier注解的自动绑定01", "张三");
}
}
【beans0601autowirequlifierannotation.xml】 新增 <context:annotation-config/> 元素, 以激活 @Autowired 和 @Qualifier 注解
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!-- 注册 AutowiredAnnotationBeanPostProcessor, 用于检查是否有@Autowired标注的位置以便注入依赖
AutowiredAnnotationBeanPostProcessor 激活 @Autowired 注解 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<!-- 注册bean -->
<bean id="bookAppService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookAppService" />
<bean id="bookDomainService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
<bean id="bookDomainService2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
<bean id="hiltonRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HiltonRoomBookSupportImpl" />
<bean id="wandaRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.WandaRoomBookSupportImpl" />
<bean id="roomDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
<bean id="roomDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
<bean id="hotelDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
<bean id="hotelDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
</beans>
【AutowireQualifierBookAppService】 ( 构造器新增 @Quailfier注解 )
public class AutowireQualifierBookAppService {
private AutowireQualifierBookDomainService annotationBookDomainService;
@Autowired
public AutowireQualifierBookAppService(@Qualifier("bookDomainService2") AutowireQualifierBookDomainService bookDomainService) {
this.annotationBookDomainService = bookDomainService;
}
public void book(String roomId, String customer) {
annotationBookDomainService.book(roomId, customer);
}
}
【AutowireQualifierBookDomainService】( 属性上新增 @Quailfier注解 )
public class AutowireQualifierBookDomainService {
@Autowired
@Qualifier("hiltonRoomBookSupportImpl")
private IAutowireQualifierBookSupport bookSupport;
public void book(String roomId, String customer) {
bookSupport.saveRoomBookInf(roomId, customer);
}
}
【IAutowireQualifierBookSupport】
public interface IAutowireQualifierBookSupport {
void saveRoomBookInf(String roomId, String customer);
}
【HiltonRoomBookSupportImpl】 ( setter方法与普通方法上新增 @Quailfier注解 )
public class HiltonRoomBookSupportImpl implements IAutowireQualifierBookSupport {
private RoomDAO bookDAO;
private HotelDAO hotelDAO;
@Override
public void saveRoomBookInf(String roomId, String customer) {
System.out.println("希尔顿酒店预订助手");
if (!hotelDAO.IfAuthLegal(customer)) {
System.out.println("权限校验失败");
return;
}
System.out.println("权限校验成功");
bookDAO.insertRoomBook(roomId, customer);
}
@Autowired
public void setBookDAO(@Qualifier("roomDAO") RoomDAO bookDAO) {
this.bookDAO = bookDAO;
}
@Autowired
public void injectHotelDAO(@Qualifier("hotelDAO") HotelDAO hotelDAO) {
this.hotelDAO = hotelDAO;
}
}
【WandaRoomBookSupportImpl】 ( setter方法与普通方法上新增 @Quailfier注解 )
public class WandaRoomBookSupportImpl implements IAutowireQualifierBookSupport {
private RoomDAO bookDAO;
private HotelDAO hotelDAO;
@Override
public void saveRoomBookInf(String roomId, String customer) {
System.out.println("万达酒店预订助手");
if (!hotelDAO.IfAuthLegal(customer)) {
System.out.println("权限校验失败");
return;
}
System.out.println("权限校验成功");
bookDAO.insertRoomBook(roomId, customer);
}
@Autowired
public void setBookDAO(@Qualifier("roomDAO2") RoomDAO bookDAO) {
this.bookDAO = bookDAO;
}
@Autowired
public void injectHotelDAO(@Qualifier("hotelDAO2") HotelDAO hotelDAO) {
this.hotelDAO = hotelDAO;
}
}
【打印日志】
希尔顿酒店预订助手
HotelDAO#IfAuthLegal(): [张三]权限校验合法
权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[基于注解的自动绑定房间01], customer=[张三]
【2.3】关于@Primary注解的补充
@Primary注解: 标注的类的bean实例化后, 若该类下有多个实例化的bean,则优先使用 被Primary标注的那个bean;
【PdfFileReader】FileReader子类1
package basic.ioc.wiring.finetune;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Primary
@Component
public class PdfFileReader implements FileReader {
@Override
public void print() {
System.out.println("Inside PdfFileReader");
}
}
【WordFileReader】FileReader子类2
package basic.ioc.wiring.finetune;
import org.springframework.stereotype.Component;
@Component
public class WordFileReader implements FileReader {
@Override
public void print() {
System.out.println("Inside WordFileReader");
}
}
【FineTuneAutowiring】Primary注解测试案例入口
package basic.ioc.wiring.finetune;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(ConfigWiring.class)
public class FineTuneAutowiring {
@Autowired
private FileReader fileReader;
@Test
public void testFileReader() {
Assert.assertNotNull(fileReader);
Assert.assertEquals(fileReader.getClass(), PdfFileReader.class);
}
}
因为 PdfFileReader 被@Primary注解标注, 所以业务bean#FineTuneAutowiring通过 @Autowired自动绑定 FileReader实例时,优先选择PdfFileReader示例进行绑定;
【2.3】关于context:annotation-config元素
1)<context:annotation-config /> 可以激活多个注解以便在class中被探测到: 激活的注解清单如下:
- @Required, @Autowired
- JSR 250定义的注解 @PostConstruct, @PreDestroy and @Resource ;
- JAX-WS’s @WebServiceRef
- EJB 3’s @EJB
- JPA’s @PersistenceContext and @PersistenceUnit
或者你也可以选择注入这些注解对应的BeanPostProcessor ;
2)context:annotation-config 注册的BeanPostProcessor清单如下: 参见: https://docs.spring.io/spring-framework/reference/core/beans/annotation-config.html
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- EventListenerMethodProcessor
3) 我们可以这样理解:因为context:annotation-config 注册了多个BeanPostProcessor到spring容器,这些BeanPostProcessor可以扫描注解标注的位置,以此自动绑定相应依赖注入;即 注册BeanPostProcessor,间接激活了自动绑定的注解 ;
4)所以 beans0601autowirequlifierannotation.xml中可以删除 AutowiredAnnotationBeanPostProcessor 这个BeanPostProcessor的注入配置;因为 context:annotation-config 激活了对应的BeanPostProcessor;
删除AutowiredAnnotationBeanPostProcessor注入配置后xml如下 :
【beans0601autowirequlifierannotation.xml】
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!-- 注册bean -->
<bean id="bookAppService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookAppService" />
<bean id="bookDomainService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
<bean id="bookDomainService2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
<bean id="hiltonRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HiltonRoomBookSupportImpl" />
<bean id="wandaRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.WandaRoomBookSupportImpl" />
<bean id="roomDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
<bean id="roomDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
<bean id="hotelDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
<bean id="hotelDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
</beans>
【补充】 如果xml文件中只注入 AutowiredAnnotationBeanPostProcessor , 而不引入 <context:annotation-config />元素, 则无法激活 @Qualifier注解。 这会导致spring自动绑定依赖关系时,若发现同一个类有多个bean,则抛出异常 【No qualifying bean of type ‘XXX’ available: expected single matching bean but found 2: XXX1, XXX2】。
【2.4】JSR250注解
1) jsr250,即第250号java规范请求,其提供的注解包括 @Resource, @PostConstruct , @PreDestory ;
- @Resource: 根据byName自动绑定依赖注入关系; 可以标注属性和方法,但不能标注构造器;
- @PostConstruct: bean实例化后被调用(构造器执行后被调用)
- @PreDestory: bean销毁前被调用
【2.4.1】 @Resource注解标注依赖注入关系代码
【Jsr250AnnotationAutowireMain】JSR250注解自动装配main入口
public class Jsr250AnnotationAutowireMain {
public static void main(String[] args) {
String[] locations = {"classpath:chapter06/beans0601autowirequlifierannotation.xml"
, "classpath:chapter06/beans0601jsr250annotation.xml"};
ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext(locations);
// 若有bean销毁前执行回调,则需要注册关闭钩子回调
container.registerShutdownHook();
container.getBean(Jsr250AnnotationBookAppService.class).book("JSR250#@Resource注解的自动绑定01", "张三");
System.out.println("容器关闭");
}
}
【Jsr250AnnotationBookAppService】JSR250注解@Resource标注bean以便自动绑定依赖关系
public class Jsr250AnnotationBookAppService {
@Resource(name = "bookDomainService2")
private AutowireQualifierBookDomainService annotationBookDomainService;
public Jsr250AnnotationBookAppService() {
System.out.println("Jsr250AnnotationBookAppService 构造器");
}
public void book(String roomId, String customer) {
annotationBookDomainService.book(roomId, customer);
}
@PostConstruct
public void afterInstance() {
System.out.println("@PostConstruct注解标注的方法,实例化之后执行");
}
@PreDestroy
public void destory() {
System.out.println("@PreDestroy注解标注的方法,bean销毁前执行");
}
}
【打印日志】
Jsr250AnnotationBookAppService 构造器
@PostConstruct注解标注的方法,实例化之后执行
希尔顿酒店预订助手
HotelDAO#IfAuthLegal(): [张三]权限校验合法
权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[JSR250#@Resource注解的自动绑定01], customer=[张三]
容器关闭
@PreDestroy注解标注的方法,bean销毁前执行
【@Resource注解作用总结】
@Resource注解可以替代 @Autowired 与 @Qualilfier这2个注解结合的功能,即根据beanName到spring容器中查找匹配的bean,并注入依赖关系到当前bean(如Jsr250AnnotationBookAppService);
【2.4.2】@PostConstruct 与 @PreDestroy注解
1) @PostConstruct: bean实例化后被调用(构造器执行后被调用);
- 总结:bean实例化后的生命周期管理方式:
- @PostConstruct注解;
- 实现 InitializingBean接口;
- 通过 init-method 指定初始化方法;
2)@PreDestroy: bean销毁前被调用;
- 总结:bean销毁前执行回调的生命周期管理方式:
- @PreDestroy注解;
- 实现 DisposableBean 接口;
- 通过 destory-method 指定销毁前的回调方法;
补充: 代码示例参见 Jsr250AnnotationBookAppService ;
【2.5】 <context:component-scan>开启spring组件扫描
1)背景: 2.2 与 2.3的代码示例中, xml配置文件仅注册bean,不管理bean的依赖关系; 其依赖关系通过注解来管理; 即需要编写xml配置与注解;当bean比较多的时候,开发xml配置文件比较繁琐;
2)解决方法: 使用 classpath-scanning 扫描给定路径的package;
- 当扫描到某个类标注对应注解后,就提取该类的相关信息,构建对应 BeanDefinition,并把BeanDefinition注册到容器。
- 接着容器遍历BeanDefinition实例化bean,并通过标注注解的对应BeanPostProcessor来注入依赖关系;
3)classpath-scanning组件扫描功能,通过在xml文件配置 <context:component-scan> 来开启;
- <context:component-scan> 默认扫描 @Component注解标注的类,并实例化bean;
- 此外, 因为 @Controller注解, @Service注解, @Repository注解 是基于@Component定义的,所以上述3个也可以被 <context:component-scan> 扫描到 ;
4)补充: <context:component-scan> 元素,可以新增属性 include-filter 和 exclude-filter 分别包含和排除某些package ;
【2.5.1】开启组件扫描的自动绑定代码示例
【ComponentScanMain】main入口
public class ComponentScanMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601componentscan.xml");
container.getBean(ComponentScanAppService.class).scan();
}
}
【beans0601componentscan.xml】 配置了 <context:annotation-config/> 元素, <context:component-scan> 元素 ;
其中,当需要扫描多个包时,用逗号分割;
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注册5个BeanPostProcessor,间接激活多个自动绑定注解及JSR250注解,包括 @Autowired, @Qualiifer, @Resource, @PostConstruct, @PreDestory -->
<context:annotation-config/>
<!-- 开启组件扫描,无需手工在xml配置文件注册每个bean -->
<context:component-scan base-package="com.tom.springnote.chapter06.componentscan,com.tom.springnote.chapter06.componentscan2" />
</beans>
【ComponentScanAppService】 ( 使用 @Service注解 )
@Service
public class ComponentScanAppService {
@Autowired
private ComponentScanDomainService scanComponentDomainService;
public void scan() {
scanComponentDomainService.scan();
}
}
【ComponentScanDomainService】( 使用 @Service注解 )
@Service
public class ComponentScanDomainService {
@Autowired
@Qualifier("hiltonComponenetScanSupportImpl")
private IComponentScanSupport componentScanSupport;
public void scan() {
componentScanSupport.scan();
}
}
【IComponentScanSupport】
public interface IComponentScanSupport {
void scan();
}
【HiltonComponenetScanSupportImpl】 ( 使用 @Component注解 )
@Component("hiltonComponenetScanSupportImpl")
public class HiltonComponenetScanSupportImpl implements IComponentScanSupport {
@Autowired
private ComponentScanDAO scanComponentDAO;
@Override
public void scan() {
System.out.println("希尔顿组件扫描助手");
scanComponentDAO.scan();
}
}
【WandaComponenetScanSupportImpl】 ( 使用 @Component注解 )
@Component("wandaComponenetScanSupportImpl")
public class WandaComponenetScanSupportImpl implements IComponentScanSupport {
@Autowired
private ComponentScanDAO scanComponentDAO;
@Override
public void scan() {
System.out.println("万达组件扫描助手");
scanComponentDAO.scan();
}
}
【ComponentScanDAO】 ( 使用 @Repository注解 )
@Repository
public class ComponentScanDAO {
public void scan() {
System.out.println("ScanComponentDAO#scan():扫描完成");
}
}
【打印日志】
希尔顿组件扫描助手
ScanComponentDAO#scan():扫描完成
【3】小结
1)本文描述了2种自动绑定依赖关系的方式, 包括xml配置文件版本的自动绑定,注解版本的自动绑定;
- 依赖注入自动化: 在介绍注解版本的自动绑定时,本文引入了 <context:annotation-config> xml元素 ,它注册5个BeanPostProcessor,间接激活多个自动绑定注解及JSR250注解,包括 @Autowired, @Qualiifer, @Resource, @PostConstruct, @PreDestory ; 有了这些组件,我们可以实现自动绑定依赖关系,而无需手工在xml文件中配置每个bean的依赖关系, 开发量显著减少;
- 对象bean注册自动化:本文还引入了 <context:component-scan> xml元素, 开启组件扫描,无需手工在xml配置文件注册每个bean ;
2)问题:能否仅使用注解实现自动绑定; 答案是否;
- 因为无法通过注解来标注第三方提供的类库;即针对第三方类库, 我们可以结合使用基于配置文件的依赖注入方式;因为基于xml的依赖注入方式是spring提供的最基本,最强大的表达方式 ;
【补充】 JSR250
1)JSR,Java Specification Request, 即 java规范请求: 是 Java 平台提议和最终规范的实际描述。任何时候都有大量 JSR 正在通过审查和批准流程。 JSR250表示第250号java规范请求; 参见 https://jcp.org/en/jsr/overview ;JSR清单,参见:https://jcp.org/en/jsr/all
简单理解, 开发者自行开发了api,把api封装到JSR并报送给java社区;审批通过后,以JSR形式发布api;