1. 程式人生 > 其它 >Spring學習日記(一)

Spring學習日記(一)

技術標籤:SSMspringjavaiocbean

Spring學習日記(一)----IoC

一、IoC容器簡介

Spring IoC容器是一個管理Bean的容器。在Spring的定義中,所有的IoC容器都需要實現介面BeanFactory。

下面是BeanFactory原始碼:

public interface BeanFactory {
    //字首
    String FACTORY_BEAN_PREFIX = "&";

    //多個getBean方法
    Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException; Object getBean(String var1, Object... var2) throws BeansException; <T> T getBean(Class<T> var1) throws BeansException; <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
//是否包含Bean boolean containsBean(String var1); //是否是單例 boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; //是否是原型 boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; //是否型別匹配 boolean isTypeMatch(String var1, ResolvableType var2) throws
NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException; //獲取Bean型別 Class<?> getType(String var1) throws NoSuchBeanDefinitionException; //獲取Bean別名 String[] getAliases(Str ing var1); }

多個getBean方法,用於按型別、名稱等獲取Bean;

isSingleton方法判斷Bean是否單例,預設情況Bean都是單例的;

isPrototype方法與isSingleton方法相反,如果isPrototype結果為true,使用getBean時,IoC容器會返回一個新的Bean給呼叫者。

二、裝配Bean

Spring可以使用XML或Java配置檔案來裝配Bean,或者使用註解掃描裝配

1、通過配置檔案

首先,我們定義一個POJO

public class Student {
    private Long id;
    private String name;

    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }
	
	//get、set方法省略
	
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

然後,在bean.xml檔案中裝配它

<bean id="student" class="XMLComponent.Student">
        <constructor-arg name="id" value="1001"></constructor-arg>
        <constructor-arg name="name" value="xml_student"></constructor-arg>
</bean>

接下來,就可以通過getBean獲取這個Bean

public class XMLComponent {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Student student = (Student)context.getBean("student");
        System.out.println(student);
    }
}

剛剛獲取的Bean:

image-20210116182545032

除了通過xml配置檔案可以裝配bean,通過Java配置檔案(使用@Configuration註解)也可以裝配Bean:

我們定義一個Java配置檔案:

@Configuration
public class StudentConfig {
    @Bean(name="java_config_bean")
    public Student init(){
        Student student = new Student();
        student.setId(10003L);
        student.setName("java_config_name");
        return student;
    }
}

使用@Configuration註解表明類StudentConfig是一個配置檔案,@Bean註解表示init方法將返回POJO裝配到IoC容器中,name屬性為Bean的名字(為配置將使用方法名init作為Bean的名字)

下面,我們使用getBean方法獲取這個bean

public class JavaConfigComponent {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(StudentConfig.class);
        Student student = (Student)ac.getBean("java_config_bean");
        System.out.println(student);
    }
}

成功獲取到Bean:

image-20210116183748123

2、通過註解掃描

一個個裝配Bean太麻煩,我們可以使用@Component@ComponentScan註解進行掃描裝配

註解@Component標明將被IoC容器掃描裝配的類,註解中value的值為Bean的名稱,如果未註明value,IoC容器將會將類名首字母小寫後的類名作為Bean名稱。

@Component("teacher")
public class Teacher {
    @Value("1234")
    private Long id;
    @Value("annotationScan")
    private String name;

    //省略get、set方法
    
    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

指定Bean,還需要指定何種策略去掃描裝配Bean,為了讓IoC容器裝配該類,需要改造類AppConfig,使用註解@ComponentScan對當前包和子包進行掃描:

@Configuration
@ComponentScan
public class AppConfig {
}

這樣,就可以使用getBean方法來獲取裝配好的Bean:

public class AnnotationScan {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        Teacher teacher = (Teacher)ac.getBean("teacher");
        System.out.println(teacher);
    }
}

測試一下,成功的獲取到了裝配的Bean:

image-20210116225939868

註解@ComponentScan可以自定義掃描規則:

定義掃描的包名:

@ComponentScan( basePackages = {"cn.codeaper.*"})

定義掃描的類:

@ComponentScan( basePackageClass = {User.class})

此外,還可以加上註解@Filter定義過濾條件

使用includeFilters定義滿足過濾器條件才掃描:

@ComponentScan(basePackages = "AnnotationScan",includeFilters = {@ComponentScan.Filter(classes = Service.class)})

