zoukankan      html  css  js  c++  java
  • Spring 注解驱动开发-IOC (精华版)

    Spring 注解驱动开发-IOC

    组件注册

    1. 包扫描+组件标注注解(@ComponetScan/ @Controller/@Service/@Repository/@Component)
    2. @Bean[导入的第三方包里面的组件]
    3. @Import[快速给容器中导入一个组件]
      1. @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。
      2. ImportSelector:返回需要导入的组件的全类名数组;
      3. ImportBeanDefinitionRegistrar:手动注册bean到容器中
    4. 使用Spring提供的FactoryBean(工厂Bean);

    @Configraution,@Bean

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

    @ComponentScan

    @Configuration
    @ComponentScan(basePackages = "com.zzp",
            useDefaultFilters = false,includeFilters =
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
    //        excludeFilters =
    //        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class, Controller.class})
    )
    //@ComponentScan  value:指定要扫描的包
    //excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
    //includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
    public class MainConfig {
    
    }
    
    自定义TypFilter

    MainConfig.java

    @Configuration
    @ComponentScan(basePackages = "com.zzp",
            useDefaultFilters = false,includeFilters =
            @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilterType.class})
    
    )
    //@ComponentScan  value:指定要扫描的包
    //excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
    //includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
    //FilterType.ANNOTATION  根据注解类型来过滤
    //FilterType.ASSIGNABLE_TYPE  根据类的类型来过滤
    //FilterType.ASPECTJ  根据表达式来过滤
    //FilterType.REGEX  根据正则来过滤
    //FilterType.CUSTOM 自定义过滤规则
    public class MainConfig {
    }
    

    MyFilterType.java

    public class MyFilterType implements TypeFilter {
        /**
         *
         * @param metadataReader      读取到的当前正在扫描类的信息
         * @param metadataReaderFactory 可以获取到其他任何类的信息
         * @return
         * @throws IOException
         */
        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();
            if (className.contains("er")){
                return true;  //返回true  合格
            }
            return false;  //false  不合格
        }
    }
    

    @Scope

    @Configuration
    public class MainConfig2 {
    
        @Scope  //调整作用域
        //	PROTOTYPE 多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
        //	                   每次获取的时候才会创建对象
        //	SINGLETON 单例的(默认值): ioc容器启动会调用方法创建对象到ioc容器中,以后每次获取就直接从容器中拿
        //	REQUEST 同一次请求创建一个实例
        //	SESSION 同一个session创建一个实例
        @Bean
        public Person person(){
            return new Person("zzp",18);
        }
    }
    

    @Lazy

    主要针对于单实例bean, 默认在容器启动时创建对象;

    懒加载:启动容器不创建对象,第一次使用(获取)Bean的时候创建对象,并进行初始化。

    @Configuration
    public class MainConfig2 {
        @Scope
        @Lazy
        @Bean
        public Person person(){
            return new Person("zzp",18);
        }
    }
    

    @Conditional

    MainConfig2.java

    //满足当前条件,该配置类才会生效
    @Conditional(WindowsCondition.class)
    @Configuration
    public class MainConfig2 {
    
        @Scope
        @Lazy
        @Bean
        public Person person(){
            return new Person("zzp",18);
        }
    
        //满足当前条件,该bean才会生效
        @Conditional(WindowsCondition.class)
        @Bean("bill")
        public Person person01(){
            return new Person("bill Gates",62);
        }
    
        @Conditional(LinuxCondition.class)
        @Bean("linux")
        public Person person02(){
            return new Person("linux",49);
        }
    }
    
    自定义Condition

    WindowsCondition.java

    public class WindowsCondition implements Condition {
        /**
         *
         * @param context   当前使用的上下文
         * @param metadata  注释信息
         * @return
         */
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
            //获取到ioc容器使用的beanFactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    
            //获取到类加载器
            ClassLoader classLoader = context.getClassLoader();
    
            //获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
    
            //获取到当前环境信息
            Environment environment = context.getEnvironment();
            String property = environment.getProperty("os.name");
    
            if (property.contains("Windows")){
                return true;
            }
            return false;
        }
    }
    

    LinuxCondition.java

    public class LinuxCondition implements Condition {
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
            Environment environment = context.getEnvironment();
            String property = environment.getProperty("os.name");
            if (property.contains("linux")){
                return true;
            }
            return false;
        }
    }
    

    @Import

    @Configuration
    //将Blue导入到ioc容器中,id为全类名com.zzp.domain.Blue
    //ImportSelector:返回需要导入的组件的全类名数组
    //@Import({Blue.class, Red.class})
    //ImportBeanDefinitionRegistrar手动注册进容器中
    @Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    public class MainConfig2 {
    }
    
    自定义ImportSelector

    MyImportSelector.java

    public class MyImportSelector implements ImportSelector {
    	//返回值就是导入到容器中的组件全类名
        //importingClassMetadata :当前标注@Import注解的类的所有注解信息
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.zzp.domain.Blue","com.zzp.domain.Red"};
        }
    }
    
    自定义ImportBeanDefinitionRegistrar

    MyImportBeanDefinitionRegistrar.java

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
         *
         * @param importingClassMetadata 当前标注@Import的注解的类的所有注解信息
         * @param registry  BeanDefinition注册类
         *                  把所有需要添加到容器的bean 调用registry.registerBeanDefinition()方法手工注册进去
         *
         */
    
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            //判断BeanDefinition注册类 中是否包含 id为com.zzp.domain.Blue 的bean
            boolean blue = registry.containsBeanDefinition("com.zzp.domain.Blue");
            boolean red = registry.containsBeanDefinition("com.zzp.domain.Red");
            if (blue && red){
                //手工注册进ioc容器中
    
                //1.实例化一个beanDefinition
                RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
                //2.并注册进 BeanDefinition注册类中
                registry.registerBeanDefinition("rainBow",beanDefinition);
            }
        }
    }
    

    工厂模式 FactoryBean 组件注册

    @Configuration
    public class MainConfig3 {
        
        /**
         *  使用Spring 提供的FactoryBean(工厂Bean)
         *  1.默认获取到的是工厂 bean调用getObject创建对象
         *  2.要获取工厂Bean本身,需要在id前面加上&
         *  eg:  ac.getBean("&color")  获取到的对象类型是 class com.zzp.domain.ColorFactoryBean
         *       ac.getBean("color")  获取到的对象类型是 class com.zzp.domain.Color
         */
        @Bean("color")
        public ColorFactoryBean colorFactoryBean(){
            return new ColorFactoryBean();
        }
    }
    

    ColorFactoryBean.java

    public class ColorFactoryBean implements FactoryBean<Color> {
        public Color getObject() throws Exception {
            return new Color();
        }
    
        public Class<?> getObjectType() {
            return Color.class;
        }
    
        public boolean isSingleton() {
            return true;
        }
    }
    

    生命周期

    bean的声明周期:

    ​ bean创建----> 初始化 ---->销毁的过程

    销毁:

    • 单实例: 容器关闭的时候 //application.close()
    • 多实例:容器不会管理这个bean,容器不会调用销毁方法。

    1.通过@Bean指定init-method和destory-method

    /**
     *  指定初始化和销毁方法: 通过@Bean指定init-method和destory-method
     */
    @Configuration
    public class MainConfigOfLifeCycle {
    
        @Bean(initMethod = "init",destroyMethod = "destory")
        public Red red(){
            return new Red();
        }
    }
    

    Red.java

    public class Red {
        public Red() {
            System.out.println("red constructor .....");
        }
    
        public void init(){
            System.out.println("red init...");
        }
    
        public void destory(){
            System.out.println("red destory....");
        }
    }
    

    2.通过实现InitializingBean和DisposableBean接口的方法

    @Configuration
    public class MainConfigOfLifeCycle {
    
        @Bean
        public Black black(){
            return new Black();
        }
    }
    

    Black.java

    public class Black implements InitializingBean, DisposableBean {
    
        public Black() {
            System.out.println("Black constroctor...");
        }
    	
        //destory-method
        public void destroy() throws Exception {
            System.out.println("Black destory ....");
        }
    
        //init-method
        public void afterPropertiesSet() throws Exception {
            System.out.println("Black afterPropertiesSet ...");
        }
    }
    

    3.通过JSR250 @PostConstruct注解和@PreDestroy注解

    @Configuration
    public class MainConfigOfLifeCycle {
    
        @Bean
        public Green green(){
            return new Green();
        }
    }
    

    Green.java

    public class Green {
        public Green() {
            System.out.println("Green constroctor...");
        }
    
        //init-method
        @PostConstruct
        public void init(){
            System.out.println("Green init...");
        }
    
        //destory-method
        @PreDestroy
        public void destory(){
            System.out.println("Green destory...");
        }
    }
    

    4.BeanPostProcessor接口(bean的后置处理器)

    注意:这个是围绕在init初始化前后

    /**
     * BeanPostProcessor【interface】 bean的后置处理器
     * 在bean初始化前后进行一些处理工作
     * postProcessBeforeInitialization:在初始化之前
     * postProcessAfterInitialization:在初始化之后
     */
    @Configuration
    @ComponentScan("com.zzp.condition")
    public class MainConfigOfLifeCycle {
        @Bean
        public Green green(){
            return new Green();
        }
    }
    

    MyBeanPostProcessor.java

    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
        /**
         * @param bean      当前bean对象
         * @param beanName  当前bean的 id名字
         * @return
         * @throws BeansException
         */
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessBeforeInitialization :beanName:"+ beanName + " " + "bean对象:" + bean);
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessAfterInitialization:beanName:"+ beanName + " " + "bean对象:" + bean);
            return bean;
        }
    }
    

    属性赋值

    @Value

    @Configuration
    //加载配置文件到环境中
    @PropertySource("classpath:person.properties")
    public class MainConfigValue {
    
        @Bean
        public Person person(){
            return new Person();
        }
    }
    

    Person.java

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Person {
        /**
         *  使用Value赋值
         *1.基本数值
         *2.可以写SpEL: #{}
         *3.可以写${}:取出配置文件中的值(在运行环境变量里面的值)
         */
    
        @Value("zhangsan")
        private String name;
        
        @Value("#{20-2}")
        private Integer age;
        
        @Value("${person.nickname}")
        private String nickname;
    }
    

    自动装配

    1.@Autowired 自动注入

    /**
     * 搭配使用:
     * 1)、默认优先按照类型去容器中找相对应的组件:applicationContext.getBean(BookDao.class);
     *
     * 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
     *
     * 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id
     *
     * 4)、自动装配默认一定要将属性赋值好,没有就会报错
     *                            允许没有该装配的组件  @Autowired(required=false)
     *
     * 5)、@Primary: 如果有多个同类型的组件  可以让Spring进行自动装配的时候,默认首选的bean  注意:这个注解是放在需要装配的组件上的
     *
     */
    
    
    
    

    2.Spring还支持使用@Resource(JSR250) 和@Inject(JSR330) 【java规范的注解】

    /**
     * @Resource:
     *          可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的。
     *          缺点: 1.没有能支持@Primary功能  2.没有支持@Autowired(required=false)的功能
     * 
     * @Inject:
     *          需要导入javax.inject包,和@Autowired的功能一样
     *          缺点: 没有支持@Autowired(required=false)的功能
     * 
     * @Autowired是Spring定义的,   @Resource和@Inject都是Java规范
     * 
     * AutowiredAnnotationBeanPostProcessor(增强器):  解析上面这些注解以及@Autowired注解并完成自动装配功能的 
     */
    

    3.xxxAware

    自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)

    自定义组件实现xxxAware接口:在创建对象的时候,会调用接口规定的方法注入相关组件

    把Spring底层一些组件注入到自定义的Bean中;

    xxxAware的功能: 底层是使用xxxProcessor 来处理的 该xxxProcessor也是实现了BeanPostProcessor增强器

    例子: 实现ApplicationContextAware 底层使用 class ApplicationContextAwareProcessor implements **BeanPostProcessor ** 增强器

    Rea.java

    @Component
    public class Rea implements ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        public ApplicationContext getApplicationContext() {
            return applicationContext;
        }
        
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
            System.out.println("context:" + applicationContext);
            this.applicationContext = applicationContext;
        }
    }
    

    4.@Profile

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

    @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件。

    案例:

    MyConfigOfProfile.java

    /**
     *
     * @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定则任何环境下都能注册这个组件。
     * 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
     * 2)、写在配置类上,只有指定的环境的时候,整个配置类里面的所有配置才能生效
     * 3)、没有标注环境标识的bean在任何环境下都是加载的。
     */
    
    @Profile("test")  
    @PropertySource("classpath:jdbc.properties")
    @Configuration
    public class MyConfigOfProfile implements EmbeddedValueResolverAware {
    
        @Value("${jdbc.user}")
        private String user;
    
        @Value("${jdbc.password}")
        private String password;
    
        private String driverClass;
    
        @Profile("test")
        @Bean("dataSourceTest")
        public DataSource dataSourceTest() throws Exception {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(password);
            dataSource.setJdbcUrl("jdbc:mysql:///test");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
    
        @Profile("prod")
        @Bean("dataSourceProd")
        public DataSource dataSource1() throws Exception {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(password);
            dataSource.setJdbcUrl("jdbc:mysql:///luffy");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
    
    
        @Profile("dev")
        @Bean("dataSourceDev")
        public DataSource dataSourceDev() throws Exception {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(password);
            dataSource.setJdbcUrl("jdbc:mysql:///staf");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
    
    
        //采用实现Aware接口的方式来解析值
        @Override
        public void setEmbeddedValueResolver(StringValueResolver resolver) {
            this.driverClass = resolver.resolveStringValue("${jdbc.driver}");
        }
    }
    

    运行时怎么去切换环境呢?

    1.通过代码去控制

    MyTest.java

    public class MyTest {
    
        @Test
        public void test(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            //获取环境
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            //设置环境的profile值
            environment.setActiveProfiles("test");
            //注册配置类
            applicationContext.register(MyConfigOfProfile.class);
            applicationContext.refresh();
    
            String[] names = applicationContext.getBeanNamesForType(DataSource.class);
            for (String name : names) {
                System.out.println(name);
            }
        }
    }
    
    

    2.通过命令行参数来控制

    -Dspring.profiles.active=test
    
    万般皆下品,唯有读书高!
  • 相关阅读:
    UVAlive5135_Mining Your Own Business
    UVAlive3523_Knights of the Round Table
    UVA10759_Dice Throwing
    POJ3177_Redundant Paths
    UVA11027_Palindromic Permutation
    Codechef_JULY14
    UVAlive4255_Guess
    UVA10054_The Necklace
    杜教BM
    【2018徐州网络赛】Morgana Net (矩阵快速幂)
  • 原文地址:https://www.cnblogs.com/s686zhou/p/14530977.html
Copyright © 2011-2022 走看看