搜档网
当前位置:搜档网 › Spring中注解配置与xml配置分析

Spring中注解配置与xml配置分析

从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,开发人员对注解(Annotation)的态度也是萝卜青菜各有所爱,个人认为注解可以大大简化配置,提高开发速度,同时也不能完全取代XML配置方式,XML方式更加灵活,并且发展的相对成熟,这种配置方式为大多数 Spring 开发者熟悉;注解方式使用起来非常简洁,但是尚处于发展阶段,XML配置文件和注解(Annotation)可以相互配合使用。

注解其实也没什么神秘的,和XML配置文件类似都是一种配置的方式而已,只不过利用JDK的反射机制,在编译时或者运行时动态获取所配置的信息而已,注解本身只是个标识,注解的真正意义在于通过注解标识获取注解所在对象的信息以及注解中配置的信息。

Spring的注解方式只是简化了XML配置文件,可以在读入Bean定义资源时可以动态扫描给定的路径,在解析和依赖注入时,XML方式配置的Bean,Spring需要解析XML文件,注解方式配置的Bean,Spring需要通过JDK的反射机制获取注解配置的信息。

虽然 2.0 版本发布以来,Spring 陆续提供了十多个注解,但是提供的这些注解只是为了在某些情况下简化 XML 的配置,并非要取代 XML 配置方式。这一点可以从 Spring IoC 容器的初始化类可以看出:ApplicationContext 接口的最常用的实现类是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext,它们都是面向 XML 的。Spring 3.0 新增了另外两个实现类:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。从名字便可以看出,它们是为注解而生,直接依赖于注解作为容器配置信息来源的 IoC 容器初始化类。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法与后者相比几乎没有什么差别

使用注解方式时,必须在spring配置文件的schema中添加注解的命名空间如下:

xmlns:context="https://www.sodocs.net/doc/259100862.html,/schema/context
https://www.sodocs.net/doc/259100862.html,/schema/context/spring-context-2.5.xsd

是否有了这些 IOC 注释,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:
1) 注释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注释配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注释配置,因为注释是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。
2) 如果 Bean 不是自己编写的类(如 JdbcTemplate、S

essionFactoryBean 等),注释配置将无法实施,此时 XML 配置是唯一可用的方式。
3) 注释配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释,使用 aop/tx 命名空间的事务配置更加灵活和简单。

所以在实现应用中,我们往往需要同时使用注释配置和 XML 配置,对于类级别且不会发生变动的配置可以优先考虑注释配置;而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。

定义配置文件中使用的参数定义文件:

在后面的配置中就可以使用类似: 这样的变量来引用属性参数了

=====================================================================
Spring IoC容器对于类级别的注解和类内部的注解分以下两种处理策略:

(1).类级别的注解:如@Component、@Repository、@Controller、@Service以及WebSerivce和JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解,Spring容器根据注解的过滤规则扫描读取注解Bean定义类,并将其注册到Spring IoC容器中。

(2).类内部的注解:如@Autowire、@Value、@Required、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解,SpringIoC容器通过Bean后置注解处理器解析Bean内部的注解。是容器对Bean对象实例化和依赖注入时,通过容器中注册的Bean后置处理器处理这些注解的。

Spring中管理注解Bean定义的容器有两个:AnnotationConfigApplicationContext和 AnnotationConfigWebApplicationContext。这两个类是专门处理Spring注解方式配置的容器,直接依赖于注解作为容器配置信息来源的IoC容器。AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的Web版,它们对于注解Bean的注册和扫描是基本相同的,但是AnnotationConfigWebApplicationContext对注解Bean定义的载入稍有不同。

Spring对注解的处理分为两种方式:

(1).直接将注解Bean注册到容器中:可以在初始化容器时注册;也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理。
(2).通过扫描指定的包及其子包下的所有类:在初始化注解容器时指定要自动扫描的路径。
如果容器创建以后向给定路径动态添加了注解Bean,则需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的Bean进行处理。

======================================================================
配置启用bean注解处理器

Annotati

onConfigApplicationContext通过调用类路径Bean定义扫描器ClassPathBeanDefinitionScanner扫描给定包及其子包下的所有类

当使用Spring的注解功能时,在Spring配置文件中添加如下前两行配置可以开启Spring的注解处理器:


以及四个 Bean 后处理器。这种方式的缺点是使得没有机会再给 HandlerMapping 注入interceptors,就无法指定拦截器

该配置隐式地向Spring容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor等四个专门用于处理注解的Bean后置处理器。注解后置处理器是Spring容器专门处理配置了自动依赖注入装配相关注解的Bean

也可以单独显式地来启用某个注解处理器,而且可以给处理器添加拦截器:













也可以使用下面的方式来让系统自动扫描类里面的注解

注解特定的类
通过正则表达式定义过滤的类
通过 AspectJ 表达式定义过滤的类

如上配置,我们就不再需要在 XML 中显式地使用 对该 Bean 进行配置了。Spring 在容器初始化时将自动扫描 base-package 指定的包及其子包下的所有 class 文件,所有标注了 @Repository、@Service、@Controller 和 @Component 的类都将被注册为 Spring Bean。


