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....

相關文章
相關標籤/搜索