标注和自动识别
在大型的项目中,有数百的beans,无论用xml还是手动代码设置都很崩溃。可以通过自动扫描和标注设置,如servlet中的annotation的标注。
类标注:@org.springframework.stereotype.Component:可以写@Compoment,表明是Srping管理的beans,Sping将实例化,并注入到他们的依赖。@Controller(可以进一步分为@WebController,@RestController), @Repository,@Service,都是bean。
方法标注:@org.springframework.beans.factory.annotation.Autowired(在import相关的package,同样可以写为@Autowired),表明应在实例化后注入,也可以在构造函数中标记。一般Spring管理的bean必须有零参数的构造函数,但是在@Autowired构造函数的情况下,将注入所有的构造函数的参数。
java代码中增加标注
@Service
@Service
public class GreetingServiceImpl implements GreetingService{
@Override
public String getGreeting(String name) {
return "Hello," + name + "!";
}
}
@Controller和@Autowired
我们可以使用@Inject(javax.inject.Inject)来替代@Autowired。@Controller
public class HelloController {
private GreetingService greetingService;
//表明实例化后,此处马上注入
@Autowired
public void setGreetingService(GreetingService greetingService) {
this.greetingService = greetingService;
}
.......
}
我们还可以将@Autowired加在field上
@Controller
public class HelloController {
@Autowired
private GreetingService greetingService;
public void setGreetingService(GreetingService greetingService) {
this.greetingService = greetingService;
}
.......
}
我们还可以将@Autowired加在constructor上
public class HelloController {
private GreetingService greetingService;
@Autowired
public HelloController(GreetingService greetingService){
this.greetingService = greetingService;
}
... ...
}
上下文的xml配置
rootContext.xml
使用上一个例子的web.xml,无需更改。rootContext.xml使用到context:,schema所以需要加上context,我们设置通过标注来配置,以及扫描哪个包。<?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.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:annotation-config />
<context:component-scan base-package="cn.wei.flowingflying.chapter12">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>
ServletContext.xml
看xml的内容,有bean,mvc,context,在schema中要补齐。同样设置通过标注来配置,以及扫描哪个包。<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="cn.wei.flowingflying.chapter12" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>
exclude和include
注意到在根上下文的扫描设置context:component-scan中,我们加入了exclude-filter,而在servlet context中则设置了include-filter。如果我们不设置,均采用下面:<context:component-scan base-package="cn.wei.flowingflying.chapter12" />
如果这样设置,将扫描所有的@Components。也就是将这个cn.wei.flowingflying.chapter12下面的文件分布在分布扫描了两次,一次在根上下文,一次在dispatcherservlet context,就会有两个HelloController实例和两个GreetingServiceImpl实例。
我们希望在root context中扫描services, repositories以及其他业务逻辑,而DispacherServlet context只扫描controller。因此在root context中,我们通过exclude-filter设置扫描controller外所有的组件:
<context:component-scan base-package="com.yourcompany.yourproject">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
在DispatcherServlet context中则通过include-filter要求扫描Controller。use-default-filters为false表示不扫描,缺省为true。下面表示只扫描Controller。
<context:component-scan base-package="com.yourcompany.yourproject" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
局限
自动扫描方式很方便,但有局限:
- 第三方提供的类作为bean:我们无法去增加spring annotation
- 不能自动注册的bean,例如Java Persistence API。
在上述情况,我们需要在配置中声明<bean>元素进行补充。
识别或注入错误
如果spring找不到bean的依赖,或者找到多个依赖,就会在启动是抛出异常。
对于多个依赖,可以使用@org.springframework.beans.factory.annotation.Qualifier或者@org.springframework.context.annotation.Primary来避免。
Controller指定注入bean名字:@Qualfier
当使用@Autowired,@Qualifier可以制定bean的名字。我们在例子中增加GreetingServiceImpl2的Service实现
service bean greeting1:
@Service(value="greeting1")
public class GreetingServiceImpl implements GreetingService{
......
}
service bean greeting2:
@Service(value="greeting2")
public class GreetingServiceImpl2 implements GreetingService{
......
}
controller bean
@Controller
public class HelloController {
private GreetingService greetingService;
@Autowired
@Qualifier("greeting2")
public void setGreetingService(GreetingService greetingService) {
this.greetingService = greetingService;
}
... ...
}
bean要求作为@Primary
service bean 1:@Service
public class GreetingServiceImpl implements GreetingService{
........
}
service bean 2:
@Service
@Primary
public class GreetingServiceImpl2 implements GreetingService{
.... ....
}
Controller bean,将优先使用标记@Primary的bean
@Controller
public class HelloController {
private GreetingService greetingService;
@Autowired
public void setGreetingService(GreetingService greetingService) {
this.greetingService = greetingService;
}
}
相关链接: 我的Professional Java for Web Applications相关文章