如果使用了SpringMVC,则下面的语句隐式的向Spring容器注册 DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter,用来处理 @RequestMapping, @InitBinder, @NumberFormat, @DateTimeFormat, @RequestBody, @ResponseBody 等注解


当然了也可以使用如下的方式显式地加载:





当一个 Bean 被自动检测

到时,会根据那个扫描器的 BeanNameGenerator 策略生成它的 bean 名称。默认情况下,对于包含 name 属性的 @Component、@Repository、 @Service 和 @Controller,会把 name 取值作为 Bean 的名字。如果这个注解不包含 name 值或是其他被自定义过滤器发现的组件,默认 Bean 名称会是小写开头的非限定类名。

四个注解处理器的作用:
(1)AutowiredAnnotationBeanPostProcessor 提供对 Spring 特有的 @Autowired 和 @Qualifier 注释的解析工作
(2)CommonAnnotationBeanPostProcessor 是Spring中用于处理JavaEE5中常用注解(主要是EJB相关的注解)和Java6中关于JAX-WS相关的注解,可以处理@PostConstruct、@PreDestroy等Bean生命周期相关事件的注解,该后置处理最核心的是处理@Resource注解,同时还可以处理JAX-WS相关的注解
(3)RequiredAnnotationBeanPostProcessor 是Spring中用于处理@Required注解的,@Required注解强制要求Bean属性必须被配置,当Spring容器对Bean的属性进行依赖注入时,配置了@Required注解的属性,Spring容器会检查依赖关系是否设置
(4)PersistenceAnnotationBeanPostProcessor 是Spring中用于处理JPA相关注解的Bean后置处理器,主要解析和处理@PersistenceUnit、@PersistenceContext注解,其主要作用是为JPA的实体管理器工厂(EntityManagerFactory)和实体管理器(EntityManager)注入相应的持久化单元(PersistenceUnit)或持久化上下文(PersistenceContext)。

Spring容器在对WebSerice进行注入时,首先通过JNDI查找容器中的实例对象,如果没有找到,则根据wsdl文件实例化WebService对象,如果没有指定wsdl文件的路径,则根据类型利用JDK的反射机制生成WebService实例对象,完成注入。

======================================================================
Spring中常用的四个类级别注解:

(1) Component注解原型定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component { String value() default ""; } //@Component 有一个可选的入参,用于指定 Bean 的名称

(2) Service注解原型定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service { String value() default ""; }

(3) Controller注解原型定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller { String value() default ""; }
不用@Controllers注解的处理器则需要实现Controller接口:
public class MyController implements Controller 并且要在xml配置文件中指明controller和urlmapping的映射关系:

(4) Repository注解原型定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)

@Documented
@Component
public @interface Repository { String value() default ""; }

通过分析Spring这4个常用的注解定义,我们看到:@Service、@Controller和@Repository注解都添加了一个@Component注解,因此他们都属于@Component

使用 @Repository、@Service、@Controller 和 @Component 将类标识为 Bean:
(1) @Repository 注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO 类上即可。示例:
@Repository
public class UserDao {}
为什么 @Repository 只能标注在 DAO 类上呢?这是因为该注解的作用不只是将类识别为 Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。
Spring 本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。
(2) @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
@Repository 通常作用在持久化层,例如setDao()上
@Service 通常作用在业务层,但是目前该功能与 @Component 相同。
@Constroller 通常作用在控制层,但是目前该功能与 @Component 相同。
这三个注解除了作用于不同软件层次的类,其使用方式与 @Component 是完全相同的。
(3) 默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标
@Scope("prototype") 当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了
@Scope("session") 每个不同的session使用不同的实例

========================================================================
Spring中常用的几个类内部注解:

(1) 使用 @Required 进行 Bean 的依赖检查:
Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。依赖检查的作用是,判断给定 Bean 的相应 Setter 方法是否都在实例化的时候被调用了。而不是判断字段是否已经存在值了。Spring 进行依赖检查时,只会判断属性是否使用了 Setter 注入。如果某个属性没有使用 Setter 注入,即使是通过构造函数已经为该属性注入了值,Spring 仍然认为它没有执行注入,从而抛出异常。另外,Spring 只管是否通过 Setter 执行了注入,而对注入的值却没有任何要求,即使注入的 ,Spring 也认为是执行了依赖注入。当某个被标注了 @Required 的 Setter 方法没有被调用,则 Spring 在解析的时候会抛出异常

,以提醒开发者对相应属性进行设置

(2) 使用 @Autowired 和 @Qualifier 指定 Bean 的自动装配:
Spring 可以通过bean类的自省自动绑定依赖性,所以不必显式(在xml文件中进行明确配置)指明bean的属性和构造函数。Bean属性可以通过属性名称或类型匹配来实现自动绑定。构造函数通过类型匹配来实现自动绑定。甚至可以指定自动检测 autowiring 模式,根据指定的自动装配规则,将某个 Bean 所需要引用类型的 Bean 注入进来。@Autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。

要使@Autowired能够工作,还需要在配置文件中启用如下PostProcessor:

在XML配置文件中使用的 元素中,提供了一个指定自动装配类型的 autowire 属性,该属性有如下选项:
no -- 显式指定(使用 )不使用自动装配。
byName -- 如果存在一个和当前属性名字一致的 Bean,则使用该 Bean 进行注入。如果名称匹配但是类型不匹配,则抛出异常。如果没有匹配的类型,则什么也不做。
byType -- 如果存在一个和当前属性类型一致的 Bean(相同类型或者子类型),则使用该 Bean 进行注入。byType 能够识别工厂方法,即能够识别 factory-method 的返回类型。
如果存在多个类型一致的 Bean,则抛出异常。如果没有匹配的类型,则什么也不做。
constructor -- 与 byType 类似,只不过它是针对构造函数注入而言的。如果当前没有与构造函数的参数类型匹配的 Bean,则抛出异常。使用该种装配模式时,优先匹配参数最多的构造函数。
autodetect -- 根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配。如果 Bean 提供了默认的构造函数,则采用 byType;否则采用 constructor 进行自动装配。

使用 @Autowired 注解进行装配,只能是根据类型进行匹配。@Autowired 注解可以用于 Setter 方法、构造函数、字段,甚至普通方法,前提是方法必须有至少一个参数。
@Autowired 标注作用于数组和使用泛型的集合类型。然后 Spring 会将容器中所有类型符合的 Bean 注入进来。
@Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。
@Autowired 标注作用于普通方法时,会产生一个副作用,就是在容器初始化该 Bean 实例的时候就会调用该方法。当然,前提是执行了自动装配,对于不满足装配条件的情况,该方法也不会被执行。
@Autowired 默认是按

照对象的数据类型进行自动装配的。

当标注了 @Autowired 后,在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常。我们可以给 @Autowired 标注增加一个 required=false 属性,等于告诉 Spring:在找不到匹配 Bean 时也不报错。
和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。

@Autowired 还有一个作用就是,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。

必须至少拥有一个匹配的 Bean。

如果将其注解到实例变量上:
@Autowire
private UserDao userDao;
Spring 将直接采用 Java 反射机制对该私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后,您大可将它们的 setter 方法(setUserDao(UserDao userDao))从该 bean 中删除。

