准备

1、使用 maven 创建一个 java 项目,依赖如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

pom.xml

2、创建测试 pojo:

package com.springanno.pojo;

public class User {
    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

com.springanno.pojo.User

组件注册

@Configuration-配置类

@Configuration 标注的类就是一个 Spring 配置类,配置类的作用就相当于之前我们使用的 Spring 配置文件。

1、创建配置类,通过 @Configuration 注解标注一个类为配置类:

package com.springanno.config;

import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 */
@Configuration
public class MainConfig {
}

com.springanno.config.MainConfig

2、通过读取注解配置类来创建 IOC 容器:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

test

@ComponentScan-包扫描

@ComponentScan 注解的作用相当于在 Spring 配置文件中配置 <context:component-scan> 标签。

1、创建测试 Service 类:

package com.springanno.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
}

com.springanno.service.UserService

2、使用 @ComponentScan 注解:

package com.springanno.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 */
@Configuration
@ComponentScan("com.springanno")
public class MainConfig {
}

com.springanno.config.MainConfig

3、测试:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService bean = applicationContext.getBean(UserService.class);
System.out.println(bean);
/*
com.springanno.service.UserService@77e9807f
 */

test

@ComponentScan 相关属性:

  • ComponentScan.Filter[] excludeFilters() default {} :排除扫描匹配到的类。

    excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}, // 不扫描指定注解标注的类

    例:

  • ComponentScan.Filter[] includeFilters() default {} :仅扫描匹配到的类,要生效需同时指定 useDefaultFilters = false 。
    includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})}, // 仅扫描指定注解标注的类
    useDefaultFilters = false

    例:

@ComponentScan.Filter 指定过滤规则:

@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class}), // 过滤指定注解标注的类
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserService.class}), // 过滤给定类型的类
@ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {"com.springanno.service.*Service"}), // 通过 ASPECTJ 表达式过滤指定类
@ComponentScan.Filter(type = FilterType.REGEX, pattern ={".*.*Service"}), // 通过正则过滤指定类

还可自定义过滤规则,先自定义一个 TypeFilter 类:

package com.springanno.config;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

