1 概述
相对于XML方式而言,通过注解的方式配置Bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。
2 导包
因为注解是基于面向切面的,所以要使用注解就要导入AOP包。
spring-aop-4.0.0.RELEASE.jar
3 配置扫描
3.1 是什么
使用注解不需要在XML文件中配置Bean,但需要配置扫描才能让Spring容器管理Bean。
配置扫描需要使用context名称空间。
3.2 扫描基类
使用base-package指定需要扫描的基类包,Spring容器会扫描这个基类包及其子包中的所有类,多个包使用逗号分隔。
<context:component-scan base-package="com"></context:component-scan>
3.3 扫描特定的类
如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类。
扫描全部以Service结尾的类:
<context:component-scan base-package="com" resource-pattern="*/*Service.class" />
3.4 指定类
3.4.1 包含匹配的类
在使用扫描配置中使用context:include-filter标签配置要包含的类,使用expression属性配置过滤表达式,使用type属性配置过滤表达式的类型。
通常需要与use-default-filters属性配合使用才能够达到仅包含某些Bean的效果。
<context:component-scan base-package="com" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
使用use-default-filters属性用来表示是否使用默认的过滤器,默认包含base-package属性中的所有类,false表示不包含任何类,true表示包含全部类,默认值是true。
3.4.2 排除匹配的类
在使用扫描配置中使用context:exclude-filter标签配置要排除的类,使用expression属性配置过滤表达式,使用type属性配置过滤表达式的类型。
<context:component-scan base-package="com">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>
3.4.3 配置规则
1)如果type是annotation,那么expression需要按照注解的全类名进行匹配,这个规则是根据是否标注指定的注解来过滤类。
<!-- 过滤掉所有标注了Component注解的类。 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component" />
2)如果type是assignable,那么expression需要按照类的全类名进行匹配,这个规则是根据是否是指定的类的子类来过滤类。
<!-- 过滤掉所有UserDao下的子类。 -->
<context:exclude-filter type="assignable" expression="com.dao.UserDao" />
3)如果type是aspectj,那么expression需要按照AspectJ表达式进行匹配,这个规则是根据是否匹配类名来过滤类。
<!-- 过滤掉所有类名以Service结尾的类。 -->
<context:exclude-filter type="aspectj" expression="com.*Service" />
4)如果type是regex,那么expression需要按照正则表达式进行匹配,这个规则是根据正则表达式是否匹配类名来过滤类。
<!-- 过滤掉所有anno包下的类。 -->
<context:exclude-filter type="regex" expression="com\.anno\.*" />
5)如果type是custom,那么expression需要按照编码的方式进行匹配,这个规则是根据编码的方式自定义过滤的。
该类必须实现org.springframework.core.type.filter.TypeFilter接口。
<!-- 过滤掉所有XxxTypeFilter中指定的类。 -->
<context:exclude-filter type="custom" expression="com.XxxTypeFilter" />
4 标识类
4.1 分类
4.1.1 Component
普通组件,标识一个受Spring容器管理的Bean。
4.1.2 Repository
持久化层组件,标识一个受Spring容器管理的持久化层Bean。
4.1.3 Service
业务逻辑层组件,标识一个受Spring容器管理的业务逻辑层Bean。
4.1.4 Controller
表述层控制器组件,标识一个受Spring容器管理的表述层控制器Bean。
4.2 命名规则
默认使用Bean的简单类名首字母小写后得到的字符串作为id:
@Component
public class School {
}
也可以使用注解的value属性指定id,因为只有一个value属性,因此value属性也可以省略不写:
@Component("school")
public class School {
}
4.3 获取方式
使用类名获取。
使用Bean的简单类名首字母小写获取。
使用注解指定的value值获取。
4.4 创建机制
默认是单例模式,可以通过Scope注解来改变创建机制。
@Component("school")
@Scope("prototype")
public class School {
}
5 自动装配
5.1 需求
Controller中往往需要用到Service的实例,Service中往往需要用到Repository的实例。
Spring可以通过注解的方式实现属性的装配。
5.2 实现依据
在指定了要扫描的包的同时,会自动注册一个Bean的后置处理器,即AutowiredAnnotationBeanPostProcessor的实例。
该后置处理器可以自动装配标记了Autowired注解、Resource注解、Inject注解的Bean。
5.3 注解分类
5.3.1 Autowired
根据属性类型实现自动装配。
如果找不到对应的Bean,会抛出相应的异常,在将required属性设置为false后,如果找不到则不会抛出异常,会设置为null。
@Autowired(required = false)
public Address address;
当要注入的是接口类型,存在多个类型的实现类时:
如果没有设置属性名称,一个类型对应多个实现类,会抛出NoUniqueBeanDefinitionException异常。
如果设置了属性名称,则根据属姓名称匹配Bean的id值,如果没有找到会抛出NoSuchBeanDefinitionException异常。
5.3.2 Qualifier
根据属性名称自动装配,和Autowired注解搭配使用。
如果一个接口有多个实现类,每个实现类有不同的id值,在使用Autowired注解根据属性值查找Bean失败后,可以使用Qualifier注解指定Bean的id值进行查找。
使用指定id值查找Bean时,如果找不到会抛出NoSuchBeanDefinitionException异常。
@Autowired
@Qualifier("addressBJ")
public Address address;
5.3.3 Resource
既可以按类型注入,也可以通过设置name属性按名称注入。
按类型注入:
@Resource
public Address address;
按名称注入:
@Resource(name = "addressSH")
public Address address;
该注解是由Java官方提供的注解,Autowired实现了Resource注解的功能,Spring官方不建议使用。
5.3.4 Value
用于实现普通属性注入。
@Value("张三")
public String name;
6 注入泛型依赖
6.1 说明
在父类中使用Autowired注解注入所需组件,无需将父类声明为组件。
在子类中使用父类注入的组件即可调用子类重写后的方法,需要将子类声明为组件。
6.2 使用
父类Dao:
public abstract class BaseDao<T> {
public abstract void save();
}
子类Dao:
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {
System.out.println("BookDao...save()...");
}
}
父类Service:
public class BaseService<T> {
@Autowired
protected BaseDao<T> baseDao;
}
子类Service:
@Service
public class BookService extends BaseService<Book> {
public void save() {
baseDao.save();
}
}
7 生命周期
7.1 在Bean注解中指定方法
7.1.1 说明
使用Bean注解管理Spring容器中的Bean,类似于在配置文件中使用bean标签。
Bean注解中的属性有:
通过name属性指定组件名称,如果缺省则以当前方法名为组件名称。
通过initMethod属性指定组件加载时的方法。
通过initDestroy属性指定组件销毁时的方法。
7.1.2 使用
将Bean注解标注在配置类的方法上,该方法返回某个Bean的实例:
@Bean(name="demo", initMethod="init", destroyMethod="destroy", autowire=Autowire.BY_NAME)
public Demo getDemo() {
System.out.println("DemoConfiguration getDemo() ...");
return new Demo();
}
加载容器的时候会自动调用被Bean注解修饰的方法。
在执行完Bean的构造方法后还会执行指定的初始化方法,在销毁容器前也会执行指定的销毁方法。
7.2 使用注解
7.2.1 说明
被PostConstruct注解标识的方法,会在初始化之前执行,当同时实现了InitializingBean接口时,先执行被注解标识的方法。
被PreDestory注解标识的方法,会在销毁方法之前执行,当同时实现了DisposableBean接口时,先执行被注解标识的方法。
7.2.2 使用
创建类:
public class User implements InitializingBean, DisposableBean {
public String name;
public User() {
System.out.println("Step1 执行Bean的构造方法");
}
public void setName(String name) {
System.out.println("Step2 设置Bean的属性值");
this.name = name;
}
public void initMethod() {
System.out.println("Step3 执行Bean的初始化方法");
}
public void destroyMethod() {
System.out.println("Step5 执行Bean的销毁方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Step3 执行Bean的afterPropertiesSet方法");
}
@Override
public void destroy() throws Exception {
System.out.println("Step5 执行Bean的destroy方法");
}
@PostConstruct
public void postConstruct() throws Exception {
System.out.println("Step3 执行Bean的postConstruct方法");
}
@PreDestroy
public void preDestroy() throws Exception {
System.out.println("Step5 执行Bean的preDestroy方法");
}
}
创建配置类:
@Configuration
public class BeanConfig {
@Bean(name = "user",initMethod = "initMethod", destroyMethod = "destroyMethod")
public User getUser() {
User user = new User();
user.setName("张三");
return user;
}
}
测试方法:
@Test
public void testBean() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
User user = context.getBean("user", User.class);
System.out.println("Step4 获取Bean的实例并使用");
System.out.println(user);
// Step5 手动销毁容器,执行Bean的销毁方法
context.close();
}
执行结果:
Step1 执行Bean的构造方法
Step2 设置Bean的属性值
Step3 执行Bean的postConstruct方法
Step3 执行Bean的afterPropertiesSet方法
Step3 执行Bean的初始化方法
Step4 获取Bean的实例并使用
com.demo.spring.User@5bc9ba1d
Step5 执行Bean的preDestroy方法
Step5 执行Bean的destroy方法
Step5 执行Bean的销毁方法