如果将其注解到方法上:
@Autowired
public void setUserDao(UserDao userDao) { https://www.sodocs.net/doc/259100862.html,erDao = userDao; }
这时,@Autowired 将从上下文中查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean。

如果将其注解到构造方法上:
@Autowired
public LoginBean(UserDao userDao){ }
@Autowired 将分别寻找和它们类型匹配的 Bean,将它们作为 LoginBean 的入参来创建实例 Bean。
如果用 @Autowired 同时标注了多个构造函数,那么 Spring 将采用贪心算法匹配构造函数(构造函数最长)。

!!!!!!!!!!!!!!
自动绑定可能会节省一些键入工作量并减少混乱。但是在现实项目中不应该使用这种方式,因为它牺牲了配置的可读性和可维护性。
许多指南和介绍中大肆吹捧自动绑定是Spring的一项极好的特性,而没有提到这一特性所带来的牺牲。依我来看,这就像Spring中的对象池(object-pooling),更大程度上只是宣传的噱头。
对于精简XML配置文件来说,它是一个好办法,但它实际上增加了复杂性,尤其是在运行包含大量类声明的项目时。虽然Spring允许混合使用自动绑定和显式绑定,但这会使XML配置更加晦涩难懂。

(3) 使用 @Qualifier 指定 bean 或者 属性 的别名
当容器配置中存在多个 Bean 的类型与需要注入的相同时,注入将不能执行(@Autowired 默认是按照对象的数据类型进行自动装配的),我们可以使用@Qualifier配合@Autowired来解决这些问题:给 @Autowired 增加一个候选

值,做法是在 @Autowired 后面增加一个 @Qualifier 标注,提供一个 String 类型的值作为候选的 Bean 的名字。举例如下:
...
...

@Autowired
@Qualifier("person2")
public void setPerson(person p){}

@Qualifier 甚至可以作用于方法的参数(对于方法只有一个参数的情况,我们可以将 @Qualifer 标注放置在方法声明上面,但是推荐放置在参数前面):
@Autowired(required=false)
public void sayHello(@Qualifier("person1")Person p,String name){}
等价于在配置文件中指定某个 Bean 的 qualifier 名字,方法如下:



如果没有明确指定 Bean 的 qualifier 名字,那么默认名字就是 Bean 的名字。
@Qualifier("person") 中的 person 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。

我们还可以将 @Qualifier 标注在集合类型上,那么所有 qualifier 名字与指定值相同的 Bean 都将被注入进来。例如:






我们可以使用 标签来代替 标签,如果 标签和 标签同时出现,那么优先使用 标签。
如果没有 标签,那么会用 提供的键值对来封装 标签。针对上面的例子,改造如下:





如果 @Autowired 注入的是 BeanFactory、ApplicationContext、ResourceLoader 等系统类型,那么则不需要 @Qualifier,此时即使提供了 @Qualifier 注解,也将会被忽略;

@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。
@Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。

(4) 使用 @Resource 和 @Qualifier 指定 Bean 的自动装配:
@Resource并不是Spring的注解,它的包是 javax.annotation.Resource 需要导入。但是Spring支持该注解的注入。
共同点:两者都可以写在字段和setter方法上。两者如果都

写在字段上,就不需要写写setter方法。
@Autowired为Spring提供的注解,需导入Package:org.springframework.beans.factory.annotation.Autowired;
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
@Resource 注解默认是按 byName 自动注入,由J2EE提供。

如果希望根据 name 执行自动装配,那么应该使用 JSR-250 提供的 @Resource 注解,而不应该使用 @Autowired 与 @Qualifier 的组合。

@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。

@Resource 使用 byName 的方式执行自动封装。@Resource 标注可以作用于带一个参数的 Setter 方法、字段,以及带一个参数的普通方法上。
@Resource 注解有一个 name 属性,用于指定 Bean 在配置文件中对应的名字。如果没有指定 name 属性,那么默认值就是字段或者属性的名字。
@Resource 和 @Qualifier 的配合虽然仍然成立,但是 @Qualifier 对于 @Resource 而言,几乎与 name 属性等效。

如果 @Resource 没有指定 name 属性,那么使用 byName 匹配失败后,会退而使用 byType 继续匹配,如果再失败,则抛出异常。在没有为 @Resource 注解显式指定 name 属性的前提下,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。此时 name 属性不需要指定(或者指定为""),否则注入失败;如果使用了 @Qualifier,则该注解将被忽略。而对于用户自定义类型的注入,@Qualifier 和 name 等价,并且不被忽略。

@Resource:默认是按照名称或者Id进行自动装配的,只有当找不到匹配的名称或者Id时才按类型进行装配
@Resource(name="userDao")
private UsreDao userDao; //用于字段上

@Resource(name="userDao")
public void setUserDao(UserDao userDao) { //用于属性的setter方法上
https://www.sodocs.net/doc/259100862.html,erDao= userDao;
}
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入。
注意:@Autowire和@Resource都既可以写在属性field上,也可以写在set方法上。
=========================================================================
使用 @PostConstruct 和 @PreDestroy 指

定生命周期回调方法:

@PostConstruct 和 @PreDestroy 同样是由 JSR-250 规范定义的注解。他俩和 @Resource 注释类都位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中,因此在使用之前必须将其加入到项目的类库中(import javax.annotation.Resource / PostConstruct / PreDestroy;)。还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor:

CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口,它负责扫描使用了 JSR-250 注释的 Bean,并对它们进行相应的操作。

Spring Bean 是受 Spring IoC 容器管理,由容器进行初始化和销毁的(prototype 类型由容器初始化之后便不受容器管理),通常我们不需要关注容器对 Bean 的初始化和销毁操作,由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,这通常是针对一些资源的获取和释放操作。Spring 为此提供了三种方式供用户指定执行生命周期回调的方法。

第一种方式:实现 Spring 提供的两个接口:InitializingBean 和 DisposableBean。
如果希望在 Bean 初始化完成之后执行一些自定义操作,则可以让 Bean 实现 InitializingBean 接口,该接口包含一个 afterPropertiesSet() 方法,容器在为该 Bean 设置了属性之后,将自动调用该方法;
如果 Bean 实现了 DisposableBean 接口,则容器在销毁该 Bean 之前,将调用该接口的 destroy() 方法。
这种方式的缺点是,让 Bean 类实现 Spring 提供的接口,增加了代码与 Spring 框架的耦合度,因此不推荐使用。

第二种方式:在 XML 文件中使用 的 init-method 和 destroy-method 属性指定初始化之后和销毁之前的回调方法,代码无需实现任何接口。
这两个属性的取值是相应 Bean 类中的初始化和销毁方法,方法名任意,但是方法不能有参数。例如:


第三种方式:Spring 2.5 在保留以上两种方式的基础上,提供了对 JSR-250 的支持。JSR-250 规范定义了两个用于指定声明周期方法的注解:@PostConstruct 和 @PreDestroy。
这两个注解使用非常简单,只需分别将他们标注于初始化之后执行的回调方法或者销毁之前执行的回调方法上。由于使用了注解,因此需要配置相应的 Bean 后处理器,亦即在 XML 中增加如下一行:
。 或者:

这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方

法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。

==========================================================================
使用配置类与 @Configuration 和 @Bean注解

AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解,自此 XML 配置方式不再是 Spring IoC 容器的唯一配置方式。两者在一定范围内存在着竞争的关系,但是它们在大多数情况下还是相互协作的关系,两者的结合使得 Spring IoC 容器的配置更简单,更强大。

之前我们都是将配置信息集中写在 XML 中,如今使用注解,配置信息的载体由 XML 文件转移到了 Java 类中。我们通常将用于存放配置信息的类的类名以 “Config" 结尾,比如 AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上 @Configuration 注解,以明确指出该类是 Bean 配置的信息源。并且 Spring 对标注 Configuration 的类有如下要求:
1) 配置类不能是 final 的;
2) 配置类不能是本地化的,亦即不能将配置类定义在其他类的方法内部;
3) 配置类必须有一个无参构造函数。