public class MyTypeFilter implements TypeFilter {
    /**
     * @param metadataReader 正在扫描的类的信息
     * @param metadataReaderFactory 可获取其它类信息
     * @return 如果返回 true ,说明当前扫描的类匹配成功
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前类注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前类资源
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        return true;
    }
}

com.springanno.config.MyTypeFilter

使用如下:

@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}) // 自定义规则过滤

@Bean-实例

@Bean 注解的作用相当于在 Spring 配置文件中配置 <bean> 标签。

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    /**
     * 默认情况下方法名为 bean 的 id
     * 返回值为加入到 IOC 容器的实例
     * 可通过 @Bean 注解的 value 属性指定 bean 的 id
     *
     *  <bean id="user1" class="com.springanno.pojo.User">
     *    <property name="name" value="张三"/>
     *    <property name="age" value="20"/>
     *  </bean>
     */
    @Bean(value = "user1")
    public User user(){
        return new User("张三", 20);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Object user1 = applicationContext.getBean("user1");
System.out.println(user1);
/*
User{name='张三', age=20}
 */

test

还可以通过 FactoryBean 方式注册:

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.beans.factory.FactoryBean;

public class UserFactoryBean implements FactoryBean<User> {
    /**
     * 返回的对象将会添加到容器
     */
    public User getObject() throws Exception {
        return new User("ice",22);
    }

    /**
     * 对象类型
     */
    public Class<?> getObjectType() {
        return User.class;
    }

    /**
     * 是否单例
     */
    public boolean isSingleton() {
        return true;
    }
}

com.springanno.config.UserFactoryBean

package com.springanno.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean
    public UserFactoryBean userFactoryBean(){
        return new UserFactoryBean();
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}
Object user = applicationContext.getBean("userFactoryBean");
System.out.println(user);
/*
mainConfig
userFactoryBean
User{name='ice', age=22}
 */
// 可以看到,我们获取的是 userFactoryBean,但实际上返回的是 userFactoryBean 对应实例的 getObject 方法的返回值
// 如果我们的确要获取 userFactoryBean 对应的实例,可通过 &id 这种方式获取:
UserFactoryBean userFactoryBean = (UserFactoryBean) applicationContext.getBean("&userFactoryBean");
System.out.println(userFactoryBean);
/*
com.springanno.config.UserFactoryBean@4461c7e3
 */

test

@Scope-单/多例

@Scope 注解作用相当于在 <bean> 标签上的 scope 属性。

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig {
    /**
     * @Scope
     *  value 属性有如下可选值:
     *      singleton(默认): 单例。IOC 容器启动时就会调用方法创建对象放到容器,之后每次使用都是直接从容器中取。
     *      prototype : 多例。只有要使用该对象时才会调用该方法创建对象。
     *      request (web 环境): 一次请求。
     *      session (web 环境): 一次会话。
     */
    @Scope("prototype")
    @Bean
    public User user(){
        return new User("张三", 20);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Object user1 = applicationContext.getBean("user");
Object user2 = applicationContext.getBean("user");
System.out.println(user1==user2);
/*
false
 */

test

@Lazy-懒加载

@Lazy 注解作用相当于在 <bean> 标签上配置属性 lazy-init="true" 。

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class MainConfig {
    /**
     *  懒加载:针对单实例 bean,控制容器启动时不创建对象,第一次获取该 bean 时才创建对象。
     */
    @Lazy
    @Bean
    public User user(){
        System.out.println("创建了");
        return new User("张三", 20);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
/*
[无任何输出]
*/

test

@Conditional-条件注册

按照指定的条件进行判断,满足条件才在容器中注册 bean。

有如下示例,如果当前操作系统为 Windows 时,我们注册一个 id 为 windows 的 bean,当前系统为 Linux 时,注册一个 id 为 linux 的 bean。

1、创建 Condition 类:

package com.springanno.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * 判断是否是 Windows 系统
 */
public class WindowsCondition implements Condition {
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String osName = environment.getProperty("os.name").toLowerCase();
        return osName.contains("windows");
    }
}

com.springanno.condition.WindowsCondition

package com.springanno.condition;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * 判断是否是 Linux 系统
 */
public class LinuxCondition implements Condition {

    /**
     * @param conditionContext      判断条件能使用的上下文
     * @param annotatedTypeMetadata 注解信息
     */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 获取到容器使用的 BeanFactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        // 获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        // 获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        // 获取 bean 定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();

        String osName = environment.getProperty("os.name").toLowerCase();
        return osName.contains("linux");
    }
}

com.springanno.condition.LinuxCondition

2、使用:

package com.springanno.config;

import com.springanno.condition.LinuxCondition;
import com.springanno.condition.WindowsCondition;
import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Conditional({WindowsCondition.class})
    @Bean
    public User windows() {
        return new User("windows", 20);
    }

    @Conditional({LinuxCondition.class})
    @Bean
    public User linux() {
        return new User("linux", 3);
    }
}

com.springanno.config.MainConfig

3、测试:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
    System.out.println(beanName);
}
/*
mainConfig
windows
*/
// 当前是在 windows 下,所以可以看到只注册了 windows bean

test

@Import-快速注册

@Import 提供了下面几种注册 bean 到容器的方式。

@Import

可以直接将指定 bean 实例注册到容器:

@Configuration
/**
 * @Import 可以直接将注册指定 bean 到容器中,id 为 bean 的类全路径名
 */
@Import({User.class})
public class MainConfig {
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

/*
mainConfig
com.springanno.pojo.User
 */

test

@ImportSelector

通过 @ImportSelector 返回 bean 的全路径数组批量注册:

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        List<String> classNameList = new ArrayList<String>();
        classNameList.add(User.class.getName());
        return StringUtils.toStringArray(classNameList);
    }
}

com.springanno.config.MyImportSelector

package com.springanno.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
/**
 * MyImportSelector.selectImports() 方法返回的类的全路径列表,
 * @Import 将会把这些全路径对应的类都注册到容器,id 为类的全路径名
 */
@Import({MyImportSelector.class})
public class MainConfig {
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}
/*
mainConfig
com.springanno.pojo.User
 */

test

@ImportBeanDefinitionRegistrar

通过 @ImportBeanDefinitionRegistrar 手动定义 bean 的信息完成 bean 的注册:

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param annotationMetadata 当前类注解信息
     * @param beanDefinitionRegistry BeanDefinition 注册类
     *
     */
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 判断 IOC 容器中是否已经注册了 user
        boolean isContainsUser = beanDefinitionRegistry.containsBeanDefinition("user");
        // 可以通过 beanDefinitionRegistry.registerBeanDefinition() 方法注册所有需要添加到容器中的 bean
        if(!isContainsUser){
            RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class.getName());
            // id=user
            beanDefinitionRegistry.registerBeanDefinition("user", beanDefinition);
        }
    }
}

