spring2

Spring原理java

組件註冊@Configuration和@Bean

配置類mysql

package com.great.config;

import com.great.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScans;
import com.great.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;

//配置類==配置文件
@Configuration  //告訴Spring這是一個配置類
//@ComponentScans(
//        value = {
//                @ComponentScan(value="com.great",includeFilters = {
//                            //按照註解
////                            @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
//                            @Filter(type=FilterType.ANNOTATION,classes={Repository.class}),
////                            //按照給定的類型
////                            @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
//                            //使用自定義規則。由於上面的value="com.great"全部該包下的全部的類都會進入這個自定義的規則裏面進行匹配。
//                            @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
//                },useDefaultFilters = false)
//        }
//)
//@ComponentScan  value:指定要掃描的包
//excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件
//includeFilters = Filter[] :指定掃描的時候只須要包含哪些組件
//FilterType.ANNOTATION:按照註解
//FilterType.ASSIGNABLE_TYPE:按照給定的類型;
//FilterType.ASPECTJ:使用ASPECTJ表達式  (這個基本不用)
//FilterType.REGEX:使用正則指定
//FilterType.CUSTOM:使用自定義規則

//@ComponentScan(value="com.great")
public class MainConfig {

    //給容器中註冊一個Bean;類型爲返回值的類型,id默認是用方法名做爲id,這裏咱們指定的id是person
    @Bean("person")
    public Person person01(){
        return new Person("lisi", 20);
    }

}

測試一下linux

package com.great;

import com.great.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.bean.Person;

public class MainTest {
    
    public static void main(String[] args) {
                                                   //AnnotationConfigApplicationContext註解式的ApplicationContext
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);

        //獲取ioc容器裏面的Person類型的全部bean的名字
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : namesForType) {
            System.out.println(name);
        }
    
    }

}

運行結果spring

Person [name=張三, age=18, nickName=${person.nickName}]
person

分割線
---

---sql

組件註冊@Configuration和@ComponentScans自動掃描組件和指定掃描啊規則

在這裏插入圖片描述
咱們在看MyTypeFilter.class編程

package com.great.config;

import java.io.IOException;
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;

public class MyTypeFilter implements TypeFilter {

    /**
     * metadataReader:讀取到的當前正在掃描的類的信息
     * metadataReaderFactory:能夠獲取到其餘任何類信息的
     */
    @Override
    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);
        //若是className包含er就匹配成功,也就掃描進IOC容器裏面了。
        if(className.contains("er")){
            return true;
        }
        return false;
    }

}

做用域

懶加載只針對單實例bean
在這裏插入圖片描述
在這裏插入圖片描述windows

分割線


註解@Conditional

在配置類中進行配置,以下圖app

在這裏插入圖片描述