基于注解的容器初始化:
AnnotationConfigApplicationContext 提供了三个构造函数用于初始化容器。
1)AnnotationConfigApplicationContext():该构造函数初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其 register() 方法注册配置类,并调用 refresh() 方法刷新容器。
2)AnnotationConfigApplicationContext(Class... annotatedClasses):这是最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的 Bean 自动注册到容器中。
3)AnnotationConfigApplicationContext(String... basePackages):该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的 Spring Bean,将其注册到容器中。
它不但识别标注 @Configuration 的配置类并正确解析,而且同样能识别使用 @Repository、@Service、@Controller、@Component 标注的类。

除了使用上面第三种类型的构造函数让容器自动扫描 Bean 的配置信息以外,AnnotationConfigApplicationContext 还提供了 scan() 方法,其功能与上面也类似,该方法主要用在容器初始化之后动态增加 Bean 至容器中。调用了该方法以后,通常需要立即手动调用 refresh() 刷新容器,以让变更立即生效。

需要注意的是,AnnotationConfigApplicationContext 在解析配置类时,会将配置类自身注册为一个 Bean,因为 @Configuration 注解本身定义时被 @Component 标注了。因此可以说,一个 @Configuration 同时也是一个 @Component。

AnnotationConfigApplicationContext 将配置类中标注了 @Bean 的方法的返回值识别为 Spring Bean,并注册到

容器中,受 IoC 容器管理。
@Configuration
public class BookStoreDaoConfig{
@Bean
public UserDao userDao(){ return new UserDaoImpl(); }
@Bean
public BookDao bookDao(){ return new BookDaoImpl(); }
}
Spring 在解析到以上文件时,将识别出标注 @Bean 的所有方法,执行之,并将方法的返回值 ( 这里是 UserDaoImpl 和 BookDaoImpl 对象 ) 注册到 IoC 容器中。默认情况下,Bean 的名字即为方法名。因此,与以上配置等价的 XML 配置如下:



在一般的项目中,为了结构清晰,通常会根据软件的模块或者结构定义多个 XML 配置文件,然后再定义一个入口的配置文件,该文件使用 将其他的配置文件组织起来。最后只需将该文件传给 ClassPathXmlApplicationContext 的构造函数即可。针对基于注解的配置,Spring 也提供了类似的功能,只需定义一个入口配置类,并在该类上使用 @Import 注解引入其他的配置类即可,最后只需要将该入口类传递给 AnnotationConfigApplicationContext。具体示例如下:
@Configuration
@Import({BookStoreServiceConfig.class, BookStoreDaoConfig.class})
public class BookStoreConfig{ … }

@Bean 具有以下四个属性:
name: 指定一个或者多个 Bean 的名字。这等价于 XML 配置中 的 name 属性。
initMethod: 容器在初始化完 Bean 之后,会调用该属性指定的方法。这等价于 XML 配置中 的 init-method 属性。
destroyMethod:该属性与 initMethod 功能相似,在容器销毁 Bean 之前,会调用该属性指定的方法。这等价于 XML 配置中 的 destroy-method 属性。
autowire: 指定 Bean 属性的自动装配策略,取值是 Autowire 类型的三个静态属性。Autowire.BY_NAME,Autowire.BY_TYPE,Autowire.NO。
与 XML 配置中的 autowire 属性的取值相比,这里少了 constructor,这是因为 constructor 在这里已经没有意义了。

设计 @Configuration 和 @Bean 的初衷,并不是为了完全取代 XML,而是为了在 XML 之外多一种可行的选择。由于 Spring 自发布以来,Spring 开发小组便不断简化 XML 配置,使得 XML 配置方式已经非常成熟,加上 Spring 2.0 以后出现了一系列命名空间的支持,使得 XML 配置方式成为了使用简单、功能强大的 Bean 定义方式。而且,XML 配置的一些高级功能目前还没有相关注解能够直接支持。因此,在目前的多数项目中,要么使用纯粹的 XML 配置方式进行 Bean 的配置,要么使用以注解为主,XML 为辅的配置方式进行 Bean 的配置。

之所以会出现两者共存的情况,主要归结为三个原因:其一,目前绝大多数采用 Spring 进行开发的项目,几

乎都是基于 XML 配置方式的,Spring 在引入注解的同时,必须保证注解能够与 XML 和谐共存,这是前提;其二,由于注解引入较晚,因此功能也没有发展多年的 XML 强大,因此,对于复杂的配置,注解还很难独当一面,在一段时间内仍然需要 XML 的配合才能解决问题。除此之外,Spring 的 Bean 的配置方式与 Spring 核心模块之间是解耦的,因此,改变配置方式对 Spring 的框架自身是透明的。Spring 可以通过使用 Bean 后处理器 (BeanPostProcessor) 非常方便的增加对于注解的支持。这在技术实现上非常容易的事情。

对于已经存在的大型项目,可能初期是以 XML 进行 Bean 配置的,后续逐渐加入了注解的支持,这时我们只需在 XML 配置文件中将被 @Configuration 标注的类定义为普通的 ,同时注册处理注解的 Bean 后处理器即可。示例如下:

// 假设存在如下的 @Configuration 类:
package bookstore.config;
import bookstore.dao.*;

@Configuration
public class MyConfig{
@Bean
public UserDao userDao(){ return new UserDaoImpl(); }
}

此时,只需要在Spring XML配置文件中做如下声明即可:




由于启用了针对注解的 Bean 后处理器,因此在 ApplicationContext 解析到 MyConfig 类时,会发现该类标注了 @Configuration 注解,随后便会处理该类中标注 @Bean 的方法,将这些方法的返回值注册为容器总的 Bean。

对于以上的方式,如果存在多个标注了 @Configuration 的类,则需要在 XML 文件中逐一列出。另一种方式是使用前面提到的自动扫描功能,配置如下:

如此,Spring 将扫描所有 demo.config 包及其子包中的类,识别所有标记了 @Component、@Controller、@Service、@Repository注解的类,由于 @Configuration 注解本身也用 @Component 标注了,Spring 将能够识别出 @Configuration 标注类并正确解析之。

-------------------------------------------------------------------------------------
对于以注解为中心的配置方式,只需使用 @ImportResource 注解引入存在的 XML 即可,如下所示:

@Configuration
@ImportResource("classpath:/bookstore/config/spring-beans.xml")
public class MyConfig{
}
// 容器的初始化过程和纯粹的以配置为中心的方式一致:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);

=============================================================
Spring多个配置文件组合方法

很多时候,由于Spring需要管理和配置的东西比较多,如果都放在一个配置文件中,配置文件会变的比较大,同时不方便与维护,一

般好的做法是按照功能模块将Spring配置文件分开,例如:DAO层配置到一个spring-dao.xml配置文件中,Service层配置到spring-service.xml文件中,Struts的action配置到spring-action.xml文件中,然后通过下面两种办法将这些分散的配置文件组合起来:

(1) 在一个作为Spring总配置文件中的元素定义之前,通过元素将要引入的spring其他配置文件引入,例如:








这样做对于汇编模块化的bean定义来说是很有用的。

(2) 对于JavaSE的工程,当使用下面方法获取ApplicationContext对象时将所有的spring配置文件通过数组传递进去
ApplicationContext context = new org.springframework.context.support.ClassPathXmlApplicationContext(new String[]{"action.xml", "service.xml"});
对于JavaEE工程,在web.xml文件中指定spring配置文件时可以指定多个,中间有逗号“,"分隔,也可以使用通配符方式

=============================================================
Spring配置中的注入方式

spring配置文件bean的配置规则:
(1) 一个Bean可以通过一个id属性惟一指定和引用
(2) 一个Bean也可以通过一个name属性来引用和指定,如果spring配置文件中有两个以上相同name的Bean,则spring通过name引用时,运行时后面的会自动覆盖前面相同name的bean引用,而不会报错。

对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程,spring的依赖注入有3种方式:

(1) 使用构造器注入(spring在实例化该Bean时会调用配置参数符合的构造方法):
简洁形式自1.2版本起就可以使用
单个入口参数
当构造函数含有一个以上同种类型的参数,或者属性值的标签已经被占用时,Spring允许使用从0开始的下标来避免混淆。


使用index可以减少一些代码,但是与type属性相比,它更易于出错且难于阅读。只有在构造函数参数不明确的时候,才应该使用index。




老式写法:

VALUE

bean="orderDAO"/>



(2) 使用属性的setter注入:



……

使用属性的setter注入方式时,所注入的属性必须提供setter和getter方法,spring在实例化时会自动调用无参数的构造方法或者静态工厂方法,实例化之后自动调用属性的set方法将值设置进去。

(3) 使用注解注入方式(在java中的字段上或者setter方法上通过使用注解):
字段注入:
@Resource
private UserDao dao;
属性注入:
@Resource
public void setUserDao(UserDao dao){
this.dao = dao;
}

(4) 自动依赖注入(隐式的):


这里也可以显式的用注入


(5) 依赖于另外一个 bean 的注入方式:



……



……



(6) Set集合注入:


value1
value2
……



(7) List集合注入:


value1
value2
……



(8) Map集合注入:




……



(9) Properties注入:


value1
value2
……



(10)子类与父类之间的依赖注入:
Spring 提供了一种类似于继承的机制来减少配置信息的复制并简化XML配置。定义一个子类,它就可以从父类那里继承配置信息,而父类实际上成为子类的一个模板。




两个子类 实现两个数据源









=======================================================
在 Sping MVC 中启用注解

启动Spring MVC的注解功能,完成请求和注解 POJO 的映射。在dispatcher-servlet.xml中写入:




使用 会自动注册这两个bean

使用到的注解:
@Component是通用标注,@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问
使用实例:
@Controller 或者 @Controller("softCreateController") 带有controller id
public class SoftCreateController extends SimpleBaseController {}

