zoukankan      html  css  js  c++  java
  • Spring 注解回顾

    在Spring框架里面常用的注解其实就那么几个,但是深入源码分析如果不懂很多的注解,源码是很难看懂的。

    • 一、@Configuration
    • 二、@Bean
    • 三、@ComponentScan
    • 四、@Scope
    • 五、@Condition
    • 六、@Import
    • 七、使用FactoryBean注册组件
    • 八.在这里也算上@Compont组件,为了后面好区分@Configuration

     

    给容器中注册组件常用

    包扫描+组件标注注解(@Component、@Service、@Controller、@Repository),主要是自己写的类,这三个用于不同的场景。
    @Bean [导入的第三方包里面的组件]
    @Import [快速给容器中导入一个组件]
    Import(类名.class),容器中就会自动注册这个组件,id默认是组件的全名
    ImportSelector:返回需要导入的组件的全类名的数组
    ImportBeanDefinitionRegistrar:手动注册bean
    使用Spring提供的FactoryBean(工厂bean)
    默认获取到的是工厂bean调用getObject创建的对象
    要获取到bean本身,需要给id前面加个&标识
    @Conditional({Condition}) :按照一定的条件判断,满足条件给容器中注册bean
    @Scope
    prototype:多例的 ioc容器启动并不会去调用方法创建对象在容器中,而是每次获取时才会调用方法创建对象
    singleton:单例的(默认值) ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是从容器中拿


    一、@Configuration

    @Configuration : 把一个类标记为spring的配置类,相当于之前的applicationContext.xml文件
    1、看看之前通过applicationContext.xml配置文件来创建类的实例

    public class SomeBean {}
    View Code
    <?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">
    
        <!-- 创建SomeBean的实例,交给IoC来管理 -->
        <bean id="somebean" class="com.zy._01_hello.SomeBean"/>
    </beans>
    View Code

    测试方法

    public class SomeBeanTest {
        /*
            Spring XML Config
         */
        @Test
        public void test(){
            // 加载配置文件
            ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            // 从Spring容器中获取SomeBean的实例
            SomeBean someBean = ctx.getBean("somebean", SomeBean.class);
            System.out.println(someBean);
        }
    }
    View Code

    这种方式是通过Spring XML Config的方式来将类交给Spring容器处理; 但是后来发现有很多这样的xml不容易管理,形成了配置类; 于是就有了后来的SpringBoot 在SpringBoot中几乎看不到配置文件了,取而代之的是Spring Java Config的配置类的形式!

    2、创建配置类
    // 把一个类标记为spring的配置类; (类名Config可以简单理解为XML中的 beans)

    @Configuration
    public class Config {
    
        @Bean
        public SomeBean somebean(){
            return new SomeBean();
        }
    }
    View Code

    测试方法

    public class SomeBeanTest {
        /*
            Spring Java Config
         */
        @Test
        public void test1(){
            ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
            SomeBean someBean = ctx.getBean(SomeBean.class);
            System.out.println(someBean);
        }
    }
    View Code

    这里要通过AnnotationConfigApplicationContext(config.class)来加载配置类,然后拿到配置类中SomeBean的实例即可(将组件的创建交给Spring容器处理, 也就是将组件注册到容器中)!


    二、@Bean

    • @Bean相当于在配置文件中写的<bean id="" class="" />, 将一个类的创建实例交给Spring IoC来处理;

    配置文件中写的bean

    <bean id="" class="" name="" init-method="" destory-method="" scope="">
        <property name="" value=""/>
        <property name="" ref=""/>
    </bean>
    View Code

    配置类中写的bean

    @Configuration
    public class Config {
        @Bean
        public SomeBean someBean1() {
            return new SomeBean();
        }
    
        @Bean
        public SomeBean someBean2() {
            return new SomeBean();
        }
    
        @Bean(name = {"sb", "sbb"})
        public SomeBean someBean3() {
            return new SomeBean();
        }
    }
    View Code

    1、在配置类中@Bean的含义

    • 被@Bean标注的方法的名字 —> bean的id
    • 方法的返回值类型 —> bean的class类型
    • 除了默认使用方法的名字作为id外, 还可以通过@Bean(name={"xxx1", "xxx2"})来指定多个id名
    @Test
    public void test2() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
        SomeBean someBean1 = ctx.getBean("someBean1", SomeBean.class); // bean的id去找
        System.out.println(someBean1);
    
        SomeBean someBean2 = ctx.getBean("someBean2", SomeBean.class);
        System.out.println(someBean2);
    
        SomeBean someBean3 = ctx.getBean("sb", SomeBean.class);
        System.out.println(someBean3);
    
        SomeBean someBean4 = ctx.getBean("sbb", SomeBean.class);
        System.out.println(someBean4);
    }
    View Code

    2、配置initMethod、destroyMethod的方法
    构造(对象创建)

    • 单例: 在容器启动(加载配置类/加载配置文件)的时候创建对象;

                        容器启动先创建对象, 然后调用init(初始化方法);

    • 多例: 在每次获取bean(getBean())的时候创建对象;

                        容器创建完成之后, 才创建对象完成init(初始化);
    方式一: 可以在@Bean中的属性initMethod, destroyMethod来指定初始化,销毁方法

    @Bean(name="sb", initMethod = "init", destroyMethod = "destory")
    View Code

    方式二: @PostConstruct,@PreDestroy来指定初始化,销毁方法

    public class SomeBean {
    
        // 方式二: 配置init,destory  @PostConstruct,@PreDestroy
    
        @PostConstruct
        public void init() {
            System.out.println("SomeBean.init");
        }
    
        @PreDestroy
        public void destory() {
            System.out.println("SomeBean.destory");
        }
    }
    View Code

    测试方法

    public class SomeBeanTest {
        /*
            Spring Java Config
         */
        @Test
        void test(){
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
            SomeBean someBean = ctx.getBean("sb", SomeBean.class);
            System.out.println(someBean);
            ctx.close(); // 非Spring Test,容器不会正常关闭,调用close方法才可以
        }
    }
    View Code

    3、@Bean依赖注入的方式

    otherBean实体

    public class OtherBean {
    }
    
    @Setter
    @Getter
    public class SomeBean {
        private OtherBean otherBean;
    }
    View Code

    方式一: 通过类似内部bean的方式

    @Configuration
    public class Config {
        /*
            方式一: 相当于内部Bean的形式
            <bean id="" class="">
                <property name="otherBean">
                    <bean class="" />  内部bean的方式
                </property>
            </bean>
            这种方式用的很少!
         */
        @Bean
        public SomeBean someBean() {
            SomeBean sb = new SomeBean();
            sb.setOtherBean(new OtherBean());
            return sb;
        }
    }
    View Code

    方式二: 通过调用需要注入的Bean的方式名()即可

    @Configuration
    public class Config {
        @Bean
        public SomeBean someBean() {
            SomeBean sb = new SomeBean();
            sb.setOtherBean(otherBean());
            return sb;
        }
    
        @Bean
        public SomeBean someBean2() {
            SomeBean sb = new SomeBean();
            sb.setOtherBean(otherBean());
            return sb;
        }
    
        @Bean
        public OtherBean otherBean() {
            return new OtherBean();
        }
    }
    View Code

    方式三: 需要依赖的Bean, 放入到参数列表中,会自动注入;
                当有多个OtherBean的实例时,可以使用 @Qualifier("bean的id")来指定
        有多个OtherBean时,在某个bean上添加 @Primary, 会优先注入该bean
        有多个OtherBean时,在参数列表中通过形参名称来指定对应的bean

    @Bean
    // public SomeBean someBean(@Qualifier("otherBean") OtherBean ob) {
    public SomeBean someBean(OtherBean otherBean) {
        SomeBean sb = new SomeBean();
        sb.setOtherBean(otherBean);
        return sb;
    }
    
    @Bean
    //@Primary
    public OtherBean otherBean() {
        return new OtherBean("ob1");
    }
    
    @Bean
    public OtherBean otherBean2() {
        return new OtherBean("ob2");
    }
    View Code

    三、@ComponentScan

    可以完成Spring组件的自动扫描(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类;

    配置类Config

    @Configuration
    // 可以完成Spring组件的自动扫描(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类
    //@ComponentScan(basePackages = "com.zy._04_componentscan") // 也可以自己指定扫描的范围
    @ComponentScan
    public class Config {
    }
    View Code

    OtherBean和SomeBean类

    @Component //设置该类作为Spring管理的组件
    public class OtherBean {
    }
    
    @Component
    public class SomeBean {
        @Autowired // 将Spring通过@Component创建好的OtherBean的实例,注入到下面的属性中
        private OtherBean otherBean;
    }
    View Code

    组件注册时的过滤条件

    • @ComponentScan value:指定要扫描的包
    • excludeFilters=Filter[]:指定扫描包的时候按照什么规则排除哪些组件
    • includeFilters=Filter[]:指定扫描包的时候要包含哪些组件,需将useDefaultFilters置false
    • FilterType.ANNOTATION:按照注解
    • FilterType.ASSIGNABLE_TYPE:按照指定的类型
    • FilterType.REGEX:使用正则指定
    • FilterType.CUSTOM:使用自定义规则
    // 配置类 == 配置文件
    @Configuration  // 告诉Spring这是一个配置类
    //@ComponentScan(value = "com.zy")    // 不写默认扫描当前类所在包(及其子包)下的所有类(指定要扫描的包)
    
    //@ComponentScan(value = "com.zy", excludeFilters = {
    //        // excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
    //        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
    //})
    @ComponentScan(value = "com.zy", includeFilters = {
            // excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
    }, useDefaultFilters = false)
    
    // @ComponentScan   value: 指定扫描的包
    // excludeFilters = Filter[] : 指定扫描的时候按照什么规则排除哪些组件
    // includeFilters = Filter[] : 指定扫描的时候只需要包含哪些组件
    // FilterType.ANNOTATION: 按照注解作为过滤规则
    // FilterType.ASSIGNABLE_TYPE: 按照给定的类型作为过滤规则
    public class MainConfig {
    
        // 给容器中注册一个Bean, 类型为返回值类型, id默认是方法名
        @Bean("person")
        public Person person(){
            return new Person("lisi", 22);
        }
    }
    View Code

    测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = MainConfig.class)
    public class IoCTest {
    
        @Test
        public void test1(){
            ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
            // 看容器中有哪些bean,返回这些bean的名称
            String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
            for (String name : beanDefinitionNames) {
                System.out.println(name);
            }
        }
    }
    View Code

    四、@Scope

    • 用来表示bean的范围(是单例还是多例)
    • prototype: 多例的 : IoC容器启动 并不会去调用方法创建对象 放在容器中, 每次获取bean的时候才会调用方法创建对象;
    • singleton: 单例的 : IoC容器启动就会 调用方法创建对象 放到IoC容器中;
    • request: 同一次请求创建一个实例
    • session: 同一个session创建一个实例
    @Scope("prototype")
        //@Scope // 默认不写value就是singleton
        @Bean
        public Person person(){
            System.out.println("给容器中添加Person...");
            return new Person("张三", 22);
        }
    View Code
    @Test
    public void test2(){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
        // 看容器中有哪些bean,返回这些bean的名称
    //        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
    //        for (String name : beanDefinitionNames) {
    //            System.out.println(name);
    //        }
        // 默认是单例的
        System.out.println("IoC容器创建完成...");
        Object person1 = ctx.getBean("person");
        Object person2 = ctx.getBean("person");
        System.out.println(person1 == person2);
    }
    View Code

       @Lazy
        第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了) ;

    //@Scope("prototype")
        @Scope
        @Lazy //第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了)
        @Bean
        public Person person(){
            System.out.println("给容器中添加Person...");
            return new Person("张三", 22);
        }
    View Code

    五、@Condition

    @Conditional: 按照一定的条件进行判断,满足条件给容器中注册bean

    MacOSXCondition

    // 判断是否Mac系统
    public class MacOSXConditaion implements Condition {
        /**
         *
         * @param conditionContext :判断条件能使用的上下文(环境)
         * @param annotatedTypeMetadata : 注释信息
         * @return
         */
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            // 是否Mac系统
    
            //1. 能获取到IoC使用的beanfactory
            ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
            //2. 获取类加载器
            ClassLoader classLoader = conditionContext.getClassLoader();
            //3. 获取当前环境信息(封住系统的的环境信息等,虚拟机等信息)
            Environment environment = conditionContext.getEnvironment();
            //4. 获取到bean定义的注册类
            BeanDefinitionRegistry registry = conditionContext.getRegistry();
    
            String property = environment.getProperty("os.name");
            if (property.contains("Mac OS X"))
                return true;
    
            return false;
        }
    }
    View Code

    WindowsCondition

    // 判断是否windows系统
    public class WindowsCondition implements Condition {
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            Environment environment = conditionContext.getEnvironment();
            String property = environment.getProperty("os.name");
            if (property.contains("Windows"))
                return true;
            return false;
        }
    }
    View Code

    测试:

    @Test
    public void test3(){
        // 根据Person类型来获取容器中bean的名称
        String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
    
        // 动态获取环境变量的值: Mac OS X
        Environment environment = ctx.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);
    
        for (String name : beanNamesForType) {
            System.out.println(name);
        }
    
        Map<String, Person> persons = ctx.getBeansOfType(Person.class);
        System.out.println(persons);
    }
    View Code

    六、@Import

    • 作用:用于导入其他的配置类
    • 也可以导入一个需要注册的组件(类), id默认是全类名;

    DataSource类

    public class DataSource {
    }
    View Code

    RedisTemplate类

    public class RedisTemplate {
    }
    View Code

    DataSourceConfig配置类

    @Configuration
    public class DataSourceConfig {
        @Bean
        public DataSource dataSource(){
            return new DataSource();
        }
    }
    View Code

    RedisConfig配置类

    @Configuration
    public class RedisConfig {
        @Bean
        public RedisTemplate redisTemplate(){
            return new RedisTemplate();
        }
    }
    View Code

    AppConfig配置类

    @Configuration
    @Import({DataSourceConfig.class, RedisConfig.class})
    public class AppConfig {
    }
    View Code

    ImportTest测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    //@ContextConfiguration(classes = {DataSourceConfig.class, RedisConfig.class})
    @ContextConfiguration(classes = AppConfig.class)
    public class ImportTest {
    
        @Autowired
        private DataSource ds;
    
        @Autowired
        private RedisTemplate rt;
    
        @Test
        public void test() {
            System.out.println(ds);
            System.out.println(rt);
        }
    }
    View Code

    1、ImportSelector 接口

         返回需要导入的组件的全类名

    •      需要导入的组件

    自定义MyImportSelector

    // 自定义逻辑返回需要导入的组件
    public class MyImportSelector implements ImportSelector {
        /**
         *
         * @param annotationMetadata 当前标注@Import注解类的所有注解信息
         * @return 返回要导入到容器中的组件的全类名
         */
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.zy.beans.Blue","com.zy.beans.Red"};
        }
    }
    View Code

    配置类

    @Configuration
    @Import(MyImportSelector.class)
    public class MainConfig2 {
    }
    View Code

    测试

    @Test
    public void testImport(){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
        printBeans((AnnotationConfigApplicationContext) ctx);
        Blue bean = ctx.getBean(Blue.class);
        System.out.println(bean); // 这样就可以成功在Spring 容器中获取到bean了
    }
    
    private void printBeans(AnnotationConfigApplicationContext atx){
        String[] definitionNames = atx.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }
    View Code

    2、@ImportResource

    • @ImportResource来引入xml配置文件, 使xml和javaconfig共同使用;
    public class OtherBean {
    }
    
    @Setter
    @Getter
    public class SomeBean {
        private OtherBean otherBean;
    }
    View Code

    配置类

    @Configuration
    @ImportResource("classpath:applicationContext.xml")
    public class AppConfig {
        @Bean
        public SomeBean someBean(OtherBean otherBean){
            SomeBean sb = new SomeBean();
            sb.setOtherBean(otherBean);
            return sb;
        }
    }
    View Code

    测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AppConfig.class)
    public class AppTest {
    
        @Autowired
        private SomeBean someBean;
    
        @Test
        public void test() {
            System.out.println(someBean.getOtherBean());
        }
    }
    View Code

    七、使用FactoryBean注册组件

    / 创建一个Spring定义的FactoryBean
    public class ColorFactoryBean implements FactoryBean<Color> {
    
        // 返回一个Color对象,这个对象会添加到容器中
        @Override
        public Color getObject() throws Exception {
            System.out.println("ColorFactoryBean.getObject");
            return new Color();
        }
    
        @Override
        public Class<?> getObjectType() {
            return Color.class;
        }
    
        // 是否是单例: true, 在容器中只会保留一份
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    View Code

    配置类

    @Configuration
    public class MainConfig{
        // 实际返回的是getObject()方法返回的对象
        @Bean
        public ColorFactoryBean colorFactoryBean(){
            return new ColorFactoryBean();
        }
    }
    View Code

    测试

    @Test
    public void testImport() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
        printBeans((AnnotationConfigApplicationContext) ctx);
        Blue bean = ctx.getBean(Blue.class);
        System.out.println(bean);
    
        // 工厂Bean获取的是调用getObject创建的对象
        Object bean2 = ctx.getBean("colorFactoryBean");
        Object bean3 = ctx.getBean("colorFactoryBean");
        System.out.println("bean的类型:" + bean2.getClass());
        System.out.println(bean2 == bean3);
    
        // 获取ColorFactorybean的本身
        Object bean4 = ctx.getBean("&colorFactoryBean");
        System.out.println(bean4.getClass());
    }
    
    private void printBeans(AnnotationConfigApplicationContext atx) {
        String[] definitionNames = atx.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }
    View Code
  • 相关阅读:
    字符串 CSV解析 表格 逗号分隔值 通讯录 电话簿 MD
    Context Application 使用总结 MD
    RxJava RxPermissions 动态权限 简介 原理 案例 MD
    Luban 鲁班 图片压缩 MD
    FileProvider N 7.0 升级 安装APK 选择文件 拍照 临时权限 MD
    组件化 得到 DDComponent JIMU 模块 插件 MD
    gradlew 命令行 build 调试 构建错误 Manifest merger failed MD
    protobuf Protocol Buffers 简介 案例 MD
    ORM数据库框架 SQLite 常用数据库框架比较 MD
    [工具配置]requirejs 多页面,多入口js文件打包总结
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/12848788.html
Copyright © 2011-2022 走看看