package com.great.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 {

    /**
     * ConditionContext:判斷條件能使用的上下文(環境)
     * AnnotatedTypeMetadata:註釋信息
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 是否爲linux系統
        //一、能獲取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //二、獲取類加載器
        ClassLoader classLoader = context.getClassLoader();
        //三、獲取當前環境信息
        Environment environment = context.getEnvironment();
        //四、獲取到bean定義的註冊類
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");
        //能夠判斷容器中的bean註冊狀況,也能夠給容器中註冊bean。
        boolean definition = registry.containsBeanDefinition("person");
        if(property.contains("linux")){
            return true;
        }
        return false;
    }

}
package com.great.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 {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }

}

測試一下
在這裏插入圖片描述
運行結果
在這裏插入圖片描述
注意註解Conditional也能夠放在類上面的ide

在這裏插入圖片描述

@Import註解

該註解是給容器中註冊組件
在這裏插入圖片描述post

第一種import

能夠直接用import導

在這裏插入圖片描述

第二種import

@Configuration
//@Import(Color.class)
//@Import導入組件,id默認是組件的全類名
@Import({Color.class,MyImportSelector.class})
public class MainConfig2 {
package com.great.condition;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

//自定義邏輯返回須要導入的組件
public class MyImportSelector implements ImportSelector {

    //返回值,就是到導入到容器中的組件全類名
    //AnnotationMetadata:當前標註@Import註解的類的全部註解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //importingClassMetadata
        //方法不要返回null值,要否則報空指針異常
        //這樣就把實體類Blue,Yellow掃描進容器裏面了。
        return new String[]{"com.great.bean.Blue","com.great.bean.Black"};
    }

}

這樣就把Bule和Black加載到容器裏面了

第三種import

在這裏插入圖片描述

package com.great.condition;

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;
import com.great.bean.RainBow;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata:當前類的註解信息
     * BeanDefinitionRegistry:BeanDefinition註冊類;
     *      把全部須要添加到容器中的bean;調用
     *      BeanDefinitionRegistry.registerBeanDefinition手工註冊進來
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //判斷是否有Black和Blue的bean
        boolean definition = registry.containsBeanDefinition("com.great.bean.Black");
        boolean definition2 = registry.containsBeanDefinition("com.great.bean.Blue");
        if(definition && definition2){
            //指定Bean定義信息;(Bean的類型,Bean。。。)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //註冊一個Bean,指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }

}

運行結果就是把RainBow註冊進容器裏了,bean的名字是rainBow

給容器中註冊組件的方式

在這裏插入圖片描述
如上圖所示有四種方式,上面咱們介紹完了import的方式,下面咱們看FactoryBean的方式
在配置類裏面加下面的代碼

在這裏插入圖片描述

package com.great.bean;

import org.springframework.beans.factory.FactoryBean;

//建立一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一個Color對象,這個對象會添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    //是單例?
    //true:這個bean是單實例,在容器中保存一份
    //false:多實例,每次獲取都會建立一個新的bean;
    @Override
    public boolean isSingleton() {
        return false;
    }

}

測試一下
在這裏插入圖片描述

bean的生命週期

> 這裏是引用
在這裏插入圖片描述

指定指定初始化和銷燬方法的幾種方式

在這裏插入圖片描述
咱們先看第一種@Bean的方式

@Configuration
public class MainConfigOfLifeCycle {

//    @Scope("prototype") //多實例:容器不會管理這個bean;容器不會調用銷燬方法。
    @Bean(initMethod="init",destroyMethod="detory")
    public Car car(){
        return new Car();
    }

}
package com.great.bean;

import org.springframework.stereotype.Component;

@Component
public class Car {

    public Car(){
        System.out.println("car constructor...");
    }

    public void init(){
        System.out.println("car ... init...");
    }

    public void detory(){
        System.out.println("car ... detory...");
    }

}

測試

package com.great.test;

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.config.MainConfigOfLifeCycle;

public class IOCTest_LifeCycle {

    @Test
    public void test01(){
        //一、建立ioc容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器建立完成...");

//        applicationContext.getBean("car");
        //關閉容器
        applicationContext.close();
    }

}

構造(對象建立):
單實例:在容器啓動的時候建立對象
多實例:在每次獲取的時候建立對象
在這裏插入圖片描述
在這裏插入圖片描述
第二種方式
在這裏插入圖片描述
在這裏插入圖片描述
第三種方式
加兩個註解就能夠了
在這裏插入圖片描述
在這裏插入圖片描述
第四種方式
在這裏插入圖片描述
這裏使用的BBP的原理也就是BeanPostProcessor bean的後置處理器

在這裏插入圖片描述
容器裏面的全部的bean都是要走這個咱們自定義的MyBeanPostProcessor
在這裏插入圖片描述

BeanPostProcessor的原理

仍是上面咱們自定義的
在這裏插入圖片描述
斷點進來

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

BeanPostProcessor原理深挖

在這裏插入圖片描述
這裏咱們看一下這個ApplicationContextAwareProcessor
在這裏插入圖片描述
在這裏插入圖片描述

這樣咱們自定義一個類來試一下
在這裏插入圖片描述

屬性賦值 @Value註解

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
運行結果
在這裏插入圖片描述

自動裝配

@Autowired&@Qualifier&@Primary

在這裏插入圖片描述
在這裏插入圖片描述

自動裝配-@Resource&@Inject

在這裏插入圖片描述
在這裏插入圖片描述

@Autowired不只能標註在屬性位置

1 . 標註在方法參數上面 @Bean+方法參數
在這裏插入圖片描述
2.放在構造器上面和放在方法上面
在這裏插入圖片描述
總結:

在這裏插入圖片描述

分割線

自定義組件想要使用Spring容器底層的一些組件

在這裏插入圖片描述

新建一個red類

package com.great.bean;

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.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("傳入的ioc:"+applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("當前bean的名字:"+name);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
       //解析字符串
        String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
        System.out.println("解析的字符串:"+resolveStringValue);
    }

}

這樣就完成自定義組件想要使用Spring容器底層的一些組件

咱們來看一下原理
以ApplicationContextAware接口爲列例子
在這裏插入圖片描述
咱們在Red類裏面的setApplicationContext上面打斷點
咱們debug進去
在這裏插入圖片描述
能夠看出來是ApplicationContextAwareProcessor和BeanPostProcessor在起做用

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

原理總結:

咱們的bean在初始化的時候,利用後置處理器,判斷這個bean是否實現某個aware接口,而後調用相應的方法把組件傳過來

取配置文件裏面的常量值

在這裏插入圖片描述

@Profile註解

package com.great.config;


import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import com.great.bean.Yellow;
import com.mchange.v2.c3p0.ComboPooledDataSource;


/**
 * Profile:
 *      Spring爲咱們提供的能夠根據當前環境,動態的激活和切換一系列組件的功能;
 *
 * 開發環境、測試環境、生產環境;
 * 數據源:(/A)(/B)(/C);
 *
 *
 * @Profile:指定組件在哪一個環境的狀況下才能被註冊到容器中,不指定,任何環境下都能註冊這個組件
 *
 *      1)、加了環境標識的bean,只有這個環境被激活的時候才能註冊到容器中。默認是default環境
 *      2)、寫在配置類上,只有是指定的環境的時候,整個配置類裏面的全部配置才能開始生效
 *      3)、沒有標註環境標識的bean在,任何環境下都是加載的;
 */

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{

    @Value("${db.user}")
    private String user;

    //實現EmbeddedValueResolverAware接口把咱們的值解析器引進來
    //這也是一種方式用於解析值
    private StringValueResolver valueResolver;

    private String  driverClass;

    @Bean
    public Yellow yellow(){
        return new Yellow();
    }

    //默認是default
//    @Profile("default")
    @Profile("test")
    @Bean("testDataSource")         //@Value註解也能夠直接寫在參數上面
    public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }


    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }

}
package com.great.test;

import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.bean.Yellow;
import com.great.config.MainConfigOfProfile;

public class IOCTest_Profile {

    //一、使用命令行動態參數: 在虛擬機參數位置加載 -Dspring.profiles.active=test
    //二、代碼的方式激活某種環境;
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext =
                //這裏用無參構造,目的是後面設置值
                new AnnotationConfigApplicationContext();
        //一、建立一個applicationContext
        //二、設置須要激活的環境
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //三、註冊主配置類
        applicationContext.register(MainConfigOfProfile.class);
        //四、啓動刷新容器
        applicationContext.refresh();

        String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String string : namesForType) {
            System.out.println(string);
        }

        Yellow bean = applicationContext.getBean(Yellow.class);
        System.out.println(bean);
        applicationContext.close();
    }

}

AOP功能測試

在這裏插入圖片描述
【動態代理】

  • 指在程序運行期間動態的將某段代碼切入到指定方法指定位置進行運行的編程方式;

建一個目標類
在這裏插入圖片描述
而後是切面類
在這裏插入圖片描述
而後是配置類
在這裏插入圖片描述
測試一下
在這裏插入圖片描述
沒有異常的時候運行結果以下

div運行。。。@Before:參數列表是:{[1, 1]}
MathCalculator...div...
div結束。。。@After
div正常返回。。。@AfterReturning:運行結果:{1}

有異常的時候運行結果以下

div運行。。。@Before:參數列表是:{[1, 0]}
MathCalculator...div...
div結束。。。@After
div異常。。。異常信息:{java.lang.ArithmeticException: / by zero}

AOP原理

//給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】
@EnableAspectJAutoProxy

一切從這個註解提及。
要明白enable開頭的看給容器中註冊了什麼組件,這個組件何時工做,這個組件的功能是什麼?
在這裏插入圖片描述
在這裏插入圖片描述
上面流程能夠看出註解@EnableAspectJAutoProxy注入了AnnotationAwareAspectJAutoProxyCreator.class

AnnotationAwareAspectJAutoProxyCreator.class是一個後置處理器,這個能夠從他的父類看的出來

在這裏插入圖片描述
在這裏插入圖片描述

如今咱們重點研究AnnotationAwareAspectJAutoProxyCreator.

相關文章
相關標籤/搜索