MVC中专用的注解:
@RequestMapping 用来标注 Controler 中的请求分配,可以注解类对象也可以注解到具体的方法。例如:
@Controller
@RequestMapping("/bbs")
public class BbsForumController {
@RequestMapping(method=RequestMethod.GET, value="/listBoardTopic.do")
public ModelAndView listBoardTopic(HttpServletRequest request) {}
}
除了在请求路径中使用 URI 模板(参后面的@PathVariable说明)定义变量之外,@RequestMapping 中还支持通配符“* "。
如下面的代码我就可以使用 /myTest/whatever/wildcard.do 访问到Controller 的testWildcard 方法。
@Controller
@RequestMapping("/myTest")
public class MyController{
@RequestMapping("*/wildcard")
public String testWildcard() {
return "wildcard";
}
}
使用属性: params 指定请求参数的
@RequestMapping(value="testParams" , params={ "param1=value1" , "param2" , "!param3" })
在上面的代码中我们用@RequestMapping 的params 属性指定了三个参数,这些参数都是针对请求参数而言的,它们分别表示:
参数param1 的值必须等于value1 ,参数param2 必须存在,值无所谓,参数param3 必须不存在,只有当请求/testParams.do 并且满足指定的三个参数条件的时候才能访问到该方法。
使用属性:method 主要是用于限制能够访问的方法类型的
@RequestMapping (value= "testMethod" , method={RequestMethod.GET , RequestMethod.DELETE })
使用属性:headers 可以通过请求头信息来缩小@RequestMapping 的映射范围
@RequestMapping (value="testHeaders" , headers={"host=localhost", "Accept"})
在上面的代码中当请求/testHeaders.do 的时候只有当请求头包含Accept 信息,且请求的 host 为 localhost 的时候才能正确的访问到testHeaders 方法。

@RequestParam 用来标

注请求处理方法的参数和URI中参数的对应。例如:
@RequestParam(value="/listBoardTopic.do")
public String listBoardTopic(@RequestParam("id")int topicId, ModelMap model) {}
在@RequestParam 中除了指定绑定哪个参数的属性value 之外,还有一个属性required ,它表示所指定的参数是否必须在request 属性中存在,默认是true ,表示必须存在,当不存在时就会报错。

@CookieValue 绑定 cookie 的值到 Controller 方法参数
@RequestMapping("/testCookie")
public String testCookieValue(@CookieValue("hello")String cookieOne, @CookieValue String cookieTwo) { 后一个取 cookie 中的命名为 cookieTwo 的 cookie 值
return "cookieValue" ;
}

@RequestHeader 绑定 HttpServletRequest 头信息到 Controller 方法参数
@RequestMapping("testRequestHeader")
public String testRequestHeader(@RequestHeader("Host") String hostAddr, @RequestHeader String Host, @RequestHeader String host) {
System.out.println(hostAddr + "-----" + Host + "-----" + host );
return "requestHeader";
}

@ModelAttribute 和 @SessionAttributes 传递和保存数据
SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型和控制器之间共享数据。

当 @ModelAttribute 标记在处理器方法参数上的时候,表示该参数的值将从 model 或者 Session 中取对应名称的属性值
@ModelAttribute声明标注在Controller参数上,表示该参数的 value 来源于 model 里"queryBean",并被保存到 model 里。例如:
public String handleInit(@ModelAttribute("queryBean") ManagedUser sUser,Model model) {}

声明在方法上,表示该方法的返回值被保存到model里。当 @ModelAttribute 标记在方法上的时候,该方法将在处理器方法执行之前执行,然后把返回的对象存放在 session 或模型属性中,
属性名称可以使用 @ModelAttribute("attributeName") 在标记方法的时候指定,若未指定,则使用返回类型的类名称(首字母小写)作为属性名称。
例如:
@ModelAttribute("coopMap")//将coopMap返回到页面
public Map coopMapItems(){}

@SessionAttributes 只能声明在类上,指定 ModelMap 中的哪些属性需要转存到session中,以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。例如:
@SessionAttributes("currentUser") //将ModelMap中属性名为currentUser的属性值存放在session中
@SessionAttributes({"attr1","attr2"})
@SessionAttributes(types = User.class) //single attribute
@SessionAttributes(types = {User.class,Dept.class}) //attribute: user dept
@SessionAttributes(value = {"attr1","attr2"}, types = {User.class,Dept.class})

@Cacheable 声明一个方法的返回值应该被缓存。例如:

@Cacheable(modelId="testCaching")
@Cacheable注解可以用在方法或者类级别。当他应用于方法级别的时候,就是如上所说的缓存返回值了。当应用在类级别的时候,这个类的所有方法的返回值都将被缓存。
@Cacheable注解有三个参数,value是必须的,还有key和condition。第一个参数,也就是value指明了缓存将被存到什么地方。
@Cacheable(value="employee", key="#surname", condition="#age<25")) 这里的key使用下面方法的参数作为key,并且是age参数的值小于25才缓存 (或者"#surname.length() > 20 )
public Person findEmployeeBySurname(String firstName, String surname, int age)

@CachePut 这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。例如:
@CachePut(value="accountCache",key="#account.getName()")// 更新accountCache 缓存
public Account updateAccount(Account account) {
return updateDB(account);
}

@CacheFlush 声明一个方法是清空缓存的触发器。例如:
@CacheFlush(modelId="testCaching")

@CacheEvict 声明标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。
@CacheEvict(value="employee", key="#surname", condition="#age<25"))
该注解还有另外两个属性参数:
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value="testcache",allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。
例如: @CachEvict(value="testcache",beforeInvocation=true)

