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
  • 相关阅读:
    小程序开发之初体验
    phantomjs 爬去动态页面
    css实现三角形
    多种方式实现千位分隔符
    基于浏览器的人脸识别标记
    Axios源码阅读笔记#1 默认配置项
    基于图形检测API(shape detection API)的人脸检测
    页面性能优化
    目标
    HelloWorld!
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/12848788.html
Copyright © 2011-2022 走看看