组件扫描(component scanning): Spring 能够从 classpath 下自动扫描,
侦测和实例化具有特定注解的组件.
常用的注解
- @Component: 基本注解, 标识了一个受 Spring 管理的组件
- @Repository: 标识持久层(dao层)组件
- @Service:标识业务层(service层)组件
- @Controller: 标识表现层(控制器)组件
- 上面四种注解作用都是一样的,只存在语义上的区别而已,用来提高代码可读性。
- @Component是基础注解,没有语义,当你不知道标志的模块属于哪一层时可以使用这个。
- 其他三个都是在@Component注解基础上衍生出的,都有属于自己标识的模块。
- 这四个注解是可以混用的,作用都是一样的,不过基于代码规范不介意这样使用。
使用注解
- 控制层:用@Controller标识
package com.spring.annotation.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void save() {
System.out.println("UserController save...");
}
}
相当于在配置文件中配置了
<bean id="userController" class="com.spring.annotation.controller.UserController"></bean>
- 业务层:用@Service标识
package com.spring.annotation.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void save() {
System.out.println("UserService save...");
}
}
相当于在配置文件中配置了
<bean id="userService " class="com.spring.annotation.service.UserService "></bean>
- 持久化层:用@Repository标识
这里我们一般会先创建一个接口:
package com.spring.annotation.repository;
public interface UserRepository {
public void save();
}
实现类:
package com.spring.annotation.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository{
@Override
public void save() {
System.out.println("UserRepository save...");
}
}
相当于在配置文件中配置了
<bean id="userRepository " class="com.spring.annotation.repository.UserRepositoryImpl "></bean>
- 在一些大型应用中或许需要DAO和Service的多种实现(比如帐户的DAO,可能需要一种Hibernate实现、一种CMP实现和一种JDO实现),为了向上一层隐藏具体实现类,需要采用接口。如果只有一种实现就没必要使用接口了。
- 一般持久层(dao)需要多种实现的较多些,service很少用。所以一般持久层用个接口就行了。在我的工作中很少在这三层用接口,没有必要。
上面几个模块定义好了并且注解也都加上了,但是光这样还不能让spring帮我们自动创建bean,还需要一个步骤:在配置文件中配置扫描的包。
<!-- 扫描com.spring.annotation包以及所有子包,为所有加了注解的类创建bean -->
<context:component-scan base-package="com.spring.annotation">
</context:component-scan>
- 测试
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
UserController uc = (UserController) ctx.getBean("userController");
uc.save();
UserService us = (UserService) ctx.getBean("userService");
us.save();
UserRepository ur = (UserRepository) ctx.getBean("userRepository");
ur.save();
可以看到上面几个注解都生效了
命名策略
- 对于扫描到的组件, Spring 有默认的命名策略: 首字母小写的类命。 比如 UserService -> userService
- 我们也可以指定bean的名称(id),使用注解的value属性,如@Repository(value=”userRepository”)
- value属性是注解的默认属性,可以省略,即@Repository(“userRepository”)
扫描的配置
- resource-pattern
使用resource-pattern属性,进一步过滤扫描范围
<context:component-scan base-package="com.spring.annotation"
resource-pattern="service/*.class">
</context:component-scan>
这里进一步限定只扫描service包里的class文件
- context:include-filter
该子节点表示要包含的目标类
type : annotation表示只包含某个注解
<context:component-scan base-package="com.spring.annotation" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
- 只扫描有Controller注解的
- 必须设置use-default-filters=”false”,因为默认包含很多的注解,如果不将默认设置关闭,这个是不会生效的。这里设置为false将让原来默认开启的注解失效。
- expression的属性值是定义注解的类。
type:assignable表示只包含某个类
<context:component-scan base-package="com.zj.annotation" use-default-filters="false">
<context:include-filter type="assignable" expression="com.zj.annotation.controller.TestController"/>
</context:component-scan>
只扫描TestController这个类
- 使用context:exclude-filter
该子节点表示要排除的目标类
context:exclude-filter和context:include-filter恰好相反,但是使用方法是一样的,也有type属性也有assignable和annotation,这个不需要配置use-default-filters,使用默认值就可以。
本系列参考视频教程: http://edu.51cto.com/course/1956.html