com.springanno.config.MyImportBeanDefinitionRegistrar

package com.springanno.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
/**
 * @Import 会执行 MyImportBeanDefinitionRegistrar.registerBeanDefinitions(),在该方法中完成 bean 的注册
 */
@Import({MyImportBeanDefinitionRegistrar.class})
public class MainConfig {
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}
/*
mainConfig
user
 */

test

生命周期

这里说的生命周期指的是注册进 IoC 容器的 bean 的生命周期,可通过下面几种方式来监听 bean 的生命周期变化。

@Bean的initMethod&destroyMethod属性

通过 @Bean 注解的 initMethod 属性来指定 bean 的初始化方法, destroyMethod 属性来指定 bean 的销毁方法。

package com.springanno.pojo;

public class User {
    public User() {
    }

    public User(String name, Integer age) {
        System.out.println("User 构造方法");
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void init(){
        System.out.println("User 初始化方法");
    }

    public void destroy(){
        System.out.println("User 销毁方法");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

com.springanno.pojo.User

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {

    /*
    通过 @Bean 注解的 initMethod 属性来指定 bean 的初始化方法,destroyMethod 属性来指定 bean 的销毁方法

    构造方法:
        单实例 bean 在容器启动的时候执行
        多实例 bean 在每次获取 bean 的时候执行
    初始化方法:
        构造方法执行后执行
    销毁方法:
        单实例 bean 在容器关闭时执行
        多实例时,容器不会管理这个 bean ,所以不会调用销毁方法
     */
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public User user(){
        return new User("tom", 12);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
((AnnotationConfigApplicationContext) applicationContext).close();
/*
User 构造方法
User 初始化方法
User 销毁方法
 */

test

@PostConstruct&@PreDestroy注解

通过将 @PostConstruct 和 @PreDestroy 注解标注在 bean 的方法上让其成为初始化方法和销毁方法。

package com.springanno.pojo;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class User {
    public User() {
    }

    public User(String name, Integer age) {
        System.out.println("User 构造方法");
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @PostConstruct
    public void init() throws Exception {
        System.out.println("User init");
    }

    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("User destroy");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

com.springanno.pojo.User

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {

    @Bean
    public User user(){
        return new User("tom", 12);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
((AnnotationConfigApplicationContext) applicationContext).close();
/*
User 构造方法
User init
User destroy
 */

test

实现InitializingBean&DisposableBean接口

通过让 bean 实现 InitializingBean 来定义初始化逻辑,实现 DisposableBean 来定义销毁逻辑。

package com.springanno.pojo;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class User implements InitializingBean, DisposableBean {
    public User() {
    }

    public User(String name, Integer age) {
        System.out.println("User 构造方法");
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("User afterPropertiesSet");
    }

    public void destroy() throws Exception {
        System.out.println("User destroy");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

com.springanno.pojo.User

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {

    @Bean
    public User user(){
        return new User("tom", 12);
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
((AnnotationConfigApplicationContext) applicationContext).close();
/*
User 构造方法
User afterPropertiesSet
User destroy
 */

test

实现BeanPostProcessor接口

定义一个类实现 BeanPostProcessor 接口,将其注册到容器中后它便可以监听容器中所有 bean 的初始化前和初始化后操作。

package com.springanno.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * 初始化操作执行之前执行
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "---->postProcessBeforeInitialization");
        return bean;
    }

    /**
     * 初始化操作执行之后执行
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "---->postProcessAfterInitialization");
        return bean;
    }
}

com.springanno.config.MyBeanPostProcessor

package com.springanno.pojo;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class User {
    public User() {
    }

    public User(String name, Integer age) {
        System.out.println("User 构造方法");
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @PostConstruct
    public void init() throws Exception {
        System.out.println("User init");
    }

    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("User destroy");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

com.springanno.pojo.User

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
((AnnotationConfigApplicationContext) applicationContext).close();
/*
User 构造方法
user---->postProcessBeforeInitialization
User init
user---->postProcessAfterInitialization
User destroy
 */

test

Spring 底层给 bean 赋值、@Autowried 注入、生命周期注解等功能都是通过 BeanPostProcessor 完成的。

ScheduledAnnotationBeanPostProcessor (org.springframework.scheduling.annotation)
AdvisorAdapterRegistrationManager (org.springframework.aop.framework.adapter)
BeanPostProcessorChecker in PostProcessorRegistrationDelegate (org.springframework.context.support)
ImportAwareBeanPostProcessor in ConfigurationClassPostProcessor (org.springframework.context.annotation)
LoadTimeWeaverAwareProcessor (org.springframework.context.weaving)
AbstractAdvisingBeanPostProcessor (org.springframework.aop.framework)
    AbstractBeanFactoryAwareAdvisingPostProcessor (org.springframework.aop.framework.autoproxy)
DestructionAwareBeanPostProcessor (org.springframework.beans.factory.config)
    InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support)
ApplicationContextAwareProcessor (org.springframework.context.support)
MergedBeanDefinitionPostProcessor (org.springframework.beans.factory.support)
    InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    RequiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support)
BeanValidationPostProcessor (org.springframework.validation.beanvalidation)
InstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config)
    SmartInstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config)
    CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)

BeanPostProcessor的子接口及实现类

组件赋值

@Value&@PropertySource

使用 @Value 可以为属性赋基本数值,也可以通过语法 @Value("#{SpEL}") 通过 SpEL 表达式赋值,还可以通过 ${属性名} 取出配置文件(环境变量)中的值,而 @PropertySource 注解就可以帮我们读取属性文件中的属性到环境变量。

user.testKey=testValue

user.properties

package com.springanno.pojo;

import org.springframework.beans.factory.annotation.Value;

public class User {
    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Value("张三")
    private String name;
    @Value("#{19+3}")
    private Integer age;
    @Value("${user.testKey}")
    private String testKey;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", testKey='" + testKey + '\'' +
                '}';
    }
}

com.springanno.pojo.User

package com.springanno.config;

import com.springanno.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = {"classpath:user.properties"})  // 使用该注解将指定属性文件加载到环境变量
public class MainConfig {
    @Bean
    public User user() {
        return new User();
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User bean = applicationContext.getBean(User.class);
System.out.println(bean);
/*
User{name='张三', age=22, testKey='testValue'}
 */

test

@Profile

通过使用 @Profile 注解,Spring 可以实现根据当前环境动态的激活和切换一些列组件的功能。

package com.springanno.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class MainConfig {
    /*
    指定环境了的组件只有在指定环境下运行时才会注册到容器中,不指定环境的组件任何环境都能注册到容器中,默认为 default 环境
    这里模拟三个环境下的数据源
     */

    @Profile("dev")
    @Bean("dataSource")
    public String devStr(){
        return "devDataSource";
    }

    @Profile("test")
    @Bean("dataSource")
    public String testStr(){
        return "testDataSource";
    }

    @Profile("prod")
    @Bean("dataSource")
    public String prodStr(){
        return "prodDataSource";
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Object dataSource = applicationContext.getBean("dataSource");
System.out.println(dataSource);

// 通过 VM-options 来指定运行环境
// -Dspring.profiles.active=dev 时,输出 devDataSource
// -Dspring.profiles.active=test 时,输出 testDataSource
// -Dspring.profiles.active=prod 时,输出 prodDataSource

test 例:通过 VM-options 来指定运行环境

String profileStr = "test";
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 指定运行环境
applicationContext.getEnvironment().setActiveProfiles(profileStr);
applicationContext.register(MainConfig.class);
applicationContext.refresh();
Object dataSource = applicationContext.getBean("dataSource");
System.out.println(dataSource);

/*
编码指定运行环境
    profileStr="test" 时,输出 testDataSource
    profileStr="prod" 时,输出 prodDataSource
    profileStr="dev" 时,输出 devDataSource
 */

test 例:编码指定运行环境

组件注入

Spring 利用 DI(依赖注入),完成对 IoC 容器中各个组件的依赖赋值。

@Autowired

@Autowired 将会自动从 IoC 容器中查找与标注属性同类型的 bean 注入, 如果容器中有多个相同类型的 bean,将会注入 id 与 属性名相同的那个 bean。

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    /**
     * 默认情况下,如果容器中没有该属性类型对应的 bean,那么将会抛出异常
     * 可通过指定 @Autowired 的属性 required = false 让此次的自动装配不必须,此时容器中如果不存在该类型 bean 就不会抛出异常
     */
    @Autowired(required = false)
    private UserDao userDao;

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
userService.printDao();
/*
com.springanno.service.UserService@60dcc9fe
com.springanno.dao.UserDao@222114ba
 */

test

@Autowired 不仅可以用在属性上,还可以用在方法上:

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;
    /*
    将会从容器中找到与参数类型相同的 bean 传入来执行该方法
     */
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

也可以标注在构造器上:

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;
    /*
    将会从容器中找到与参数类型相同的 bean 传入来执行该构造器来创建 UserService 实例
     */
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

不仅如此,还可以标注在参数上:

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;
    /*
    将会从容器中找到与参数类型相同的 bean 传入来执行该构造器来创建 UserService 实例
     */
    public UserService(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

如果当前组件只有一个有参构造器, @Autowired 可以省略不写:

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;
    /*
    当前组件只有一个有参构造器
    将会从容器中找到与参数类型相同的 bean 传入来自动执行该构造器来创建 UserService 实例
     */
    public UserService (UserDao userDao) {
        this.userDao = userDao;
    }

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

通过 @Bean 标注的方法注册 bean 时,该方法的参数也会默认从容器中获取:

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public UserDao(){}
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import com.springanno.dao.UserDao;
import com.springanno.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

    @Bean("userService2")
    public UserService userService(UserDao userDao){
        UserService userService = new UserService();
        userService.setUserDao(userDao);
        return userService;
    }
}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService2");
UserDao userDao = applicationContext.getBean(UserDao.class);
userService.printDao();
System.out.println(userDao);
/*
com.springanno.dao.UserDao@21507a04
com.springanno.dao.UserDao@21507a04
*/

test

@Qualifier

可以通过 @Qualifier 与 @Autowired 搭配使用来指定此次要装配 bean 的 id,如下:

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public UserDao(){}
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Qualifier("userDao")  // 指定装配 id 为 userDao 的 bean
    @Autowired
    private UserDao userDao;

    public void printDao(){
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
userService.printDao();
/*
com.springanno.service.UserService@60dcc9fe
com.springanno.dao.UserDao@222114ba
 */

test

@Primary

@Primary 也是用来对自动装配进行控制的,他用来指定当容器中存在多个类型相同的 bean 时,自动装配优先装配哪个 bean,和 @Bean 一起使用。当然,它不能和 @Qualifier 同时使用。

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public UserDao(){}
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public void printDao(){
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import com.springanno.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

    @Primary // id 为 userDao2 的 bean 优先装配
    @Bean
    public UserDao userDao2() {
        return new UserDao();
    }

}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
userService.printDao();
UserDao userDao = (UserDao) applicationContext.getBean("userDao2");
System.out.println(userDao);
/*
com.springanno.service.UserService@3b2da18f
com.springanno.dao.UserDao@5906ebcb
com.springanno.dao.UserDao@5906ebcb
 */

test

@Resource

@Resource 注解(JSR250 中定义)相当于 @Autowired 和 @Qualifier 注解一起使用,既能完成自动装配,也能指定要装配 bean 的 id,不支持 @Primary 注解。

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public UserDao(){}
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {

    @Resource(name = "userDao2")
    private UserDao userDao;

    public void printDao(){
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import com.springanno.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

    @Bean
    public UserDao userDao2() {
        return new UserDao();
    }

}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
userService.printDao();
UserDao userDao = (UserDao) applicationContext.getBean("userDao2");
System.out.println(userDao);
/*
com.springanno.service.UserService@2d9d4f9d
com.springanno.dao.UserDao@4034c28c
com.springanno.dao.UserDao@4034c28c
 */

test

@Inject

@Inject 注解(JSR330 中定义)使用与 @Autowired 注解一致,只是没有属性。

package com.springanno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public UserDao(){}
}

com.springanno.dao.UserDao

package com.springanno.service;

import com.springanno.dao.UserDao;
import org.springframework.stereotype.Service;

import javax.inject.Inject;

@Service
public class UserService {

    /*
    与 Autowired 相同,需导包
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>com.springsource.javax.inject</artifactId>
        <version>1.0.0</version>
    </dependency>
     */
    @Inject
    private UserDao userDao;

    public void printDao() {
        System.out.println(userDao);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import com.springanno.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

    @Primary
    @Bean
    public UserDao userDao2() {
        return new UserDao();
    }

}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(userService);
userService.printDao();
UserDao userDao = (UserDao) applicationContext.getBean("userDao2");
System.out.println(userDao);
/*
com.springanno.service.UserService@2aa5fe93
com.springanno.dao.UserDao@5c1a8622
com.springanno.dao.UserDao@5c1a8622
*/

test

@Autowired 是 Spring 定义的,而 @Resource 和 @Inject 是Java 规范中定义的。

Aware接口

如果我们自定义的组件想要使用 Spring 容器底层的一些组件,例如 ApplicationContext、BeanFactory 等,可以让自定义组件实现相应的 Aware 接口,Spring 容器启动时会通过接口的回调给我们注入相应的组件。

package com.springanno.service;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

@Service("testUserServer")
public class UserService implements ApplicationContextAware, BeanNameAware, EnvironmentAware {

    public void setBeanName(String beanName) {
        System.out.println(beanName);
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println(applicationContext.getBean("testUserServer"));
    }

    public void setEnvironment(Environment environment) {
        System.out.println(environment);
    }
}

com.springanno.service.UserService

package com.springanno.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.springanno")
public class MainConfig {

}

com.springanno.config.MainConfig

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
/*
testUserServer
StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}
com.springanno.service.UserService@3f56875e
 */

test

下面是 Spring 提供的继承了 Aware 接口的类:

ApplicationEventPublisherAware (org.springframework.context)
NotificationPublisherAware (org.springframework.jmx.export.notification)
MessageSourceAware (org.springframework.context)
BeanFactoryAware (org.springframework.beans.factory)
EnvironmentAware (org.springframework.context)
EmbeddedValueResolverAware (org.springframework.context)
ResourceLoaderAware (org.springframework.context)
ImportAware (org.springframework.context.annotation)
LoadTimeWeaverAware (org.springframework.context.weaving)
BeanNameAware (org.springframework.beans.factory)
BeanClassLoaderAware (org.springframework.beans.factory)
ApplicationContextAware (org.springframework.context)

extends Aware

AOP

示例

1、创建被代理类:

package com.springanno.service;

public class CalculateService {
    public Integer div(int i,int j){
        return i/j;
    }
}

com.springanno.service.CalculateService

2、编写切面类:

package com.springanno.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

@Aspect
public class LogAspect {

    // 如果要在别的类引用这个切入点表达式,可以使用 com.springanno.aspects.LogAspect.pc()
    @Pointcut(value = "execution(* com.springanno.service.CalculateService.div(..))")
    public void pc() {
    }

    @Before("pc()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("%s 运行了,参数为 %s \n", methodName, Arrays.toString(args));
    }

    @After("pc()")
    public void logEnd(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + " 执行完毕");
    }

    @AfterReturning(value = "pc()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        // 注意,如果使用 JoinPoint 参数,该参数只能在第一个位置
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("%s 运行了,结果为 %s \n", methodName, result);
    }

    @AfterThrowing(value = "pc()",throwing = "ex")
    public void logException(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("%s 出现异常,异常信息为 %s \n", methodName, ex);
    }
}

com.springanno.aspects.LogAspect

3、将被代理类与切面类注册到容器,使用 @EnableAspectJAutoProxy 注解开启切面自动代理功能:

package com.springanno.config;

import com.springanno.aspects.LogAspect;
import com.springanno.service.CalculateService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy // 开启切面自动代理功能
@Configuration
public class MainConfig {
    @Bean
    public CalculateService calculateService(){
        return new CalculateService();
    }

    @Bean
    public LogAspect logAspect(){
        return new LogAspect();
    }
}

com.springanno.config.MainConfig

4、测试:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
CalculateService calculateService = applicationContext.getBean(CalculateService.class);
calculateService.div(4, 1);
/*
div 运行了,参数为 [4, 1]
div 执行完毕
div 运行了,结果为 4
 */

test

updating....

最新文章

  1. Codeforces Round #262 (Div. 2) 1004
  2. Jquery打造的类似新浪微博@提醒功能
  3. RDIFramework.NET ━ 9.3 用户管理 ━ Web部分
  4. 【转】 JAR、WAR、EAR的使用和区别
  5. SQL Server 索引分类
  6. 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:4.安装Oracle RAC FAQ-4.1.系统界面报错Gnome
  7. Android开发之ActivityManager获取系统信息
  8. android和ios流媒体库推荐
  9. cas sso单点登录系列6_cas单点登录防止登出退出后刷新后退ticket失效报500错
  10. dump文件定位程序崩溃代码行
  11. md5 加密 swfit版
  12. phpstudy 安装memcached服务和memcache扩展
  13. nginx正向代理和反正代理区别
  14. 课下必做MyCP
  15. hystrix
  16. TCP/IP 笔记 - 地址解析协议
  17. PL/SQL Developer使用小技巧
  18. javascript将list转换成树状结构
  19. UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0x9c in position 1: ordinal not in range(128)
  20. android操作sqlite数据库及心得

热门文章

  1. 公用表表达式(CTE)
  2. vue2.0引入现有css文件
  3. window10下TensorFlow-gpu环境搭建
  4. Jupyter/JupyterLab安装使用
  5. SQLSERVER性能调优小技巧
  6. 推荐一本写给IT项目经理的好书
  7. redis服务器学习一
  8. springBoot整合ftp上传图片功能
  9. [转]MapReduce:详解Shuffle过程
  10. Galera Cluster mysql+keepalived集群部署