zoukankan      html  css  js  c++  java
  • Spring注解驱动开发1:组件注册

    Spring注解驱动开发系列:

    1. Spring注解驱动开发1:组件注册
    2. Spring注解驱动开发2:生命周期和属性赋值
    3. Spring注解驱动开发3:自动装配
    4. Spring注解驱动开发4:AOP使用和原理
    5. Spring注解驱动开发5:Spring声明式事务
    6. Spring注解驱动开发6:Spring扩展原理

    Spring注解驱动开发1:组件注册

    在Spring中可以通过XML文件的方式配置Bean,但是在后来的SpringBoot中,使用更多的是通过注解来配置Spring,因此本系列文章主要介绍Spring注解的使用。

    @Configuration和@Bean

    在项目中编写如下配置类,使用@Configuration表示这是Spring的配置类,使用@Bean表示方法返回的类是一个bean,注册到spring容器中。

    //配置类==配置文件
    @Configuration
    public class MainConfigure {
    
        //给容器中注册一个Bean,类型为返回值的类型,id默认是用方法名作为id
        @Bean
        public User user() {
            return new User("jinchengll", 18);
        }
    
    }
    

    在MainTest中编写如下代码来启动Spring和获取Bean:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            //User user = (User) applicationContext.getBean("user");
            User user = (User) applicationContext.getBean(User.class);
            System.out.println(user);
        }
    }
    

    可以通过bean名称和类型进行获取。

    输入如下:

    User{name='jinchengll', age=18}
    

    如果需要指定Bean的名称,可以有如下两种方法:

    1. 修改配置类中对应Bean的方法名,例如修改user()为user1(),则Bean的名称就会变成user1。
    2. 修改配置类中对应Bean的方法的注解,@Bean注解可以指定name,例如@Bean(name = "user2")就可以将Bean名称指定为user2。

    @ComponentScan-自动包扫描

    在上面的例子中,对于Bean需要手动在Configure文件中创建,所以有了@ComponentScan注解,让Spring自动扫描Bean并装载到容器中。

    在配置类中增加@ComponentScan注解,修改MainConfigure类为如下代码:

    //配置类==配置文件
    @Configuration
    //开启自动包扫描,传入要扫描的包路径
    @ComponentScan(basePackages = "com.lin.springL")
    public class MainConfigure {
    
    }
    

    创建dao、service、controller文件夹,并创建如下代码:

    //让Spring扫描到
    @Controller
    public class UserController {
    }
    
    //让Spring扫描到
    @Service
    public class UserService {
    }
    
    //让Spring扫描到
    @Component
    public class UserDao {
    
    }
    

    在MainTest中进行验证是否被Spring加载:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    }
    

    得到输入如下:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfigure
    userController
    userDao
    userService
    

    其中除了spring自己的一些Bean,我们自定义的Bean都被自动扫描到并加入容器中。其中MainConfigure也被扫描到,通过进入Configuration注解类可以看到他也是被@Component所注解。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
    

    @ComponentScan的属性参数

    进入@ComponentScan注解类可以看到其有如下参数:

    • Filter[] includeFilters() default {};表示扫描只包含哪些注解
    • Filter[] excludeFilters() default {};表示扫描不要包含哪些注解

    例子:

    要排除Controller和Service注解,则将MainConfigure类更改为如下代码:

    //配置类==配置文件
    @Configuration
    //开启自动包扫描,传入要扫描的包路径
    //在这里使用FilterType.ANNOTATION规则来过滤,可以有多种方案,具体进入Filter类查看
    @ComponentScan(basePackages = "com.lin.springL", excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, 
                    value = {Controller.class, Service.class})})
    public class MainConfigure {
    
    }
    

    再次运行MainTest类,得到输入如下:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfigure
    userDao
    

    很明显,UserController和UserService已经被排除在外了。

    自定义Filter过滤规则

    Spring允许我们自定义包扫描时候的Filter规则,这时候需要将type指定为CUSTOM,例子如下:

    实现org.springframework.core.type.filter.TypeFilter:

    public class MyTypeFilter implements TypeFilter {
        /**
         * @param metadataReader        读取到当前正在扫描的类信息
         * @param metadataReaderFactory 可以获取到其他任何类信息
         * @return 表示是否匹配上
         * @throws IOException 异常
         */
        @Override
        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();
            System.out.println("===>" + className);
            return "com.lin.springL.dao.UserDao".equals(className);
        }
    }
    

    更改MainConfigure类为:

    //配置类==配置文件
    @Configuration
    //开启自动包扫描,传入要扫描的包路径
    @ComponentScan(basePackages = "com.lin.springL", excludeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, value = {MyTypeFilter.class})})
    public class MainConfigure {
    
    }
    

    执行MainTest得到结果如下:

    ===>com.lin.springL.MainTest
    ===>com.lin.springL.config.MyTypeFilter
    ===>com.lin.springL.controller.UserController
    ===>com.lin.springL.dao.UserDao
    ===>com.lin.springL.pojo.User
    ===>com.lin.springL.service.UserService
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfigure
    userController
    userService
    

    说明自定义的Filter起作用,并且将匹配到的Bean过滤掉。

    @Scope-设置组件作用域

    在上面我们使用的注解默认是单例模式,也就是多次获取同一个名称的Bean,得到的都是同一个Bean,更改MainTest代码如下:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            UserDao userDao = (UserDao) applicationContext.getBean("userDao");
            UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
            System.out.println(userDao == userDao1);
        }
    }
    // 输出结果:true
    

    如果需要多实例,可以使用@Scope来设置,修改UserDao类为如下代码:

    @Component
    //设置作用域
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class UserDao {
    
    }
    

    再次运行MainTest,将会得到结果:false。

    scopeName属性参数

    总共有四种作用域:

    • ConfigurableBeanFactory.SCOPE_PROTOTYPE:多实例模式
    • ConfigurableBeanFactory.SCOPE_SINGLETON:单实例模式(默认模式)
    • org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST:基于Web,一次请求新建一个Bean
    • org.springframework.web.context.WebApplicationContext.SCOPE_SESSION:基于Web,一次Session中新建一个Bean

    @Lazy-bean懒加载

    对于上面提到的Bean,都是在容器启动的时候就会创建对象。懒加载的意思就是在容器启动的时候不创建对象,而是等到第一次使用(getBean)的时候创建对象,避免空间的浪费。

    非懒加载例子

    更改UserDao的代码如下:

    @Component
    public class UserDao {
        public UserDao() {
            System.out.println("UserDao init======");
        }
    }
    

    MainTest的代码如下:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
        }
    }
    

    运行MainTest,得到结果如下:

    UserDao init======
    

    说明在容器启动时候,UserDao就已经被创建对象。

    懒加载例子

    在UserDao类上加入@Lazy注解:

    @Component
    @Lazy
    public class UserDao {
        public UserDao() {
            System.out.println("UserDao init======");
        }
    }
    

    再次运行MainTest发现没有的输出,代表着时候UserDao并没有被创建对象。

    更改MainTest代码如下:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        }
    }
    

    得到输入如下:

    UserDao init======
    

    说明在获取Bean的时候,UserDao才被创建对象。

    @Conditional-按照条件注册

    上面提到的@Bean注解是都会往容器中注册bean,但是@Conditional可以先判断一个条件,条件成立才进行注册。

    例子

    编写两个Condition:

    public class LinCondition implements Condition {
    
        /**
         * @param context 判断条件能使用的上下文环境
         * @param metadata 当前类的注释信息
         * @return 是否匹配上
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //在这里返回false,让其不通过
            return false;
        }
    }
    
    public class KeCondition implements Condition {
        /**
         * @param context  判断条件能使用的上下文环境
         * @param metadata 当前类的注释信息
         * @return 是否匹配上
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //在这里返回true,让其通过
            return true;
        }
    }
    

    更改MainConfigure代码如下:

    //配置类==配置文件
    @Configuration
    public class MainConfigure {
    
        @Conditional(value = {LinCondition.class})
        @Bean
        public User user1() {
            return new User("lin", 33);
        }
    
        @Conditional(value = {KeCondition.class})
        @Bean
        public User user2() {
            return new User("ke", 23);
        }
    }
    

    更改MainTest代码如下:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    }
    

    运行MainTest得到结果:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfigure
    user2
    

    可以看到Condition返回true的Bean会被加载。

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

    通常如果是我们自己编写的类,可以使用上面提到的@Controller等注解来注册到容器中,但是如果需要导入第三方jar包里面的类的时候,这种方法就不适用了,对于这种情况,Spring提供了两种方式,一种是在配置文件中new出对应的类并使用@Bean注释方法,还有一种是使用@Import注解来帮助导入。

    例子

    在配置文件中使用Import,更改MainConfigure代码如下:

    //配置类==配置文件
    @Configuration
    @Import(value = {User.class})
    public class MainConfigure {
    }
    

    运行MainTest得到如下结果:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfigure
    com.lin.springL.pojo.User
    

    包含了User类,id默认为全类名。

    FactoryBean创建Bean

    可以用FactoryBean来包装一个对象给容器

    例子

    编写UserFactoryBean代码如下:

    public class UserFactoryBean implements FactoryBean<User> {
    
        @Override
        public User getObject() throws Exception {
            return new User("factorybean", 33);
        }
    
        @Override
        public Class<?> getObjectType() {
            return User.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    

    更改MainConfigure文件代码如下:

    //配置类==配置文件
    @Configuration
    public class MainConfigure {
    
        @Bean
        public UserFactoryBean userFactoryBean() {
            return new UserFactoryBean();
        }
    
    }
    

    更改MainTest代码如下:

    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            Object userFactoryBean = applicationContext.getBean("userFactoryBean");
            System.out.println(userFactoryBean.getClass());
        }
    }
    

    运行MainTest得到结果如下:

    class com.lin.springL.pojo.User
    

    虽然我们注册的是一个FactoryBean,但是Spring对通过内部的getObject方法获得真实的Bean。

  • 相关阅读:
    kibana 设置登录认证
    elasticsearch
    elasticsearch安装ik
    elasticsearch 安装head
    Docker 数据卷之进阶篇
    link快捷方式
    动作方法中 参数,Json
    spring单元测试
    js之cookie操作
    idea快捷键
  • 原文地址:https://www.cnblogs.com/jinchengll/p/12909058.html
Copyright © 2011-2022 走看看