Spring IOC容器
2.3.5 基于注解的配置
使用注解定义Bean
Bean定义信息、Bean实现类以及Spring本身。
使用@Component注解在UserDao类声明处对类进行标注,它可以被 Spring 容器识别, Spring 容器自动将POJO转换为容器管理的Bean。
它和以下的 XML配置是等效的。
<bean id="userDao" class="com. smart.anno.UserDao"/>
使用注解配置信息启动Spring 容器
Spring 在2.5后提供了一个 context命名空间,它提供了通过扫描类包以应用注解定义Bean
的方式。
Spring 容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean 的定义信息。
如果仅希望扫描特定的类而非基类包下的所有类,那么可以使用 resource-pattern 属性过滤出特定的类,如下所示。
<context:component-scan base-package="com.smart" resource-pattern="anno/*.class"/>
这里将基类包设置为 com.smart,默认情况下 resource-pattern 属性的值为“**/*.class”,即基类包里的所有类。这里设置为“anno/*.class”,则 Spring 仅会扫描基类包里 anno 子包中的类。
这两个过滤元素均支持多种类型的过滤表达式,如表2-4所示。
自动装配Bean
Spring通过@Autowired 注解实现 Bean 的依赖注入,Autowired 默认按类型匹配的方式,在容器查找匹配的 Bean,当有且仅有一个匹配的Bean时, Spring 将其注入@Autowired标注的变量中。
(1)使用@Autowired 的required属性
如果容器中没有一个和标注变量类型匹配的Bean,Spring容器启动将抛出NoSuchBeanDefinitionException 异常。如果希望 Spring 即使找不到匹配的 Bean 完成注入也不要抛出异常, 那么就可以使用@Autowired(required=false)进行标注。
(2)使用@Qualifier指定注入 Bean的名称。
如果容器中有一个以上匹配的Bean,则可以通过@Qualifer注解限定Bean的名称
(3)对类方法进行标注
Autowired 可以对类成员变量及方法的入参进行标注,如代码清单2-19所示,在类的方法上使用@Autowired 注解。
一般情况下,Spring容器中的大部分Bean都是单例的,所以一般无须通过@Repository,Service等注解的value属性为Bean 指定名称,也无须使用@Qualifier按名称进行注入。
(4)对集合类进行标注
如果对类中集合类的变量或方法入参进行Autowired标注,那么 Spring 就会将容器中类型匹配的所有 Bean都自动注入进来。
Spring 如果发现变量是一个集合类, 则它会将容器中匹配集合元素类型的所有 Bean都注入进来。这里,Plugin 为一个接口, 它拥有两个实现类,分别是 OnePlugin 和 TwoPlugin,这两个实现类都通过@Component 标注为 Bean, 而 Spring 会将这两个 Bean 都注入 plugins 中。
2.3.6 基于Java类的配置
使用 Java 类提供 Bean 定义信息
JavaConfig是Spring的子项目,它旨在通过Java类的方法提供Bean得定义信息。
普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息每个标注了@Bean的类方法都相当于提供一个Bean的定义信息。
类的方法处可以标注@Bean 注解,Bean的类型由方法返回值类型决定,名称默认和方法名相同,也可通过入参显式指定Bean名称,如@Bean(name="userDao")。
基于Java类的配置方式和基于XML或基于注解的配置方式相比,前者通过代码的方式更加灵活地实现Bean的实例化及 Bean 之间的装配, 而后两者都是通过配置声明的方式, 在灵活性上要稍逊一些, 但是配置上要更简单一些。
直接通过@Configuration 类启动Spring 容器
Spring提供了一个AnnotationConfigApplicationContext类,它能够直接通过标注@Configuration 的 Java类启动Spring容器,如代码清单2-25所示。
此外,AnnotationConfigApplicationContext还支持通过编码的方式加载多个@Configuration配置类,然后通过刷新容器应用这些配置类。
用户可以通过代码逐个注册配置类,也可以通过@Import将多个配置类组装到一个配置类中,这样仅需要注册这个组装好的配置类就可以启动容器了。
通过 XML配置文件引用@Configuration 的配置
标注了Configuration 的配置类本身相当于一个标注了@Component 的类,也是一个Bean,它可以被Spring的<context:component-scan>扫描到。因此,如果希望将配置类组装到 XML配置文件中,通过XML配置文件启动Spring容器,仅需要在XML中通过<context:component-scan>扫描到相应的配置类就可以了,如代码清单2-28所示。
通过 Configuration 配置类引用 XML配置信息
假设在beans2.xml中定义了两个Bean
在@Configuration配置类中通过@ ImportResource 引入 XML 配置文件,在配置类中即可直接通过@Autowired引入XML配置文件中定义的Bean。
自动注入XML文件中定义的Bean
在2处完成了两项功能:其一,定义了LoginService的Bean;其二,通过方法入参自定注入UserDao和LogDao的Bean,这两个Bean是在XML定义的。
需要说明的是:在1处引入定义 UserDao 和 LogDao Bean 的 XML配置文件不是②处可成功自动注入UserDao和LogDaoBean 的前提条件。只要不同形式的 Bean 定义信息能够加载到Spring 容器中,Spring就能足够“智能” 地完成Bean 之间的装配。
2.3.7 不同配置方式比较
笔者一般采用XML配置DataSource、 SessionFactory 等资源 Bean,在 XML 中利用 aop、context 命名空间进行相关主题的配置。 其他所有项目中开发的Bean, 都通过基于注解配置的方式进行配置,即整个项目采用“基于 XML+基于注解”的配置方式, 很少采用基于 Java 类的配置方式。
第 3 章 Spring AOP
本章将从Spring AOP的底层实现技术入手,逐步深入Spring AOP的内核,分析它的底层结构和实现。
本章主要内容:
3.1 AOP概述
3.1.1 AOP到底是什么
AOP希望将这些分散在各个业务逻辑代码中的相同代码,通过横向切割的方式抽取到一个独立的模块中,还业务逻辑类一个清新的世界。
3.1.2 AOP 术语
连接点(JoinPoint)
程序执行的某个特定位置,如类开始初始化前、类初始化后、类某个方法调用前和调用后、方法抛出异常后。
一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
连接点由两个信息确定:基一是用方法表示的程序执行点;基二是用相对点表示的方位。
切点(Pointcut)
每个程序都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。但在这为数众多的连接点中,如何定位到某个感兴趣的连接点呢?AOP通过“切点”定位特定连接点。通过数据库查询的概念来理解切点和连接点的关系:连接点相当于数据库中的记录,而切点相当于查询条件。
切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。
在Spring中,切点通过org.springframework.aop.Pointeut 接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
增强(Advice)
增强是织入目标类连接点上的一段程序代码。在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点的方位信息和切点信息,就可以找到特定的连接点了。
正因为增强既包含了用于添加到目标连接点上的一段执行逻辑,又包含了用于定位连接点的方位信息,所以Spring所提供的增强接口都是带方位名的: BeforeAdvice 、 AfterRetuningAdvice、ThrowsAdvice 等。BeforeAdvice表示方法调用前的位置,AfterReturingAdvice 表示访问返回后的位置。所以只有结合切点和增强才能确定特定的连接点并实施增强逻辑。
目标对象(Target)
增强逻辑的织入目标类。性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入特定的连接点上。
引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过 AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
织入(Weaving)
织入是将增强添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、增强或者引介通过AOP这台织布机天衣无缝地编织到一起。AOP有3种织入的方式。
编译期织入,这要求使用特殊的Java编译器。
类装载期织入,这要求使用特殊的类装载器。
动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类,他是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可以是原类的子类,所以可以采用与调用原类相同的方式调用代理类。
切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入切面所指定的连接点中。