使用exculdeFilters定義不掃描滿足過濾條件的Bean:

@ComponentScan(basePackages = "AnnotationScan",excludeFilters = {@ComponentScan.Filter(classes = Service.class)})

注:在excludedFilters中的class屬性指定的是Controller,Service之類的,不是具體的類。

三、依賴注入

1、註解@Autowired

有時候,Bean之間會有依賴關係,比如,人類(Person)使用餐具(Tableware)吃飯,一個Bean會使用到其它Bean,在Spring IoC容器中,稱為依賴注入(DI)。

人類(Person):

@Component("person")
public class Person {

    @Autowired
    private Tableware tableware;

    public void eat(){
        this.tableware.use();
    }

    public void setTableware(Tableware tableware){
        this.tableware = tableware;
    }
}

餐具(Tableware)介面:

public interface Tableware {
    public void use();
}

筷子(Chopsticks)實現類:

@Component("chopsticks")
public class Chopsticks implements Tableware{
    public void use() {
        System.out.println("使用筷子夾菜...");
    }
}

在人類介面中,使用註解@Autowired將餐具(Tableware)例項注入,註解@Autowired根據屬性的型別找到對應的Bean進行注入,

下面我們測試一下:

@Configuration
@ComponentScan
public class AppConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        Person person = ac.getBean(Person.class);
        person.eat();
    }
}

測試成功,我們成功的在人類(Person)呼叫了tablewareuse方法,說明餐具(Tableware)例項成功注入:

image-20210117122219290

如果此時,再為餐具(Tableware)新增一個實現類勺子(Spoon):

@Component("spoon")
public class Spoon implements Tableware{
    public void use() {
        System.out.println("使用勺子舀湯...");
    }
}

那麼IoC容器將會丟擲異常:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'DI.Tableware' available: expected single matching bean but found 2: chopsticks,spoon
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
	... 14 more

因為找到了兩個型別為Tableware的bean。

註解@Autowired首先根據型別尋找,如果找到的Bean不是唯一的,將根據屬性名稱和Bean的名稱進行匹配,如果依然不是唯一的,那麼將會丟擲異常。

2、消除歧義

對類使用註解@Primary,該註解將會告訴IoC容器,發現多個Bean時優先選擇被這個註解標註的bean。

為勺子(Spoon)添加註解:

@Primary
@Component("spoon")
public class Spoon implements Tableware{
    public void use() {
        System.out.println("使用勺子舀湯...");
    }
}

IoC將會優先將它注入:

image-20210117135118978

使用註解@Qualifier也可以消除歧義,註解@Qualifier與註解@Autowired結合使用,按照型別和名稱譯器尋找bean注入:

image-20210117135820674

name為chopsticks型別為Tableware的Bean被注入:

image-20210117135928647

四、使用屬性檔案

在開發應用程式時,經常需要讀取配置檔案。最常用的配置方法是以key=value的形式寫在.properties檔案中。

Spring容器還提供了一個更簡單的@PropertySource來自動讀取配置檔案。我們只需要在@Configuration配置類上再新增一個註解:

@Component("datasource")
@PropertySource("mysql.properties") // 表示讀取classpath的app.properties
public class MysqlConfig {

    @Value("${port:3306}")
    private int port;

    @Value("${database.username}")
    private String username;

    @Value("${database.password}")
    private String password;

	//get、set方法省略

    @Override
    public String toString() {
        return "MysqlConfig{" +
                "port=" + port +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

}

${port:3306}表示使用port的值,如果沒有,九使用預設值3306。

測試一下,成功的讀取到值:

image-20210117143857818

ord;

//get、set方法省略

@Override
public String toString() {
    return "MysqlConfig{" +
            "port=" + port +
            ", username='" + username + '\'' +
            ", password='" + password + '\'' +
            '}';
}

}


`${port:3306}`表示使用port的值,如果沒有,九使用預設值3306。

測試一下,成功的讀取到值:

![image-20210117143857818](https://img.796t.com/res/2021/02-03/06/3ca7f38b491e07d209d3cd813a301282.png)

![image-20210117143843740](https://img-blog.csdnimg.cn/img_convert/63ac570c80eeac6622b99cae8604d90d.png)



原文連結:[新增連結描述](http://www.codeaper.cn/blog/QZLMRsqblKk3)