@InitBinder 定制自己的数据类型自动转换器
在通过处理器方法参数接收 request 请求参数绑定数据的时候,对于一些简单的数据类型 Spring 会帮我们自动进行类型转换,而对于一些复杂的类型由于 Spring 没法识别,所以也就不能帮助我们进行自动转换了,这个时候如果我们需要 Spring 来帮我们自动转换的话就需要我们给 Spring 注册一个对特定类型的识别转换器。 Spring 允许我们提供两种类型的识别转换器,一种是注册在 Controller 中的,一种是注册在 SpringMVC 的配置文件中。定义在 Controller 中的是局部的,只在当前 Controller 中有效,而放在 SpringMVC 配置文件中的是全局的,所有 Controller 都可以拿来使用。
当Controller处理器方法参数使用@RequestParam、@PathVariable、@RequestHeader、@CookieValue和@ModelAttribute标记的时候都会触发initBinder方法的执行

如果希望某个属性编辑器仅作用于特定的Control

ler,可以在该方法中向 Controller 注册若干个属性编辑器。例如:
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
PropertyEditor propertyEditor = new CustomDateEditor(dateFormat, true); // 第二个参数表示是否允许为空
binder.registerCustomEditor(Date.class , propertyEditor);
}
Spring 已经给我们提供了一些常用的属性编辑器,如 CustomDateEditor 、 CustomBooleanEditor 等。

在 java 中有一个封装类是实现了 PropertyEditor 接口的,它是 PropertyEditorSupport 类。所以如果需要实现自己的 PropertyEditor 的时候只需要继承 PropertyEditorSupport 类,然后重写其中的一些方法。一般就是重写 setAsText 和 getAsText 方法就可以了, setAsText 方法是用于把字符串类型的值转换为对应的对象的,而 getAsText 方法是用于把对象当做字符串来返回的。在 setAsText 中我们一般先把字符串类型的对象转为特定的对象,然后利用 PropertyEditor 的 setValue 方法设定转换后的值。在 getAsText 方法中一般先使用 getValue 方法取代当前的对象,然后把它转换为字符串后再返回给 getAsText 方法。

如果需要定义全局的类型转换器就需要实现自己的 WebBindingInitializer 对象,然后把该对象注入到 AnnotationMethodHandlerAdapter 中,这样 Spring 在遇到自己不能解析的对象的时候就会到全局的 WebBindingInitializer 的 initBinder 方法中去找,每次遇到不认识的对象时, initBinder 方法都会被执行一遍。
public class MyWebBindingInitializer implements WebBindingInitializer {
@Override
public void initBinder(WebDataBinder binder, WebRequest request) {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
PropertyEditor propertyEditor = new CustomDateEditor(dateFormat, true);
binder.registerCustomEditor(Date. class , propertyEditor);
}
}
然后在配置文件里:






@Transactional 事务管理
@Transactional(rollbackFor={Exception.class})

@ResponseBody 告诉视图解析器,把方法返回的对象当作返回的正文内容来处理,而不是当作一个视图页面名称来dispatch,也不是一个ModelAndView对象。
public @ResponseBody ReturnObject controllerMethod();

@NumberFormatannotation 数据绑定支持

@DateTimeFormat 定义日期相关的解析/格式化元数据。修正 SpringMVC 使用 @ResponseBody 时返回的日期格式
因为spri

ngMVC没有提供默认的日期转换器,前段页面传递过来日期字符串怎么转换为日期类型,如果没有提供全局日期转换器或者数据绑定的基础上,可以使用@DatetimeFormat注解完成
该注解可以作用在METHOD,FIELD以及PARAMETER级别上。 参数如下:
pattern:指定解析/格式化字段数据的模式,如"yyyy-MM-dd HH:mm:ss"
iso:指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) ISO.DATE(yyyy-MM-dd) ISO.TIME(hh:mm:ss.SSSZ) ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ),默认ISO.NONE;
style:指定用于格式化的样式模式,默认“SS",具体使用请参考Joda-Time类库的org.joda.time.format.DateTimeFormat的forStyle的javadoc;

使用@ResponseBody时返回json字符串的日期格式。Date类型属性默认返回一个Long型的时间戳,怎样能够返回自定义的日期格式?

@Valid 开启spring的Valid功能支持
该功能需要 validation-api-1.0.0.GA.jar:JDK的接口; hibernate-validator-4.2.0.Final.jar是对上述接口的实现;
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)

开启以后,在pojo bean中:
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
@RequestMapping("/login")
public String testValid(@Valid User user, BindingResult result){ //备注:这里一个@Valid的参数后必须紧挨着一个BindingResult 参数,否则spring会在校验不通过时直接抛出异常
if (result.hasErrors()){
List errorList = result.getAllErrors();
for(ObjectError error : errorList){
System.out.println(error.getDefaultMessage());
}
}
return "test";
}
在jsp页面中使用:
<%@ taglib prefix="form" uri="https://www.sodocs.net/doc/259100862.html,/tags/form"%>
来显示字段错误信息

JSR303定义的校验类型

空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.

Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false

长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是

相关主题