您的位置:首页 > 编程语言 > Java开发

Spring应用上下文配置:混合配置

2016-09-07 22:34 441 查看

前言

        所谓Spring应用上下文混合配置,指的是配置一部分在xml文件,一部分在java代码。这种方式不是很常见,常见的要么是纯xml文件配置,要么是纯java编程配置。纯java编程配置后续章节我们会详细讲解。关于这两种纯配置方式的比较,我们前面的章节已经提过。

根应用上下文配置

<?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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 
<context:annotation-config />
<context:component-scan base-package="com.gxz" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service" />
</context:component-scan>

</beans>

        配置context:annotation-config,表示注解配置,凡是java实现类做了以下的注解,都被认为是需要Spring管理的bean:@Component、@Service、@Controller、@Repository等。所谓纳入Spring管理的bean,指的是在恰当的时间,Spring将实例化bean并为之注入依赖。配置context:component-scan:表示组件扫描,扫描base-package指定的包及其子包,若这些包中的实现类有注解@Component及其具体注解,都被认为是Spring管理的bean。
        配置context:include-filter:表示扫描过滤器,一种包含的过滤器,Spring管理expression指定的bean。上述配置中,expression="org.springframework.stereotype.Service",表示Spring只管理类型为@Service的bean,而忽略其他类型的bean,比如类型为@Component、@Controller、@Repository的bean不纳入Spring的管理。
        配置type="annotation":表示过滤器针对实现类的注解。配置use-default-filters="false":表示不再使用默认的过滤器,而是使用自定义过滤器。若是我们使用了包含过滤器或是排除过滤器,那么,一定要配置不再使用默认过滤器。否则,包含过滤器或是排除过滤器的配置无效。包含过滤器的配置是context:include-filter,排除过滤器的配置是context:exclude-filter。关于排除过滤器,接下类的章节我们会详细讲解。
        从上述的配置我们可以看出,我们并没有直接配置需要Spring管理哪一个具体的bean,而是通过指定范围扫描注解的方式。在指定的范围内,如果我们希望实现类纳入Spring管理,我们需要在实现类加上注解@Component及其具体注解。这需要在xml文件配置,需要在java实现类配置(java编程配置),所以,此种配置方式称之为混合配置。
        通常情况下,我们创建一个根应用上下文和一个DispatcherServlet应用上下文,根应用上下文管理一组和业务逻辑相关
4000
的bean,而DispatcherServlet应用上下文管理一组和控制相关的bean。所以,在配置根应用上下文是,我们使用了包含过滤器,让Spring只管理类型为@Service的bean。

DispatcherServlet应用上下文配置

<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <mvc:annotation-driven />

<context:annotation-config />
<context:component-scan base-package="com.gxz" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>

业务逻辑相关的bean

package com.gxz;

import org.springframework.stereotype.Service;

@Service
public class StudentImp implements Student {
@Override
public String sayHi(String name) {
return "Hi," + name;
}

public StudentImp() {
super();
System.out.println("StudentImp()");
}

}

package com.gxz;

public interface Student {
String sayHi(String name);
}

控制器bean

package com.gxz;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class StudentController {
@Autowired
private Student student;

@ResponseBody
@RequestMapping("/")
public String sayHi() {
return "Hi!";
}

@ResponseBody
@RequestMapping(value="/say", params="name")
public String say(@RequestParam("name") String name) {
return student.sayHi(name);
}

public void setStudent(Student student) {
this.student = student;
}

public StudentController() {
super();
System.out.println("StudentController()");
}
}

        注解@Autowired:表示Spring在实例化bean StudentController时,为之注入依赖Student,所谓的依赖Student,就是一个实现了Student接口的实现类,它必须是一个bean。可以看出,这个依赖就是bean StudentImp。依赖必须是唯一的,如果实现接口Student的实现类有两个,分别是StudentImp、ComputerStudent,并且这两个实现类都是受Spring管理的bean,那么,Spring会报出异常,信息如下所示。
package com.gxz;

import org.springframework.stereotype.Service;

@Service
public class ComputerStudent implements Student {

public String sayHi(String name) {
return "Hi, " + name + ".Happy to see again.";
}

}

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.gxz.Student com.gxz.StudentController.student; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.gxz.Student] is defined: expected single matching bean but found 2: computerStudent,studentImp

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.gxz.Student com.gxz.StudentController.student; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.gxz.Student] is defined: expected single matching bean but found 2: computerStudent,studentImp

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.gxz.Student] is defined: expected single matching bean but found 2: computerStudent,studentImp
          若要解决这个异常,就必须告诉Spring,Spring在实例化bean StudentController时,为之注入依赖Student时明确指定是哪一个bean(computerStudent,studentImp二选一)。

