关于spring:建议收藏阿里P7总结的Spring注解笔记把组件注册讲的明明白白

36次阅读

共计 11453 个字符,预计需要花费 29 分钟才能阅读完成。

环境搭建

注解的形式是通过配置类的形式来注入组件,注解注入要比 XML 注入的形式简略,注解注入也须要在前者的根底上,增加一个 spring-context 的包,也是理论开发中罕用的形式。

筹备所需 Jar 包

Spring 注解之组件注册

Spring 提供了许多的注解配置,这样咱们就能够通过注解的形式实现组件的注册,下图就是 Spring 中常常应用到的注解。

@ComponentScan 和 @Configurable

原先 xml 的形式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 要扫描的包 -->
    <context:component-scan base-package="model"></context:component-scan>
</beans>

应用配置类
@Configurable 来标注该类为 Spring 中的配置类,@ComponentScan(“model”) 是为该配置类指定要去扫描的参数。

package config;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import model.Product;

/**
 * @Configurable: 该注解是标注该类是配置类
 * @ComponentScan:配置要扫描的包
 * @author GaoYang
 */
@Configurable
@ComponentScan("model")
public class MainConfig {

}

@Component

应用该注解就能够将 Java 对象 @Component 注册到 Ioc 容器中,@Component 注解要是给属性赋值要配合 @Value 注解为属性赋值。

/**
    @Componnt 能够指定该对象的 id,也能够不必指定
    默认 id 为该类的类名首字母小写
 */
@Component("students")
public class Student {@Value("01")
    private int sid;
    @Value("侯宁宁")
    private String name;
    @Value("男")
    private String sex;

配置类

/**
 * @Configurable: 该注解是标注该类是配置类
 * @ComponentScan:配置要扫描的包
 * @author GaoYang
 */
@Configurable
@ComponentScan("model")
public class MainConfig {

}

应用 @Configuration 注入

@Component("students")
public class Student {@Value("01")
    private int sid;
    @Value("侯宁宁")
    private String name;
    @Value("男")
    private String sex;

    public Student() {super();
    }
    public Student(int sid, String name, String sex) {super();
        this.sid = sid;
        this.name = name;
        this.sex = sex;
    }
    public int getSid() {return sid;}
    public void setSid(int sid) {this.sid = sid;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public String getSex() {return sex;}
    public void setSex(String sex) {this.sex = sex;}
    @Override
    public String toString() {return "Student [sid=" + sid + ", name=" + name + ", sex=" + sex + "]";
    }

}

测试

@Bean

应用 @Bean 注解该能够在咱们的 spring 注册类里标注,创建对象的办法,能够通过一个返回值为该对象的办法去创立该对象,并通过结构器为该对象的属性进行赋值。

// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
    // 默认 id 为办法名
    @Bean
    public Product product1() {return new Product("张三","hashd",1);
    }
    // 能够指定 id
    @Bean("product2")
    public Product product2() {return new Product("张三","hashd",1);
    }

}

Java-Bean 对象

public class Product {
    private String name;
    private String price;
    private int num;
    public Product() {super();
    }
    public Product(String name, String price, int num) {super();
        this.name = name;
        this.price = price;
        this.num = num;
    }
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public String getPrice() {return price;}
    public void setPrice(String price) {this.price = price;}
    public int getNum() {return num;}
    public void setNum(int num) {this.num = num;}
    @Override
    public String toString() {return "Product [name=" + name + ", price=" + price + ", num=" + num + "]";
    }

}

测试

@TypeFilter

@TypeFilter 注解
是通过设置条件来过滤一些资源,咱们能够过滤一些资源不让它加载到 ioc 容器中。它的应用要在 @ComponentScan 这个注解中国去应用,通过 excludeFilters 参数传值,excludeFilters 是一个数组,能够设定多个 @TypeFilter。

@TypeFilter 语法

@Configurable
@ComponentScan(value = "model",excludeFilters = {
        // FilterType.ANNOTATION 是通过注解的模式进行过滤
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),

        // FilterType.ASSIGNABLE_TYPE 是通过给定的类型
        @Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Product.class}),

        // FilterType.ASPECTJ 依据正则表达式
        @Filter(type = FilterType.ASPECTJ,classes = {""}),

        // FilterType.CUSTOM 应用自定义规定
        @Filter(type = FilterType.CUSTOM,classes = {TypeFilterImp.class})

})
public class MainConfig {// @Bean == <bean></bean>}

@FilterType.CUSTOM 自定义规定

应用自定义规定,咱们必须给它创立一个制订规定的类,这个类要去实现 TypeFilter 这个接口,并实现 match 这个办法,过滤器就会依据 match 办法的返回值加载,如果去 ture 就去过滤不满足条件的,如果为 false 则不会去加载!

/**
     * MetadataReader:读取到的以后正在扫描的信息
     * MetadataReaderFactory:能够获取到其余任何类的信息
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取以后类注解的信息
        AnnotationMetadata mr = metadataReader.getAnnotationMetadata();
        // 获取以后正在扫描的类的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取以后类的资源信息
        Resource resource =  metadataReader.getResource();
        // 获取以后类的名字
        String className = classMetadata.getClassName();
        System.out.println("----"+className);
        // contains 蕴含“er”if(className.contains("er")) {return true;}
        return false;
    }

}

@Scope

Spring 创建对象默认是单例的,应用 @Scope 来形容也就是 scope=“singleton”,另外 scope 还有 prototype、request、session、global session 作用域。

各作用域的的作用

  • singleton 单例模式,全局有且仅有一个实例。(默认值)
  • prototype 原型模式,每次获取 Bean 的时候会有一个新的实例。
  • request 示意该针对每一次 HTTP 申请都会产生一个新的 bean,同时该 bean 仅在以后 HTTP request 内无效,配置实例:
    request、session、global session 应用的时候首先要在初始化 web 的 web.xml 中做如下配置:
    如果你应用的是 Servlet 2.4 及以上的 web 容器,那么你仅须要在 web 利用的 XML 申明文件 web.xml 中减少下述 ContextListener 即可:
<web-app>
 ...
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
 ...
</web-app>
  • session 作用域示意该针对每一次 HTTP 申请都会产生一个新的 bean,同时该 bean 仅在以后 HTTP session 内无效
  • global session 作用域相似于规范的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 利用中才有意义。Portlet 标准定义了全局 Session 的概念,它被所有形成某个 portlet web 利用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范畴内。如果你在 web 中应用 global session 作用域来标识 bean,那么 web 会主动当成 session 类型来应用。

案例演示

singleton

@Configurable
@ComponentScan("model")
public class MainConfig {
   /**
    * @Scope
    * prototype: 多实例的  @Scope("prototype")
    * singleton: 单实例的  @Scope("person")
    * request: 一次申请创立一个实例
    * session: 同一个 session 创立一个实例
    * @return
    */
   @Scope("singleton")
   @Bean
   public Product product() {System.out.println("该实例已被创立");
       return new Product("张三","hashd",1);
   }
}

