zoukankan      html  css  js  c++  java
  • Spring注解驱动开发之Ioc容器篇

      前言:现今SpringBoot、SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解、原理,比如@Conditional、@Import、@EnableXXX等。如果掌握这些底层原理、注解,那么我们对这些高层框架就能做到高度定制,使用的游刃有余

    本篇主要内容:spring IOC 容器的组件添加、组件赋值、组件注入及生命周期

    一、组件注册

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

       过去使用xml配置文件

    	<bean id="person" class="com.atguigu.bean.Person"  scope="prototype" >
    		<property name="age" value="${}"></property>
    		<property name="name" value="zhangsan"></property>
    	</bean>
    

       现在使用@Configuration+@Bean注解

    //配置类==配置文件
    @Configuration  //告诉Spring这是一个配置类
    public class MainConfig {
    	//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    	@Bean("person")
    	public Person person01(){
    		return new Person("lisi", 20);
    	}
    

      创建spring容器及获取容器中的bean: 

          //使用配置文件注册
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
            Person bean = (Person) applicationContext.getBean("person");
            System.out.println(bean);
            //使用注解
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            Person bean = applicationContext.getBean(Person.class);
            System.out.println(bean);        

    2、组件注册-@ComponentScan-自动扫描组件&指定扫描规则:

        使用xml配置文件进行自动包扫描添加组件

            <!-- 包扫描、只要标注了@Controller、@Service、@Repository,@Component -->
    	  <context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan> 
    

        使用@ComponentScan注解

    @Configuration //前提这个类必须是容器中的组件
    @ComponentScans(
            value = {
                    @ComponentScan(value="com.atguigu",includeFilters = {
                            @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
                            @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
                            @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:使用自定义规则
    public class MainConfig {    
        @Bean("person")
        public Person person01(){
            return new Person("lisi", 20);
        }
    
    }

      自定义TypeFilter指定过滤规则:

    public class MyTypeFilter implements TypeFilter {
    
        /**
         * metadataReader:读取到的当前正在扫描的类的信息
         * metadataReaderFactory:可以获取到其他任何类信息的
         */
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                throws IOException {
            // TODO Auto-generated method stub
            //获取当前类注解的信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的类信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            //获取当前类资源(类的路径)
            Resource resource = metadataReader.getResource();
            
            String className = classMetadata.getClassName();
            System.out.println("--->"+className);
            if(className.contains("er")){
                return true;
            }
            return false;
        }

    3、@Scope-设置组件作用域 、@Lazy-bean懒加载:

      
        /**
         *
         * @Scope:调整作用域
         * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
         *                     每次获取的时候才会调用方法创建对象;
         * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
         *             以后每次获取就是直接从容器(map.get())中拿,
         * request:同一次请求创建一个实例
         * session:同一个session创建一个实例
         * 
         * 懒加载:
         *         单实例bean:默认在容器启动的时候创建对象;
         *         懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
         * 
         */
        @Lazy
        @Scope("prototype")
        @Bean("person")
        public Person person(){
            System.out.println("给容器中添加Person....");
            return new Person("张三", 25);
        }  

    4、@Conditional-按照条件注册bean

    /**
         * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
         * 
         * 如果系统是windows,给容器中注册("bill")
         * 如果是linux系统,给容器中注册("linus")
         */
       @Bean("bill")
        @Conditional(WindowsCondition.class)
        public Person person01(){
            return new Person("Bill Gates",62);
        }
        
        @Conditional(LinuxCondition.class)
        @Bean("linus")
        public Person person02(){
            return new Person("linus", 48);
        }
    
    //判断是否linux系统 public class LinuxCondition implements Condition { /** * ConditionContext:判断条件能使用的上下文(环境) * AnnotatedTypeMetadata:注释信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // TODO是否linux系统 //1、能获取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //2、获取类加载器 ClassLoader classLoader = context.getClassLoader(); //3、获取当前环境信息 Environment environment = context.getEnvironment(); //4、获取到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; } }
    //判断是否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; } }

    //注:标注在类上。满足当前条件,这个类中配置的所有bean注册才能生效;
    
    

    5、@Import-给容器中快速导入一个组件

    /**
    * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
    * or regular component classes to import.
    */
    @Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    //@Import导入组件,id默认是组件的全类名
    public class MainConfig2 {
        
    }
    
    
    //自定义逻辑返回需要导入的组件
    public class MyImportSelector implements ImportSelector {
    
        //返回值,就是到导入到容器中的组件全类名
        //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // TODO Auto-generated method stub
            //importingClassMetadata
            //方法不要返回null值
            return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
        }
    
    }
    //使用ImportBeanDefinitionRegistrar
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red"); boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } }

     6、使用FactoryBean注册组件

    @Bean
        public ColorFactoryBean colorFactoryBean(){
            return new ColorFactoryBean();
        }
    
    //创建一个Spring定义的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception { // TODO Auto-generated method stub System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; } }

       

     二、生命周期

     bean的生命周期:
    bean创建---初始化----销毁的过程
    容器管理bean的生命周期:
        我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法

    创建(对象创建)
    单实例:在容器启动的时候创建对象
    多实例:在每次获取的时候创建对象

    1、@Bean指定初始化和销毁方法  

    @Configuration
    public class MainConfigOfLifeCycle {
        
        @Bean(initMethod="init",destroyMethod="detory")
        public Car car(){
            return new Car();
        }
    }
    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...");
        }
    
    }

    2、InitializingBean和DisposableBean

    @Component
    public class Cat implements InitializingBean,DisposableBean {
        
        public Cat(){
            System.out.println("cat constructor...");
        }
    
        @Override
        public void destroy() throws Exception {
            // TODO Auto-generated method stub
            System.out.println("cat...destroy...");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // TODO Auto-generated method stub
            System.out.println("cat...afterPropertiesSet...");
        }
    
    }

    3、@PostConstruct&@PreDestroy 

    @Component
    public class Dog implements ApplicationContextAware {
        public Dog(){
            System.out.println("dog constructor...");
        }
        
        //对象创建并赋值之后调用
        @PostConstruct
        public void init(){
            System.out.println("Dog....@PostConstruct...");
        }
        
        //容器移除对象之前
        @PreDestroy
        public void detory(){
            System.out.println("Dog....@PreDestroy...");
        }   
    
    }

    4、BeanPostProcessor-后置处理器

    **
     * 后置处理器:初始化前后进行处理工作
     * 将后置处理器加入到容器中
     * @author lfy
     */
    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // TODO Auto-generated method stub
            System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            // TODO Auto-generated method stub
            System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
            return bean;
        }
    
    }

    BeanPostProcessor原理分析:

      在postProcessBeforeInitialization打个断点,然后debug运行,方法栈如下:

        

      由上而下分析方法栈中的主要方法,先来看applyBeanPostProcessorsBeforeInitialization方法:

        public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
                throws BeansException {
    
            Object result = existingBean;
        //遍历所有的BeanPostProcessor 并执postProcessBeforeInitialization方法,如果返回null,则停止遍历直接返回;
            for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                result = beanProcessor.postProcessBeforeInitialization(result, beanName);
                if (result == null) {
                    return result;
                }
            }
            return result;
        }

      接着往下看initializeBean方法中的部分源码:

    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
         ...
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
            //前一步 wrappedBean
    = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try {
           //这里调用了所有初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); }
    catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) {
            //在上面执行了所有初始化方法后 最后再遍历执行所有BeabPostProcessors的postProcessAfterInitialization方法 wrappedBean
    = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }

      再往下看doCreateBean方法中的部分代码:

    // Initialize the bean instance.
            Object exposedObject = bean;
            try {
            //给bean进行属性赋值(getter) populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } }

    大概的过程:                                                                                                      

    * populateBean(beanName, mbd, instanceWrapper);在初始化开始之前 给bean进行属性赋值
    * initializeBean
    * {
    * applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    * invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
    * applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

      }

    定制bean的生命周期: 

    * 1)、指定初始化和销毁方法;
    * 通过@Bean指定init-method和destroy-method;
    * 2)、通过让Bean实现InitializingBean(定义初始化逻辑),
    * DisposableBean(定义销毁逻辑);
    * 3)、可以使用JSR250;
    * @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
    * @PreDestroy:在容器销毁bean之前通知我们进行清理工作
    * 4)、BeanPostProcessor【interface】:bean的后置处理器;
    * 在每个bean初始化前进行一些处理工作;
    * postProcessBeforeInitialization:在初始化之前工作(其他初始化方法调用之前调用)
    * postProcessAfterInitialization:在初始化之后工作(其他初始化方法调用结束以后调用)
     

    三、属性赋值

    1、@Value赋值

    //使用@Value赋值;
        //1、基本数值
        //2、可以写SpEL; #{}
        //3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
        
        @Value("张三")
        private String name;
        @Value("#{20-2}")
        private Integer age;
        
        @Value("${person.nickName}")
        private String nickName;

    2、@PropertySource加载外部配置文件

    //使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值
    @PropertySource(value={"classpath:/person.properties"})
    @Configuration
    public class MainConfigOfPropertyValues {
        
        @Bean
        public Person person(){
            return new Person();
        }
    
    }

    四、自动装配

      Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值

    1、@Autowired&@Qualifier&@Primary

      @Autowired:自动注入:默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值,如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean("bookDao")

      @Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名,自动装配默认一定要将属性赋值好,没有就会报错, 使用@Autowired(required=false); 

      @Primary:让Spring进行自动装配的时候,默认使用首选的bean也可以继续使用,@Qualifier指定需要装配的bean的名字     

       @Primary
        @Bean("bookDao2")
        public BookDao bookDao(){
            BookDao bookDao = new BookDao();
            bookDao.setLable("2");
            return bookDao;
        }

    2、@Resource&@Inject 

      Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

        @Resource:可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的,没有能支持@Primary功能没有支持@Autowired(reqiured=false);

      @Inject:需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;

    3、方法、构造器位置的自动装配 

    @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
    * 1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
    * 2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
    * 3)、放在参数位置:

    /**
    * @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
    * @param car
    * @return
    */
      @Bean
        public Color color(Car car){
            Color color = new Color();
            color.setCar(car);
            return color;
        }

    4、Aware注入Spring底层组件&原理

      自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx),自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware接口把Spring底层一些组件注入到自定义的Bean中,xxxAware:功能底层使用了xxxProcessor,ApplicationContextAware==》ApplicationContextAwareProcessor;(后续分析源码,会细讲)

    5、@Profile根据环境注册bean 

    Profile:Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能: 

      1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
      2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
      3)、没有标注环境标识的bean在,任何环境下都是加载的;

        @Profile("test")
        @Bean("testDataSource")
        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;
        }

    激活环境方式:

      1)使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test

      2)代码的方式激活某种环境:   

    AnnotationConfigApplicationContext applicationContext = 
                    new AnnotationConfigApplicationContext();
            //1、创建一个applicationContext
            //2、设置需要激活的环境
            applicationContext.getEnvironment().setActiveProfiles("dev");
            //3、注册主配置类
            applicationContext.register(MainConfigOfProfile.class);
            //4、启动刷新容器
            applicationContext.refresh();

                                                   
      

                                     

     
  • 相关阅读:
    USACO 3.3 A Game
    USACO 3.3 Camelot
    USACO 3.3 Shopping Offers
    USACO 3.3 TEXT Eulerian Tour中的Cows on Parade一点理解
    USACO 3.3 Riding the Fences
    USACO 3.2 Magic Squares
    USACO 3.2 Stringsobits
    USACO 3.2 Factorials
    USACO 3.2 Contact
    USACO 3.1 Humble Numbers
  • 原文地址:https://www.cnblogs.com/qzlcl/p/10940263.html
Copyright © 2011-2022 走看看