@Autowired
@Qualifier("computerStudent")
private Student student;

        配置@Qualifier:指定注入的依赖是computerStudent,而不是studentImp。Spring实例化bean,默认取名为类名的骆驼命名。比如实现类StudentImp,作为bean实例化后取名为studentImp,实现类ComputerStudent取名为computerStudent,实现类StudentController取名为studentController。
        现在有这样一种场景,实现类为com.gxz.StudentImp作为bean实例化后,默认取名为studentImp,实现类com.gxz.config.StudentImp作为bean实例化后取名为studentImp,这就导致bean名字重复,Spring报出如下异常。
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/rootContext.xml]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'studentImp' for bean class [com.gxz.StudentImp] conflicts with existing, non-compatible bean definition of same name and class [com.gxz.config.StudentImp]

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'studentImp' for bean class [com.gxz.StudentImp] conflicts with existing, non-compatible bean definition of same name and class [com.gxz.config.StudentImp]

        为了解决上述问题,需要在配置实现类为bean时,为之指定实例化后的名字,以作区别。

package com.gxz.config;

import org.springframework.stereotype.Service;

import com.gxz.Student;

@Service("configStudentImp")
public class StudentImp implements Student {

public String sayHi(String name) {
return "Hi, " + name + ".Happy to see again.";
}

}

        上述代码实现类com.gxz.config.StudentImp作为bean实例化后,取名为configStudentImp。我们知道,实现类com.gxz.StudentImp作为bean实例化后,默认取名为studentImp,这样一来,二者的名字便不再重复。
        以下注解和@Autowired的用法和作用等价:@Inject、@Resource。其中,@Autowired的全称为org.springframework.beans.factory.annotation.Autowired,@Inject的全称是javax.inject.Inject,@Resource的全称是javax.annotation.Resource。
        以下注解和@Qualifier的用法和作用等价:@Named。其中,@Qualifier的全称是org.springframework.beans.factory.annotation.Qualifier,@Named的全称是javax.inject.Named。

注解@Autowired配置的位置

        到目前为止,注解@Autowired配置的位置一直在字段上。实际上,它可以位于字段的set方法上,也可以在构造函数上,该构造函数的参数之一是字段。
private Student student;
@Autowired
public void setStudent(Student student) {
this.student = student;
}

        上述代码在set方法上告诉Spring注入依赖student。
private Student student;
@Autowired
public StudentController(Student student) {
super();
this.student = student;
System.out.println("StudentController() with argument.");
}

        上述代码在构造方法上告诉Spring注入依赖。默认情况下,Spring实例化bean时,调用的是bean的默认构造函数,即无参构造函数。但是,若是有构造函数被备注为@Autowired,那么,Spring将调用该@Autowired构造函数。因为,Spring需要调用@Autowired构造函数为bean注入依赖。
        注意,一个bean不能有两个或以上的@Autowired构造函数,否则,Spring不知要调用哪个构造函数, 报出如下异常。
@Autowired
public StudentController(Student student) {
super();
this.student = student;
System.out.println("StudentController() with argument student.");
}

@Autowired
public StudentController( Teacher teacher) {
super();
this.teacher = teacher;
System.out.println("StudentController() with arguments teacher.");
}

org.springframework.beans.factory.BeanCreationException: Invalid autowire-marked constructor: public com.gxz.StudentController(com.gxz.Student). Found another constructor with 'required' Autowired annotation: public com.gxz.StudentController(com.gxz.Student,com.gxz.Teacher)

关于排除过滤器存在的问题

        上面我们已经提过排除过滤器,现在谈一谈它存在的问题。我们有这样的场景,根应用上下文管理一组和业务逻辑相关的bean,也就是说,控制器bean需要被排除。
<?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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 
<context:annotation-config />
<context:component-scan base-package="com.gxz" use-default-filters="false">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>

</beans>

        上述配置说明,根应用上下文排除控制器bean。但是,根据测试结果,上述配置排除了所有bean。也就是说,包com.gxz及其子包所有的bean都被排除。显然,这不是我们想要的结果。若是我们把use-default-filters设置为true,结果不排除任何bean。也就是说,包com.gxz及其子包所有的bean都纳入Spring管理,这也不是我们想要的结果。因此,我们要慎重使用排除过滤器,因为它总是没有达到我们的期望。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: