zoukankan      html  css  js  c++  java
  • (一)spirng IOC、DI的详细应用

    一、spring核心概念 IOC和DI

      IOC即控制反转,这是一种设计思想,有反转既有正转,那么什么是反转什么是正转呢?

    • 控制反转控制反转,首先是控制,再者是反转。
    • 所谓控制,在传统JAVA SE设计标准中,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
    • 所谓反转,有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
    • 所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

        那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

    • IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

        理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。

    二、Spring IOC 容器底层注解使用

      配置spring 容器有多种形式,起初是以xml配置文件为主流,这一方式复杂且繁琐,后期代码量大的时候难以维护。后来注解形式的配置变成了为主流,springboot便是其集大成者。

    1.   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"
    xsi:schemaLocation
    ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      /定义一个Bean的信息   
    <bean id="car" class="com.tuling.compent.Car"></bean>
    </beans>
    去容器中读取Bean
    public static void main( String[] args ) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    System.out.println(ctx.getBean("person"));
    }

      2. 基于读取配置类的形式定义Bean信息

    •   定义配置类,配置类可以直接定义一些列的bean,也可以通过扫描的形式加入bean
    @Configuration 
    public class MainConfig {
      @Bean
      public Person person(){
       return new Person(); 
      } 
    }
    • 在配置类上写@CompentScan注解来进行包扫描,这种形式可以排除或者指定某些bean。
    @Configuration
     @ComponentScan(basePackages = {"com.tuling.testcompentscan"})
     public class MainConfig {
     }
      •   排除用法 excludeFilters(排除@Controller注解的,和TulingService的),
    @Configuration 
    @ComponentScan(basePackages = {"com.tuling.testcompentscan"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), 
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TulingService.class}) })
    public class MainConfig {
     }
      •   包含用法 includeFilters ,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)
    @Configuration 
    @ComponentScan(basePackages = {"com.tuling.testcompentscan"},includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false) 
    public class MainConfig { 
    }
      • @ComponentScan.Filter type的类型

       a)注解形式的FilterType.ANNOTATION @Controller @Service @Repository @Compent

       b)指定类型的 FilterType.ASSIGNABLE_TYPE @ComponentScan.Filter(type =FilterType.ASSIGNABLE_TYPE,value = {TulingService.class})
       c)aspectj类型的 FilterType.ASPECTJ(不常用)
       d)正则表达式的 FilterType.REGEX(不常用)
       e)自定义的 FilterType.CUSTOM
       其中,ANNOTATION 和 ASSIGNABLE_TYPE 区别在于,后者一次性只能指定一个类,而前者包含所有带有该注解的类。
        •     FilterType.CUSTOM 自定义类型如何使用
    public class TulingFilterType implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader,
            MetadataReaderFactory metadataReaderFactory) throws IOException {
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            Resource resource = metadataReader.getResource();
    
            if (classMetadata.getClassName().contains("dao")) {
                return true;
            }
    
            return false;
        }
    }
    @ComponentScan(basePackages =  {
        "com.tuling.testcompentscan"}
    , includeFilters =  {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = TulingFilterType.class)
    }
    , useDefaultFilters = false)
    public class MainConfig {
    }
    • 去容器中读取Bean的信息(传入配置类)
    public static void main( String[] args ) { 
      AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); 
      System.out.println(ctx.getBean("person"));
     }

       

      3.配置Bean的作用域对象

    • @Scope指定的作用域方法取值,其中单例和多例多常用,最后两个几乎用不到
    a) singleton 单实例的(默认) 
    b) prototype 多实例的 
    c) request 同一次请求 
    d) session 同一个会话级别
    • 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)
    @Bean 
    public Person person() { return new Person(); }
    • 指定@Scope为 prototype表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)
    @Bean 
    @Scope(value = "prototype")
     public Person person() { 
    return new Person();
    }
    • Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)
    @Bean 
    @Lazy
     public Person person() { return new Person(); }
    • @Conditional进行条件判断等.
      场景,有二个组件TulingAspect 和TulingLog ,我的TulingLog组件是依赖于TulingAspect的组件
           应用:自己创建一个TulingCondition的类 实现Condition接口 
    public class TulingCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
            if (context.getBeanFactory().containsBean("tulingAspect")) {
                return true;
            }
    
            return false;
        }
    }
    public class MainConfig {
        @Bean
        public TulingAspect tulingAspect() {
            return new TulingAspect();
        }
    
    //当切 容器中有tulingAspect的组件,那么tulingLog才会被实例化.
        @Bean
        @Conditional(value = TulingCondition.class)
        public TulingLog tulingLog() {
            return new TulingLog();
        }
    }

      4.往IOC 容器中添加组件的方式

    • ①:通过@CompentScan +@Controller @Service @Respository @componet

        适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。

    • ②:通过@Bean的方式来导入组件(适用于导入第三方组件的类)
    • ③:通过@Import来导入组件 (导入组件的id为全类名路径)
    @Configuration
    @Import(value =  {
        Person.class, Car.class}
    )
    public class MainConfig {
    }
      •   通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径) 
    public class TulingImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[] { "com.tuling.testimport.compent.Dog" };
        }
    }
    @Configuration
    @Import(value =  {
        Person.class, Car.class, TulingImportSelector.class}
    )
    public class MainConfig {
    }
      •   通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称) 
    public class TulingBeanDefinitionRegister  implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
          //创建一个bean定义对象
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class);
          //把bean定义对象导入到容器中
            registry.registerBeanDefinition("cat", rootBeanDefinition);
        }
    }
    @Configuration
    @Import(value =  {
        Person.class, Car.class, TulingImportSelector.class, TulingBeanDefinitionRegister.class}
    )
    public class MainConfig {
    }
    • ④:通过实现FacotryBean接口来实现注册组件 
    public class CarFactoryBean implements FactoryBean<Car> {
        @Override
        public Car getObject() throws Exception {
            return new Car();
        }
    
    /返回bean的类型
        @Override
        public Class<?> getObjectType() {
            return Car.class;
        }
    /是否为单例
        @Override
        public boolean isSingleton() {
            return true;
        }
    }

      5.BeanFactory和ApplicationContext(bean工厂和应用上下文)

       

    • Bean 工厂(com.springframework.beans.factory.BeanFactory)是Spring 框架最核心的接口,它提供了高级IoC 的配置机制。
    • 应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基础之上。

         由上图可以知道,BeanFactory是最顶层接口,而ApplicationContext则集成BeanFactory,增加了许多高级特性。

    • ApplicationContext 的初始化和BeanFactory 有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext 则在初始化应用上下文时就实例化所有单实例的Bean 

      6.bean的生命周期

    • 指定bean的初始化和销毁方法    
      bean的创建----->初始化----->销毁方法
      •   针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法
      •   针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理. 
      由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法
    @Configuration
    public class MainConfig { //指定了bean的生命周期的初始化方法和销毁方法.
        @Bean(initMethod = "init", destroyMethod = "destroy")
        public Car car() {
            return new Car();
        }
    }
    • 通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
    @Component
    public class Person implements InitializingBean, DisposableBean {
        public Person() {
            System.out.println("Person的构造方法");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("DisposableBean的destroy()方法 ");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("InitializingBean的 afterPropertiesSet方法");
        }
    }
    • :通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法
    @Component
    public class Book {
        public Book() {
            System.out.println("book 的构造方法");
        }
    
        @PostConstruct
        public void init() {
            System.out.println("book 的PostConstruct标志的方法");
        }
    
        @PreDestroy
        public void destory() {
            System.out.println("book 的PreDestory标注的方法");
        }
    }
    • 通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程 
      •   postProcessBeforeInitialization  在init方法之前调用
      •   postProcessAfterInitialization  在init方法之后调用
    @Component
    public class TulingBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
            System.out.println(
                "TulingBeanPostProcessor...postProcessBeforeInitialization:" +
                beanName);
    
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
            System.out.println(
                "TulingBeanPostProcessor...postProcessAfterInitialization:" +
                beanName);
    
            return bean;
        }
    }
        BeanPostProcessor的执行时机 
    populateBean(beanName, mbd, instanceWrapper)
    initializeBean{ 
      applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ isInitializingBean.afterPropertiesSet
       自定义的init方法 
      }
      applyBeanPostProcessorsAfterInitialization()方法
     }
    • 自动装配
      •     @AutoWired的使用 
    //一个Dao
     @Repository
     public class TulingDao {
    } @Service
    public class TulingService {    @Autowired    private TulingDao tulingDao; }
    结论:
      a:自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配
    @Autowired
    private TulingDao tulingDao;
      比如,我容器中有二个TulingDao类型的组件 一个叫tulingDao 一个叫tulingDao2
      那么我们通过@AutoWired 来修饰的属性名称时tulingDao,那么拿就加载容器的tulingDao组件,若属性名称为tulignDao2 那么他就加载的时tulingDao2组件
     
      b:假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("tulingDao")来指定装配的组件或者在配置类上的@Bean加上@Primary注解 
    @Autowired 
    @Qualifier("tulingDao") 
    private TulingDao tulingDao2;
      •     @Resource(JSR250规范) 
        功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持 
      •     @InJect(JSR330规范)
        需要导入jar包依赖,功能和支持@Primary功能 ,但是没有Require=false的功能
      •   使用autowired 可以标注在方法上 
            标注在set方法上 
    @Autowired
     public void setTulingLog(TulingLog tulingLog) { 
      this.tulingLog = tulingLog; 
    }
            标注在构造方法上
    @Autowired
     public TulingAspect(TulingLog tulingLog) {
       this.tulingLog = tulingLog;
     }
      •   我们自己的组件 需要使用spring ioc的底层组件的时候,比如 ApplicationContext等 
        我们可以通过实现XXXAware接口来实现
    @Component
    public class TulingCompent implements ApplicationContextAware, BeanNameAware {
        private ApplicationContext applicationContext;
    
        @Override
        public void setBeanName(String name) {
            System.out.println("current bean name is :【" + name + "】");
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
  • 相关阅读:
    软件测试人员的年终绩效考核怎么应对
    收藏
    顶踩组件 前后两版
    订阅组件
    hdu 1963 Investment 完全背包
    hdu 4939 Stupid Tower Defense 动态规划
    hdu 4405 Aeroplane chess 动态规划
    cf 414B Mashmokh and ACM 动态规划
    BUPT 202 Chocolate Machine 动态规划
    hdu 3853 LOOPS 动态规划
  • 原文地址:https://www.cnblogs.com/shyroke/p/14532664.html
Copyright © 2011-2022 走看看