Spring IOC 知識點彙總

@Configuration配置spring並啓動spring容器

@Configuration用於定義配置類,標註在類上,至關於把該類做爲spring的xml配置文件中的<beans>,做用是用於配置spring容器(應用上下文)css

實例說明:html

配置類java

@Configuration
public class MyConfig {
    public MyConfig() {
        System.out.println("TestConfig容器初始化!!!!");
    }
}
複製代碼

至關於xmlmysql

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
複製代碼

測試類web

public class TestConfig {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfig.class);
    }
}
複製代碼

運行結果spring

TestConfig容器初始化!!!!sql

@Configuration&@Bean給容器中註冊組件

@Bean標註在方法上,至關於xml配置配置中的</bean>編程

代碼實例說明:後端

註冊類數組

public class ConfigurationBean {
    public void start({
        System.out.println("容器初始化開始!!!!!");
    }
}
複製代碼

配置類

@Configuration
public class MyConfiguration {
    //能夠指定bean 的名稱,也能夠指定注入容器以前加載的方法
    @Bean()
     public ConfigurationBean configurationBean(){
         return new ConfigurationBean();
     }

}
複製代碼

至關於xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="configurationBean" class="zfcoding.bean.ConfigurationBean" ></bean> 
</beans>
複製代碼

測試方法

@Test
    public void ConfigurationTest()
{
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
        ConfigurationBean configurationBean=(ConfigurationBean) applicationContext.getBean("configurationBean");
        System.out.println(configurationBean);
    }
複製代碼

運行結果:

容器初始化開始!!!!!
zfcoding.bean.ConfigurationBean@77be656f

說明幾點:

(1)、@Bean註解標註在方法上,若是未經過@Bean指定bean的名稱,則默認與標註的方法名相同;

(2)、@Bean註解標註在方法上,若是想經過@Bean指定bean的名稱,例如 @Bean("configurationBean1"),那麼容器中實例化bean的名稱爲configurationBean1;

@Bean 指定初始化和銷燬的方法及@Scope ,@Lazy

容器bean管理的生命週期:

咱們能夠自定義初始化和銷燬的方法,容器在bean進行到當前生命週期的時候來調用咱們自定義的初始化和銷燬的方法。

@Bean 中指定初始化和銷燬的方法。

@Bean(initMethod = "init",destroyMethod = "destory")

xml

<bean id="student" class="zfcoding.bean.Student"  init-method="init" destroy-method="destory"></bean>
複製代碼

@Scope

默認狀況下: singleton 單實例(默認值),ioc容器啓動會調用方法建立對象放到ioc容器當中。之後每次獲取直接獲取對象。

prototype :多實例,ioc容器啓動並不會調用方法建立對象放到容器當中。,而是每次獲取的時候纔會調用方法建立對象,調用一次建立一次。

@Lazy 懶加載,

針對單實例bean,默認在容器啓動的時候建立對象

懶加載:容器啓動不創鍵對象,第一次使用Bean建立對象,並初始化。

實例說明:

public class Student {

    public Student({
        System.out.println("Student容器加載開始!!!!");
    }
    //對象建立完成,並賦值好,調用初始化方法......
    public void init({
        System.out.println("Student.init()初始化開始.....");
    }
    //銷燬針對(單實例),容器關閉的時候調用。
    //多實例(@Scope("prototype")):容器不會管理這個bean,容器也不回調用銷燬的方法。
    public void destory({
        System.out.println("Student.destory().........");
    }
}
複製代碼
@Configuration
public class MyConfigStudent {
    //@Lazy
    //@Scope("prototype")
    @Bean(value = "student",initMethod = "init",destroyMethod = "destory")
    public Student student(){
        return new Student();
    }
}
複製代碼

測試類一

public class MyConfigStudentTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
    }
}
複製代碼

運行結果

Student容器加載開始!!!!
Student.init()初始化開始…..

那麼說明,ioc容器啓動會調用方法建立對象放到ioc容器當中。之後每次獲取直接獲取對象。

若是咱們使用@Lazy註解(單實例),容器啓動不創鍵對象,測試結果沒有輸出信息。

測試類二,咱們第一次使用Bean建立對象,並初始化

public class MyConfigStudentTest {
    @Test
    public void test()
{
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
        Student student = (Student)applicationContext.getBean("student");
        System.out.println(student);
        applicationContext.close();
    }
}
複製代碼

運行結果

Student容器加載開始!!!!
Student.init()初始化開始.....
zfcoding.bean.Student@7364985f
Student.destory().........
複製代碼

說明懶加載(@Lazy):當容器啓動不創鍵對象,而是第一次使用Bean建立對象,並初始化。

InitializingBean 和DisposableBean

InitializingBean接口爲bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是繼承該接口的類,在初始化bean的時候都會執行該方法。

測試類

public class TestInitializingBean implements InitializingBean {
    public void afterPropertiesSet() throws Exception {
        System.out.println("TestInitializingBean.afterPropertiesSet()......");
    }

