java框架之Spring(5)-注解驱动开发

准备

一、使用 maven 建立一个 java 项目,依赖以下: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

二、建立测试 pojo:linux

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 配置文件。web

一、建立配置类,经过 @Configuration 注解标注一个类为配置类:spring

package com.springanno.config;

import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 */
@Configuration
public class MainConfig {
}
com.springanno.config.MainConfig

二、经过读取注解配置类来建立 IOC 容器:windows

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
test

@ComponentScan-包扫描

@ComponentScan 注解的做用至关于在 Spring 配置文件中配置 <context:component-scan> 标签。数组

一、建立测试 Service 类:session

package com.springanno.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
}
com.springanno.service.UserService

二、使用 @ComponentScan 注解:app

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

三、测试:maven

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

@ComponentScan 相关属性:ide

  • 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。

一、建立 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

二、使用:

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

三、测试:

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

示例

一、建立被代理类:

package com.springanno.service;

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

二、编写切面类:

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

三、将被代理类与切面类注册到容器,使用 @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

四、测试:

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

updating....

相关文章
相关标签/搜索