测试代码

public class text {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("Ioc 容器已创立实现!");
        Product bean1 = applicationContext.getBean(Product.class);
        Product bean2 = applicationContext.getBean(Product.class);
        System.out.println(bean1== bean2);

    }
}

从下图能够看到,bean1 == bean2

Layz-bean

@Layz 赖加载次要是针对的是单例模式下,单例模式下 ioc 容器初始化时,就将 bean 对象注入到了容器中,@Layz 注解能够让容器创立时不去注册容器,而是等到第一次调用时才去注册 bean 对象。此时,创立的对象仍然是单例模式!

应用语法

// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
    /**
     * 懒加载:*         针对的是单实例的 bean,默认在容器启动的时候创建对象
     *         赖加载:容器启动时不创建对象,当第一次被调用时被创立
     * 
     */
    @Lazy
    @Bean
    public Product product() {System.out.println("该实例已被创立");
        return new Product("张三","hashd",1);
    }    

测试

@Conditional

@Conditional 注解是依据制订条件来进行注册,须要我创立配置条件的配置类,如果条件满足就进行注册,不满足就不去注册。

语法

配置类

@Configurable
public class MainConfig {@Conditional({winCondition.class})
    @Bean("wind")
    public Product wind() {System.out.println("该实例已被创立");
        return new Product("张三","wind",1);
}    

条件类必须去实现 Condition 接口,并增加为实现的办法!

public class winCondition implements Condition{

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {Environment environment = context.getEnvironment();
        // 获取以后操作系统的名字
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")) {return true;}
        return false;
    }
}

案例

需要依据以后操作系统去注册组件。

// 配置类
@Configurable
@Import(Hero.class)
public class MainConfig {
    // Windows 零碎
    @Conditional({winCondition.class})
    @Bean("wind")
    public Product wind() {System.out.println("该实例已被创立");
        return new Product("张三","wind",1);
    }
    // Linux 零碎  
    @Conditional({linuxCondition.class})
    @Bean("linux")
    public Product linux() {return new Product("李四","linux",2);

    }

}

条件配置类

public class winCondition implements Condition{
    // Windows 零碎,返回 true
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")) {return true;}
        return false;
    }

}
public class linuxCondition implements Condition{
    /**
     * ConditionContext: 判断条件能应用上下文环境
     * AnnotatedTypeMetadata: 正文信息
     */

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 是否 Linux 零碎
        // 1、能获取到 ioc 应用的 bean 工厂
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 2、获取类加载器
        ClassLoader clLoader = context.getClassLoader();
        // 3、获取以后环境信息
          Environment environment = context.getEnvironment();
          String property = environment.getProperty("os.name");

