zoukankan      html  css  js  c++  java
  • Spring IOC 知识点总结

    @Configuration配置spring并启动spring容器

    @Configuration用于定义配置类,标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用是用于配置spring容器(应用上下文)

    实例说明:

    配置类

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

    相当于xml

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

    测试类

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

    运行结果

    TestConfig容器初始化!!!!

    @Configuration&@Bean给容器中注册组件

    @Bean标注在方法上,相当于xml配置配置中的

    代码实例说明:

    注册类

    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 给容器快速导入一个组件

    1、@Import(要导入的组件),容器就会自动注册这个组件,id默认是全类名。

    2、ImportSelector , 自定义逻辑返回要导入的组件 编写的类学要实现 ImportSelector 接口,返回需要导入的主键的全类名数组。

    3、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

    总结:

    给容器中导入组件:

    ​ 1、@Configuration&@Bean给容器中注册组件

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

    ​ 3、@Import 快速的给容器中导入一个组件

    @Value赋值 和@PropertySource 加载外部配置文件

    @Value

    ​ 1、基本数值

    ​ 2、SPEL #{}

    ​ 3、 可以写${},去配置文件中的值(运行环境中的值)

    使用@PropertySource去取外部配置文件中的值保存到运行的环境当中。加载完尾部的配置文件之后,使用${}取出配置文件中的值.

    代码实例:

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

    public class Person {
        @Value("张三")
        private String name;
        @Value("#{20-1}")
        private Integer age;
        @Value("${person.sex}")
        private String sex;
    }
    
    
    @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,领取福利,你懂的。

  • 相关阅读:
    时间序列算法(平稳时间序列模型,AR(p),MA(q),ARMA(p,q)模型和非平稳时间序列模型,ARIMA(p,d,q)模型)的模型以及需要的概念基础学习笔记梳理
    Python两步实现关联规则Apriori算法,参考机器学习实战,包括频繁项集的构建以及关联规则的挖掘
    基于逻辑回归的利用欠采样处理类别不平衡的信用卡欺诈检测
    利用sklearn对MNIST手写数据集开始一个简单的二分类判别器项目(在这个过程中学习关于模型性能的评价指标,如accuracy,precision,recall,混淆矩阵)
    利用Sklearn实现加州房产价格预测,学习运用机器学习的整个流程(包含很多细节注解)
    Javax虚拟机-常见的JVM工具
    Java虚拟机-虚拟机字节码执行引擎
    Java虚拟机-虚拟机类加载机制
    Java虚拟机-类文件结构
    Java虚拟机-内存分配策略
  • 原文地址:https://www.cnblogs.com/xiaofuzi123456/p/13124281.html
Copyright © 2011-2022 走看看