002SpringIOC004基于注解配置Bean

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的销毁方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值