        // 5、bean 注册类
          BeanDefinitionRegistry registry = context.getRegistry();
          if(property.contains("Linux")) {return true;}

        return false;
}

测试…

@import

  • @Import 只能用在类上,@Import 通过疾速导入的形式实现把实例退出 spring 的 IOC 容器中
  • 退出 IOC 容器的形式有很多种,@Import 注解就绝对很牛皮了,@Import 注解能够用于导入第三方包,当然 @Bean 注解也能够,然而 @Import 注解疾速导入的形式更加便捷
  • @Import 注解有三种用法

第一种用法:间接填 class 数组

间接填对应的 class 数组,class 数组能够有 0 到多个。对应的 import 的 bean 都将退出到 spring 容器中,这些在容器中 bean 名称是该类的全类名,比方 com.yc. 类名

@Import({类名.class , 类名.class...})
public class TestDemo {

}

第二种用法:ImportSelector 形式【重点】

这种形式的前提就是一个类要实现 ImportSelector 接口,如果我要用这种办法,指标对象是 Myclass 这个类,剖析具体如下:
创立 Myclass 类并实现 ImportSelector 接口

public class Myclass implements ImportSelector {
// 既然是接口必定要实现这个接口的办法
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[0];
    }
}
// 剖析实现接口的 selectImports 办法中的:// 1、返回值:就是咱们实际上要导入到容器中的组件全类名【重点】// 2、参数:AnnotationMetadata 示意以后被 @Import 注解给标注的所有注解信息【不是重点】// 须要留神的是 selectImports 办法能够返回空数组然而不能返回 null,否则会报空指针异样!

以上剖析结束之后,具体用法步骤如下:
第一步:创立 Myclass 类并实现 ImportSelector 接口,这里用于演示就增加一个全类名给其返回值

public class Myclass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.yc.Test.TestDemo3"};
    }
}

第二步:编写 TestDemo 类,并标注上应用 ImportSelector 形式的 Myclass 类

@Import({TestDemo2.class,Myclass.class})
public class TestDemo {
        @Bean
        public AccountDao2 accountDao2(){return new AccountDao2();
        }

}

第三步:编写打印容器中的组件测试类

**
 * 打印容器中的组件测试
 */
public class AnnotationTestDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestDemo.class);  // 这里的参数代表要做操作的类

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){System.out.println(name);
        }

    }
}

第三种用法:ImportBeanDefinitionRegistrar 形式

同样是一个接口,相似于第二种 ImportSelector 用法,类似度 80%,只不过这种用法比拟自定义化注册,具体如下:

public class Myclass2 implements ImportBeanDefinitionRegistrar {
// 该实现办法默认为空
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {}}
// 参数剖析:// 第一个参数:annotationMetadata 和之前的 ImportSelector 参数一样都是示意以后被 @Import 注解给标注的所有注解信息
// 第二个参数示意用于注册定义一个 bean

第二步:编写代码,自定义注册 bean

public class Myclass2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 指定 bean 定义信息(包含 bean 的类型、作用域...)RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
        // 注册一个 bean 指定 bean 名字(id)beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
    }
}

第三步:编写 TestDemo 类,并标注上应用 ImportBeanDefinitionRegistrar 形式的 Myclass2 类

@Import({TestDemo2.class,Myclass.class,Myclass2.class})
public class TestDemo {

        @Bean
        public AccountDao2 accountDao222(){return new AccountDao2();
        }

}

@FactoryBean

编写配置类

// 标记这是一个 Spring 配置类
@Configuration
public class SpringConfiguration {// 如果没有 @Bean 注解, 则注入到容器中的 id 就是办法名(也就是 myFactoryBean), 然而如果显示的给了值, 那么注入到容器中的就是 factoryBean
    @Bean("factoryBean")
    public MyFactoryBean myFactoryBean(){return new MyFactoryBean();
    }
}

测试类

public class SpringDemo {

    @Test
    public void springTest01() throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        // 容器中获取的 Bean, 实际上就是工厂 Bean(MyFactoryBean 通过 getObject()办法返回的对象)
        Object factoryBean01 = context.getBean("factoryBean");
        System.out.println("实际上注入到容器中的类型是:" + factoryBean01.getClass());

        Object factoryBean02 = context.getBean("factoryBean");
        System.out.println("注入到容器内的对象是否是单例:" + (factoryBean01 == factoryBean02));

        Object factoryBean03 = context.getBean("&factoryBean");
        System.out.println("如果想获取到 MyFactoryBean 的对象, 应用 & 前缀:" + factoryBean03);

        // 输入打印 Spring 中的所有 Bean 名称
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {System.out.println(beanDefinitionName);
        }
    }
}

最初

感激你看到这里,文章有什么有余还请斧正,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享 java 相干技术文章或行业资讯,欢送大家关注和转发文章!

正文完
 0