    public void init() {
        System.out.println("TestInitializingBean.init().......");
    }
}
複製代碼

配置類

@Configuration
public class MyInitializingBeanConfig {
    @Bean(value = "testInitializingBean",initMethod = "init")
    public TestInitializingBean testInitializingBean(){
        return new TestInitializingBean();
    }
}
複製代碼

測試類

public class TestnitializingBeanConfig {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyInitializingBeanConfig.class);
    }
}
複製代碼

運行結果:

TestInitializingBean.afterPropertiesSet()……
TestInitializingBean.init()…….

從結果能夠看出,在Spring初始化bean的時候,若是該bean實現了InitializingBean接口,而且同時在配置文件中指定了init-method,系統則是先調用afterPropertieSet()方法,而後再調用init-method中指定的方法。

那麼這種方式在spring中是怎麼實現的呢,經過查看Spring加載bean的源碼類AbstractAutowiredCapableBeanFactory類中的invokeInitMethods(),以下:

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable 
{
       判斷該bean是否實現了實現了InitializingBean接口,若是實現了InitializingBean接口,則只掉調用bean的afterPropertiesSet方法
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
複製代碼

總結:

Spring爲bean提供了兩種初始化bean的方式,一種是實現InitializingBean接口,重寫afterPropertiesSet方法,另外一種四在配置文件中經過init-method指定,可是若是同時指定的話是先調用InitializingBean實現的方法。

@PostConstruct和@PreDestroy

能夠使用JS250,@PostConstruct 標註在方法上面,bean建立完成而且屬性賦值完成,來執行初始化方法

@PreDestroy ,在容器銷燬bean以前通知咱們進行清理工做.

@ComponentScan&@Configuration給容器註冊組件

@Configuration+@ComponentScan 注入組件, 只是把標註了註解爲@Controller ,@Service, @Repository, @Component 加入到Spring容器中.

xml 方式的配置

<context:component-scan base-package="zfcoding"></context:component-scan>
複製代碼

代碼實例說明:

須要注入的類

@Component
public class PersonComponent {
}
@Repository
public class PersonDao {
}
@Service
public class PersonService {
}
@Controller
public class PersonController {
}
複製代碼

配置類

@Configuration
//@ComponentScan(basePackages = "zfcoding",
////        includeFilters =@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters=false )
@ComponentScan(basePackages = "zfcoding",
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}))
public class MyCompoentScan {

}
複製代碼

測試的方法

 @Test
    public void ComponentSanTest(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyCompoentScan.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String definitionName:definitionNames){
            System.out.println(definitionName);
        }
    }
複製代碼

運行結果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myCompoentScan
personComponent
personDao
複製代碼

說明:

excludeFilters 排除不須要加載的組件

includeFilters 只包含須要加載的組件,使用的時候須要把 useDefaultFilters=false,才能生效

@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}),指定的過濾規則,排除註解標註是@Controller和@Service 類。

@Conditional-設置組件做用域

按照必定的條件進行判斷,知足條件給容器中註冊bean

自定義Conditional條件須要編寫一個類,這個類須要實現Condition接口,咱們直接使用就@Conditional({WindowsEnvironment.class})

@Conditional既能夠定義在方法上,也能夠定義在類上,代碼實現自定義條件須要實現Condition接口

條件類

public class WindowsEnvironment implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        BeanDefinitionRegistry registry = context.getRegistry();
        if(property.contains("Windows 10")){
           return true;
        }
        return false;
    }
}
複製代碼
public class LiunxEnvironment implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        if (environment.containsProperty("Liunx")){
            return true;
        }
        return false;
    }
}
複製代碼

配置類

@Configuration
public class MyCondition {

    @Conditional({WindowsEnvironment.class})
    @Bean
    public ConfigurationBean configurationBean(){
        return new ConfigurationBean("張三");
    }
    @@Conditional({LiunxEnvironment.class})
    @Bean
    public ConfigurationBean configurationBean1(){
        return new ConfigurationBean("李四");
    }
}
複製代碼

測試方法

public class CoditionTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyCondition.class);
        String[] beanNamesForType = applicationContext.getBeanNamesForType(ConfigurationBean.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }
}
複製代碼

運行結果

configurationBean

@Import 給容器快速導入一個組件

一、@Import(要導入的組件),容器就會自動註冊這個組件,id默認是全類名。

二、ImportSelector , 自定義邏輯返回要導入的組件 編寫的類學要實現 ImportSelector 接口,返回須要導入的主鍵的全類名數組。

三、ImportBeanDefinitionRegistrar,自定義邏輯須要編寫類實現ImportBeanDefinitionRegistrar接口,手動註冊bean到容器當中。

代碼實例

public class Red {
}
public class Blue {
}
複製代碼
//
@Import({Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyImport {

}
複製代碼
public class MyImportSelector implements ImportSelector {

    //AnnotationMetadata :當前標註@Import 註解類的全部註解信息
    //返回類的全類名
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"zfcoding.bean.Blue"};
    }
}
複製代碼
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
     //importingClassMetadata:當前類的註解信息
    //BeanDefinitionRegistry  BeanDefinition註冊類,把全部須要添加到容器中的bean,BeanDefinitionRegistry.registerBeanDefinition手工註冊

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("zfcoding.bean.Red");
        if (red){
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class);
            registry.registerBeanDefinition("blue",rootBeanDefinition);
        }

    }
}
複製代碼

測試類

public class ImportTest {
    @Test
    public void test()
{
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyImport.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String s:beanDefinitionNames){
            System.out.println(s);
        }
    }
}
複製代碼

運行結果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactorymyImport
zfcoding.bean.Red
zfcoding.bean.Blue
blue

總結:

給容器中導入組件:

​ 一、@Configuration&@Bean給容器中註冊組件

​ 二、@ComponentScan&@Configuration(@Controller ,@Service, @Repository, @Component)

​ 三、@Import 快速的給容器中導入一個組件

@Value賦值 和@PropertySource 加載外部配置文件

@Value

​ 一、基本數值

​ 二、SPEL #{}

​ 三、 能夠寫

使用@PropertySource去取外部配置文件中的值保存到運行的環境當中。加載完尾部的配置文件以後,使用{}取出配置文件中的值.

代碼實例:

代碼省略setter(),getter(),toString()方法。

public class Person {
    @Value("張三")
    private String name;
    @Value("#{20-1}")
    private Integer age;
    @Value("${person.sex}")
    private String sex;
}
複製代碼
//<context:property-placeholder location="person.properties"></context:property-placeholder>
@PropertySource("classpath:person.properties")
@Configuration
public class MyConfigValue {
      @Bean
      public Person person(){
          return new Person();
      }
}
複製代碼

person.properties

person.sex=1
複製代碼

測試類

@Test
    public void test1()
{
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigValue.class);
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }
複製代碼

運行結果

Person{name='張三', age=19, sex='1'}
複製代碼

@Autowired &@Qualifier&@Primary

自動裝配:

@Autowired自動注入,單個默認優先按照類型去容器中找到對應的組件,默認必定要將屬性賦值好,沒有找到就會報錯,能夠使用@Autowired (required=false),非必須的 ;

@Qualifier ,指定須要裝配的組件id ;

@Primary 讓Spring 進行自動裝配的時候, 首選裝配的組件(配置類中),也能夠使用@Qualifier指定須要裝配的bean的名字.

@Resource &@Inject

Spring還支持@Resource(JSR250)和@Inject (JSR330){java規範的註解}

@Resource 和@Autowired同樣實現自動裝配的功能,默認按照組件的名稱進行裝配。

@Inject須要導入javax.inject的包,和@Autowired功能同樣,沒有required=false的功能。

@Profile 環境搭建

Spring 爲咱們提供能夠根據當前環境,動態的激活和切換一系列組件的功能

下面咱們來使用數據源說明這個問題:

@Profile指定哪一個環境下才能被註冊到容器當中,加了@Profile,只有這個環境激活的時候才能註冊到spring容器當中,默認是defalut

代碼實例:

@PropertySource("classpath:db.properties")
@Configuration
public class MyConfigProfile  implements EmbeddedValueResolverAware {
    @Value("${db.username}")
    private String username;
    @Value("${db.password=}")
    private String password;

    private StringValueResolver stringValueResolver;

    private String driveClass;

    @Profile("default")
    @Bean("testDataSource")
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser(username);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
        dataSource.setDriverClass(driveClass);
        return dataSource;
    }
    @Profile("dev")
    @Bean("devDataSource")
    public DataSource devDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser(username);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
        dataSource.setDriverClass(driveClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.stringValueResolver=resolver;
        driveClass = stringValueResolver.resolveStringValue("db.driverClass");
    }
}
複製代碼

db.properties

db.username=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver
複製代碼

測試代碼:

public class DataSourceProfileTest {
    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigProfile.class);
        String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }

    /* 使用無參構造器
     * 設置激活的環境
     * 註冊主配置類
     * 啓動刷新容器
     */

    @Test
    public void test1(){
        //使用無參構造器 
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
        //設置激活的環境
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //註冊主配置類
        applicationContext.register(MyConfigProfile.class);
        //啓動刷新容器
        applicationContext.refresh();
        String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }
}
複製代碼

測試案例運行結果

testDataSource

devDataSource

小結:

@Profile還能夠標記在類上,表示只有指定環境的時候,整個配置文件裏面的配置才能開始生效。沒有標註環境表示的bean,在任何環境下都是加載的(前提是配置類是生效的),還能夠經過命令行參數:-Dspring.profiles.active=test激活對應的環境。

我是阿福,公衆號「阿福聊編程」做者,對後端技術保持學習愛好者,我會常常更新JAVA技術文章,在進階的路上,共勉!

歡迎你們關注個人公衆號,後臺回覆666,領取福利,你懂的。

相關文章
相關標籤/搜索