• 将 Bean 放入 Spring 容器中的方式


    参考学习地址: https://mp.weixin.qq.com/s/OQPxh5Y9m-YAIOIyUIISiA
    蚂蚁课堂: https://xiaoe.mayikt.com/

    将 Bean 放入 Spring 容器中的方式

    在开发中使用Spring的时候,都是将对象交由Spring去管理,对象交给Spring管理的方式:

    [练习代码: technology-springbean-demo项目中,以项目实战为案例进行练习]

    package com.tomdd;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * 

    spring bean注入容器中的启动类

    *

    * 启动类需要扫描到配置类,即启动类包含配置类到配置类 * * @author zx * @date 2022年10月27日 9:05 */ @SpringBootApplication public class SpringBeanApplication { public static void main(String[] args) { SpringApplication.run(SpringBeanApplication.class); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.@Configuration + @Bean

    @Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中【工厂方法形式注入】

    package com.tomdd.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    /**
     * 

    用户实体

    * * @author zx * @date 2022年10月27日 9:07 */
    @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class User { private String name; private Integer age; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package com.tomdd.config;
    
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    /**
     * @author zx
     * @date 2022年10月27日 9:09
     */
    @RestController
    @Api(tags = "用户服务接口")
    public class UserService {
        @Autowired
        private User user;
    
    
        @GetMapping("/getUser")
        @ApiOperation("从容器中获取用户信息")
        public BaseResponse<?> getUserInfo() {
            return BaseResponse.ok(user);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.@Componet + @ComponentScan

    @Componet中文译为组件,放在类名上面,然后@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中

    package com.init;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * 

    订单实体对象

    * * @author zx * @date 2022年10月27日 9:17 */
    @Data @Component @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class Order { /** * 订单名称 */ private String name; /** * 下单时间 */ private Date placeOrderDate; /** * 订单价格 */ private Double price; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    配置类上加上路径扫描:

    package com.tomdd.config;
    
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    package com.tomdd.service;
    
    import com.init.Order;
    import com.tomdd.resp.BaseResponse;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Date;
    
    /**
     * @author zx
     * @date 2022年10月27日 9:09
     */
    @RestController
    @Api(tags = "订单服务服务接口")
    public class OrderService {
        @Autowired
        private Order order;
    
    
        @GetMapping("/getOrderInfo")
        @ApiOperation("从容器中获取订单信息")
        public BaseResponse<?> getOrderInfo() {
            order.setName("apple phone 13 pro").setPlaceOrderDate(new Date()).setPrice(5321.3);
    
            return BaseResponse.ok(order);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.@Import注解导入

    在进行Spring扩展时经常会用到,@Import注解经常搭配自定义注解进行使用,然后往容器中导入一个配置文件。

    @Import注解源码:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
     
        /**
       * 用于导入一个class文件
         * {@link Configuration @Configuration}, {@link ImportSelector},
         * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
         */
        Class<?>[] value();
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.1 @Import直接导入类

    package com.tomdd.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    /**
     * 

    学生信息类

    * * @author zx * @date 2022年10月27日 9:29 */
    @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class StudentInfo { /** * 学生名称 */ private String name; /** * 班级 */ private String classGrade; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在配置类上导入StudentInfo类:

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") @Import(StudentInfo.class) public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试:

    package com.tomdd.service;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.resp.BaseResponse;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 

    通用的服务接口

    * * @author zx * @date 2022年10月27日 9:31 */
    @RestController @Api(tags = "通用服务接口") public class CommonService { @Autowired private StudentInfo studentInfo; @GetMapping("/getSudentInfo") @ApiOperation("通过直接导入@Import 方式") public BaseResponse<?> getStudentInfo(){ studentInfo.setName("tomdd").setClassGrade("Java高级培训班"); return BaseResponse.ok(studentInfo); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3.2 @Import + ImportSelector

    实现ImportSelector的接口 ,返回一个数组,改数组中包含需要导入的类即导入的类的全限定名写在里面即可

    package com.tomdd.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    /**
     * 

    部分信息

    * * @author zx * @date 2022年10月27日 9:41 */
    @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class DepartmentInfo { /** * 部门名称 */ private String name; /** * 部门经理 */ private String manager; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    package com.tomdd.config;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 

    自定义导入选择器

    * * @author zx * @date 2022年10月27日 9:40 */
    public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.tomdd.model.DepartmentInfo"}; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    配置类中加入自定义选择器:

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") @Import({StudentInfo.class,MyImportSelector.class}) public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试访问:

    package com.tomdd.service;
    
    import com.tomdd.model.DepartmentInfo;
    import com.tomdd.resp.BaseResponse;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 

    通用的服务接口

    * * @author zx * @date 2022年10月27日 9:31 */ @RestController @Api(tags = "通用服务接口") public class CommonService { @Autowired private DepartmentInfo departmentInfo; @GetMapping("/getDepartmentInfo") @ApiOperation("通过自定义ImportSelector方式进行导入") public BaseResponse getDepartmentInfo(){ departmentInfo.setManager("zhongxu").setName("研发部"); return BaseResponse.ok(departmentInfo); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.3 @Import + ImportBeanDefinitionRegistrar

    实现 ImportBeanDefinitionRegistrar 接口,可以自己定义bean定义信息,定义好之后,bean定义注入,交给spring工厂去管理对象,这个就会涉及到spring的生命周期了。

    package com.tomdd.config;
    
    import com.tomdd.model.EmployeeInfo;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 

    自定义导入bean定义注册

    * * @author zx * @date 2022年10月27日 10:03 */
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 构建一个beanDefinition, 可以简单理解为bean的定义. AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(EmployeeInfo.class).getBeanDefinition(); // 将beanDefinition注册到Ioc容器中. 定义bean名称 registry.registerBeanDefinition("employee", beanDefinition); ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    配置类导入注解中加入自定义导入bean定义注册:

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") @Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class}) public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试访问:

    @Autowired
    private EmployeeInfo employee;
    
    @GetMapping("/getEmployeeInfo")
    @ApiOperation("导入bean定义注册形式")
    public BaseResponse<?> getEmployeeInfo(){
        employee.setEmployeeNo("K0001").setDepartmentName("研发部");
        return BaseResponse.ok(employee);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.4 通过bean定义注册后置处理器+ FactoryBean 创建代理进行ERP远程调用

    我在物流项目中真实案例,一个简单的ERP远程调用的一个starter插件案例

    3.4.1 定义FactoryBean

    Spring的beanFactory调用getObject的时候返回接口的代理对象

    package com.logistics.rpc;
    
    import org.springframework.beans.factory.FactoryBean;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class ServiceFactory<T> implements FactoryBean<T> {
    
        private Class<T> interfaceType;
    
        public ServiceFactory(Class<T> interfaceType) {
            this.interfaceType = interfaceType;
        }
    
        @Override
        public T getObject() {
            InvocationHandler handler = new ServiceProxy<>(interfaceType);
            return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),
                    new Class[]{interfaceType}, handler);
        }
    
        @Override
        public Class<T> getObjectType() {
            return interfaceType;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    3.4.2 定义远程调用的接口
    package com.logistics.client;
    
    import com.logistics.model.bo.OrderTransportDTO;
    import com.logistics.model.bo.QueueRequestBO;
    import com.logistics.resp.BaseResponse;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    
    import javax.validation.constraints.Size;
    
    /**
     * 

    物流排队访问客户端

    * * @author zx * @date 2022年09月27日 17:28 */
    public interface LogisticsQueueClient { /** *

    测试接口 Get请求方式

    * * @return 测试信息字符串 */
    @GetMapping("/logistics/queue/testInfo") BaseResponse<?> testInfo(String name); @PostMapping("/logistics/queue/postTestInfo") BaseResponse<?> postTestInfo(@RequestBody QueueRequestBO queueRequestBO); /** * 物流排队 * @param orderTransportDTO * @return */ @PostMapping("/logistics/queue/doQueue") BaseResponse<Void> doQueue(@RequestBody @Validated @Size(min = 1, message = "至少是一车一单") OrderTransportDTO orderTransportDTO); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    3.4.3 自定义bean定义注册后置处理器
    package com.logistics.rpc;
    
    import com.logistics.client.LogisticsQueueClient;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.beans.factory.support.GenericBeanDefinition;
    
    /**
     * 

    物流排队调用接口bean定义注册到容器[代理对象]

    * * @author zx * @date 2022年09月27日 17:35 */
    @Slf4j public class LogisticsQueueBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor { /** * bean定义的后置处理器回调方法 */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LogisticsQueueClient.class); GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.getConstructorArgumentValues().addGenericArgumentValue(LogisticsQueueClient.class); definition.setBeanClass(ServiceFactory.class); definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); registry.registerBeanDefinition(LogisticsQueueClient.class.getSimpleName(), definition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { //bean factory post process , do not anyhing } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    思考:多个访问接口如何取注册了? 通过反射指定包名,获取改包下的所有的class。进行循环注册即可

    比如;

    /**
     * 为接口注入实现类
     */
    @Component
    public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {
        private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
        private static ApplicationContext applicationContext;
        private MetadataReaderFactory metadataReaderFactory;
        private ResourcePatternResolver resourcePatternResolver;
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            Set<Class<?>> clazzSet = scannerPackages("com.logistics.client");
            clazzSet.stream().filter(Class::isInterface).forEach(x -> registerBean(registry, x));
        }
    
        private void registerBean(BeanDefinitionRegistry registry, Class clazz) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            definition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
            definition.setBeanClass(ServiceFactory.class);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            registry.registerBeanDefinition(clazz.getSimpleName(), definition);
        }
    
        /**
         * 获取指定路径及子路径下的所有类
         */
        private Set<Class<?>> scannerPackages(String basePackage) {
            Set<Class<?>> set = new LinkedHashSet<>();
            String basePackageName = ClassUtils.convertClassNameToResourcePath(applicationContext.getEnvironment().resolveRequiredPlaceholders(basePackage));
    
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    basePackageName + '/' + DEFAULT_RESOURCE_PATTERN;
            try {
                Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
                for (Resource resource : resources) {
                    if (resource.isReadable()) {
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        String className = metadataReader.getClassMetadata().getClassName();
                        Class<?> clazz;
                        try {
                            clazz = Class.forName(className);
                            set.add(clazz);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return set;
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
            this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    3.4.4 定义代理执行的InvocationHandler
    package com.logistics.rpc;
    
    import com.alibaba.fastjson.JSONObject;
    import com.logistics.resp.BaseResponse;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.util.HashMap;
    import java.util.Map;
    
    @Slf4j
    @SuppressWarnings("all")
    public class ServiceProxy<T> implements InvocationHandler {
    
        private T target;
    
        public ServiceProxy(T target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            JSONObject result = null;
            if (method.isAnnotationPresent(GetMapping.class)) {
                result = getMappingHandler(method, args, result);
            } else if (method.isAnnotationPresent(PostMapping.class)) {
                //post 请求方式
                result = postMappingHandler(method, args[0]);
            } else {
                throw new IllegalArgumentException("request method error ,Currently supported get /post");
            }
    
            log.info("http request result:{}", result);
            return resultHandler(result);
        }
    
        private BaseResponse resultHandler(JSONObject result) throws Exception {
            if (result == null) {
                return BaseResponse.faile("remote procedure call return result is null ");
            }
    
            if ("200".equals(result.get("code"))) {
                return BaseResponse.data((String) result.get("msg"), (String) result.get("code"), result.get("data"));
            }
            throw new Exception("远程调用异常:" + result.get("msg"));
        }
    
        private JSONObject postMappingHandler(Method method, Object arg1) {
            JSONObject result;
            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            String uri = postMapping.value()[0];
            Object arg = arg1;
            log.info("post transmit param:{}", arg);
    
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(arg));
            log.info("convert jonsobject :{}", jsonObject);
            result = LogisticsQueueHttpClient.httpPost(uri, jsonObject);
            return result;
        }
    
        private JSONObject getMappingHandler(Method method, Object[] args, JSONObject result) {
            //get 请求处理逻辑
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            if (getMapping != null) {
                Map<String, String> getParamMap = new HashMap<>();
                String[] value = getMapping.value();
                String uri = value[0];
    
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    String parameterName = parameters[i].getName();
                    String parameterValue = (String) args[i];
                    getParamMap.put(parameterName, parameterValue);
                }
                //通过反射获取参数名称
                result = LogisticsQueueHttpClient.httpGet(uri, getParamMap);
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    3.4.5 定义远程调用httpclient

    简单实现一个轮询策略

    package com.logistics.rpc;
    
    import com.alibaba.fastjson.JSONObject;
    import com.logistics.strategy.LoadBalancFactory;
    import com.logistics.strategy.LoadBalance;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpStatus;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.CharArrayBuffer;
    import org.apache.http.util.EntityUtils;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.core.env.Environment;
    import org.springframework.core.env.StandardEnvironment;
    
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    @Slf4j
    public class LogisticsQueueHttpClient implements EnvironmentAware {
    
        private static RequestConfig requestConfig = null;
        private static final int SOCKET_TIMEOUT = 5000;
        private static final int CONNECT_TIMEOUT = 5000;
    
        private static final String SERVERURL = "logistics.queue.server-url";
        private static final String LOAD_BALANC_STRATEGY = "logistics.queue.nlb";
        private static String DEFAULT_LOAD_BALANC_STRATEGY = "com.logistics.strategy.laodbalance.Defau ltPollLoadBalance";
    
        private static final String SERVERURL_SEPARATOR = ",";
    
        private static List<String> serverUrlList = new ArrayList<>();
    
        static {
            // 设置请求和传输超时时间
            requestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).build();
        }
    
        /**
         * post请求传输json参数
         *
         * @param jsonParam 参数
         * @return
         */
        public static JSONObject httpPost(String uri, JSONObject jsonParam) {
            JSONObject jsonResult = null;
            String url = getUrl() + uri;
            log.info("access url:{}", url);
            HttpPost httpPost = new HttpPost(url);
            CloseableHttpClient httpClient = HttpClients.createDefault();
            // 设置请求和传输超时时间
            httpPost.setConfig(requestConfig);
            try {
                if (null != jsonParam) {
                    // 解决中文乱码问题
                    StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
                    entity.setContentEncoding("UTF-8");
                    entity.setContentType("application/json");
                    httpPost.setEntity(entity);
                }
                CloseableHttpResponse result = httpClient.execute(httpPost);
                // 请求发送成功,并得到响应
                if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    String str = "";
                    try {
                        // 读取服务器返回过来的json字符串数据
                        str = EntityUtils.toString(result.getEntity(), "utf-8");
                        // 把json字符串转换成json对象
                        jsonResult = JSONObject.parseObject(str);
                    } catch (Exception e) {
                        log.error("请求服务器端出错:", e);
                    }
                }
            } catch (IOException e) {
                log.error("请求服务器端出错:", e);
                JSONObject error = new JSONObject();
                error.put("msg",e.getMessage());
                return error;
            } finally {
                httpPost.releaseConnection();
            }
            return jsonResult;
        }
    
        public static JSONObject httpGet(String uri, Map<String, String> mapParam) {
            String result = null;
            String url = getUrl() + uri;
            log.info("access url:{}", url);
    
            CloseableHttpClient httpClient = HttpClients.createDefault();
            List<NameValuePair> pairs = new ArrayList<NameValuePair>();
            for (Map.Entry<String, String> entry : mapParam.entrySet()) {
                pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            CloseableHttpResponse response = null;
    
            try {
                URIBuilder builder = new URIBuilder(url);
                builder.setParameters(pairs);
                HttpGet get = new HttpGet(builder.build());
                response = httpClient.execute(get);
                if (response != null && response.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = response.getEntity();
                    result = entityToString(entity);
                }
                return JSONObject.parseObject(result);
            } catch (Exception e) {
                log.error("请求服务器端出错:", e);
                JSONObject error = new JSONObject();
                error.put("msg",e.getMessage());
                return error;
            } finally {
                try {
                    httpClient.close();
                    if (response != null) {
                        response.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static String entityToString(HttpEntity entity) throws IOException {
            String result = null;
            if (entity != null) {
                long lenth = entity.getContentLength();
                if (lenth != -1 && lenth < 2048) {
                    result = EntityUtils.toString(entity, "UTF-8");
                } else {
                    InputStreamReader reader1 = new InputStreamReader(entity.getContent(), "UTF-8");
                    CharArrayBuffer buffer = new CharArrayBuffer(2048);
                    char[] tmp = new char[1024];
                    int l;
                    while ((l = reader1.read(tmp)) != -1) {
                        buffer.append(tmp, 0, l);
                    }
                    result = buffer.toString();
                }
            }
            return result;
        }
    
        private StandardEnvironment environment;
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = (StandardEnvironment) environment;
            serverUrlList = getServerUrlInfo(environment);
    
        }
    
        private List<String> getServerUrlInfo(Environment environment) {
            String serverUrl = environment.getProperty(SERVERURL);
            if (StringUtils.isNotBlank(environment.getProperty(LOAD_BALANC_STRATEGY))) {
                DEFAULT_LOAD_BALANC_STRATEGY = environment.getProperty(LOAD_BALANC_STRATEGY);
            }
            log.info("logistics queue server url:{}", serverUrl);
            if (StringUtils.isBlank(serverUrl)) {
                throw new IllegalArgumentException("logistics queue server url is not empty");
            }
            if (!serverUrl.contains(SERVERURL_SEPARATOR)) {
                serverUrlList.add(serverUrl);
            }
            return Arrays.asList(serverUrl.split(SERVERURL_SEPARATOR));
        }
    
    
        /**
         * 

    根据负载策略获取访问的logistics server url

    * * @return 根据负载策略返回具体的访问服务地址 */
    @SneakyThrows private static String getUrl() { //find load balance strategy, // use simple factory handler load balance LoadBalance loadBalance = LoadBalancFactory.getLoadBalanc(DEFAULT_LOAD_BALANC_STRATEGY); return loadBalance.choseServerUrl(serverUrlList); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    3.4.6 轮询策略接口
    package com.logistics.strategy;
    
    import java.util.List;
    
    /**
     * @author zx
     * @date 2022年09月28日 9:14
     */
    public interface LoadBalance {
        /**
         * 选择一个服务访问地址
         * @param serverUrlList 服务访问地址集合
         * @return  负载均衡后的一个地址
         */
        String choseServerUrl(List<String> serverUrlList);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3.4.7 轮询策略的实现
    package com.logistics.strategy.laodbalance;
    
    import com.logistics.strategy.LoadBalance;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * 

    默认的轮询负载

    * * @author zx * @date 2022年09月28日 9:14 */
    @Slf4j public class DefaultPollLoadBalance implements LoadBalance { /** * 记录访问请求次数 */ private AtomicInteger recordRequest = new AtomicInteger(0); @Override public String choseServerUrl(List<String> serverUrlList) { int size = serverUrlList.size(); int current; int next; do { current = recordRequest.get(); next = current >= Integer.MAX_VALUE ? 0 : current + 1; }while (!recordRequest.compareAndSet(current,next)); int index = next % size; String url = serverUrlList.get(index); log.info("current :{},next:{},index:{},request url:{}",current,next,index,url); return url; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    3.4.8 轮询工厂
    package com.logistics.strategy;
    
    import com.logistics.strategy.laodbalance.DefaultPollLoadBalance;
    
    import java.util.Map;
    import java.util.Optional;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * 

    负载工厂得到具体的负载

    * * @author zx * @date 2022年09月28日 9:57 */
    public class LoadBalancFactory { private static Map<String,LoadBalance> loadBalanceMap = new ConcurrentHashMap<>(); static { loadBalanceMap.put("com.logistics.strategy.laodbalance.DefaultPollLoadBalance",new DefaultPollLoadBalance()); } public static LoadBalance getLoadBalanc(String loadBalancClassType){ return Optional.ofNullable(loadBalanceMap.get(loadBalancClassType)) .orElseThrow(() -> new IllegalArgumentException("loadBalancClassType:" + loadBalancClassType + " not find ,please check !!!!")); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    3.4.9 先关实体对象定义
    package com.logistics.model.bo;
    
    import com.logistics.model.QueueTypeEnums;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class OrderTransportDTO {
    
        /**
         * 车牌号
         */
        private String carNumber;
    
        /**
         * 运单号
         */
        private String waybillNumber;
    
    
        /**
         * 排队类型: PURCHASE | SALE
         */
        private QueueTypeEnums queueTypeEnums;
    
        private List<TransInfo> transInfos;
    
        private Map<String,Object> paramTransmit = new HashMap<>(16);
    
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class TransInfo{
    
            /**
             * 物料编码
             */
            private String materialCode;
    
            /**
             * 物料名称
             */
            private String materalName;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    package com.logistics.model.bo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    /**
     * 

    排队请求业务对象

    * @author zx * @date 2022年09月28日 13:37 */
    @Data @AllArgsConstructor @NoArgsConstructor public class QueueRequestBO { /** * 车牌号 */ private String carNo; /** * 运单号 */ private String waybillNo; /** * 排队类型 */ private String typ; /** * 物料编码集合 */ private List<String> materialNo; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    package com.logistics.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    /**
     * 

    类型枚举

    * * @author zx * @date 2022年09月19日 14:12 */
    @AllArgsConstructor @Getter public enum QueueTypeEnums { /** * (采购) */ PURCHASE, /** * 销售 */ SALE; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    3.4.10 配置类
    package com.logistics.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    /**
     * @author zx
     * @date 2022年09月27日 17:23
     */
    @ConfigurationProperties(prefix = "logistics.queue")
    public class LogisticsQueueProperties {
        /**
         * 物流排队服务地址
         * http://localhost:9003
         */
        private String serverUrl;
        /**
         * 负载策略
         */
        private String nlb;
    
        public String getServerUrl() {
            return serverUrl;
        }
    
        public String getNlb() {
            return nlb;
        }
    
        public void setNlb(String nlb) {
            this.nlb = nlb;
        }
    
        public void setServerUrl(String serverUrl) {
            this.serverUrl = serverUrl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    package com.logistics.config;
    
    import com.logistics.rpc.LogisticsQueueBeanDefinitionRegister;
    import com.logistics.rpc.LogisticsQueueHttpClient;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    物流排队配置类

    * * @author zx * @date 2022年09月27日 17:18 */
    @Configuration @EnableConfigurationProperties(LogisticsQueueProperties.class) @Import({LogisticsQueueBeanDefinitionRegister.class, LogisticsQueueHttpClient.class}) public class LogisticsQueueConfig { }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    3.4.11 META-INFO/spring.factory
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.logistics.config.LogisticsQueueConfig
    
    • 1
    • 2
    • 3

    3.5 @Import + DeferredImportSelector

    DeferredImportSelector 它是 ImportSelector 的子接口,所以实现的方法和第二种无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件 具有延迟延迟导入,如果发现容器中已经存在开发人员导入的对象,那么实现改接口的导入都不会进行导入.

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import org.springframework.context.annotation.DeferredImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 

    延迟导入

    * * @author zx * @date 2022年10月27日 10:38 */
    public class MyDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{StudentInfo.class.getName()}; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    配置类导入注解中加入延迟导入:

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") @Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyDeferredImportSelector.class}) public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    测试:

    package com.tomdd;
    
    import com.tomdd.config.SpringBeanConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.Arrays;
    
    /**
     * @author zx
     * @date 2022年10月27日 10:40
     */
    public class SpringBeanMainTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
            Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        }
    }
    /* 
    只有一个StudentInfo对象
    打印:
    springBeanConfig
    order
    com.tomdd.model.StudentInfo
    com.tomdd.model.DepartmentInfo
    user
    employee
    
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.6 扩展: springboot SPI加载配置鳄梨

    比如我们定义一个自动装配的接口,里面没有方法。其他的配置类实现改接口。使用延迟导入形式导入容器中

    AutoConfiguration

    package com.tomdd.simulation.tomimport;
    
    import com.tomdd.simulation.config.AutoConfiguration;
    import org.springframework.context.annotation.DeferredImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    import java.util.ArrayList;
    import java.util.ServiceLoader;
    
    /**
     * 

    批量导入配置类

    * 这个 @ImportSelect 也是导入功能 *

    * SpringBoot spring.factory SPI机制 * * @author zx * @date 2022年09月24日 12:54 */ public class TomBatchImport implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //基于java SPI 进行加载 ServiceLoader serviceLoader = ServiceLoader.load(AutoConfiguration.class); ArrayList list = new ArrayList<>(); for (AutoConfiguration autoConfiguration : serviceLoader) { list.add(autoConfiguration.getClass().getName()); } return list.toArray(new String[0]); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.7 使用FactoryBean接口

    FactoryBean, 后缀为bean,那么它其实就是一个bean;

    FactoryBean, 后缀为bean,那么它其实就是一个bean

    package com.tomdd.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    /**
     * 

    运单实体

    * * @author zx * @date 2022年10月27日 10:57 */
    @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class Waybill { /** * 车牌号 */ private String carNo; /** * 运单号 */ private String waybillNo; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    定义WaybillFactoryBean:

    package com.tomdd.model;
    
    import org.springframework.beans.factory.FactoryBean;
    
    /**
     * 

    运单实体的工厂bean

    * * @author zx * @date 2022年10月27日 10:58 */
    public class WaybillFactoryBean implements FactoryBean<Waybill> { @Override public Waybill getObject() throws Exception { return new Waybill(); } @Override public Class<?> getObjectType() { return Waybill.class; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    配置类中注入WaybillFactoryBean:

    package com.tomdd.config;
    
    import com.tomdd.model.StudentInfo;
    import com.tomdd.model.User;
    import com.tomdd.model.WaybillFactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * 

    bean 配置类

    * * @author zx * @date 2022年10月27日 9:08 */
    @Configuration @ComponentScan("com.init") @Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyDeferredImportSelector.class}) public class SpringBeanConfig { @Bean public User user() { return new User("zhongxu", 34); } @Bean public WaybillFactoryBean waybillFactoryBean(){ return new WaybillFactoryBean(); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    测试访问:

    • 查看容器中注入的类型是什么:
    package com.tomdd;
    
    import com.tomdd.config.SpringBeanConfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.Arrays;
    
    /**
     * @author zx
     * @date 2022年10月27日 10:40
     */
    public class SpringBeanMainTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
            Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        }
    }
    /*
    在容器中类型名称: waybillFactoryBean
    打印结果:
    springBeanConfig
    order
    com.tomdd.model.StudentInfo
    com.tomdd.model.DepartmentInfo
    user
    waybillFactoryBean
    employee
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 使用Waybill对象:
    @Autowired
    private WaybillFactoryBean waybillFactoryBean;
    
    @GetMapping("/getWaybillInfo")
    @ApiOperation("通过FactoryBean+@Configuration形式注入Waybill对象")
    public BaseResponse<?> getWaybillInfo() throws Exception {
        Waybill waybill = waybillFactoryBean.getObject();
        waybill.setWaybillNo("CG202210270001").setCarNo("渝A23B4");
        return BaseResponse.ok(waybill);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.8 使用 BeanDefinitionRegistryPostProcessor

    这种方式也是利用到了 BeanDefinitionRegistry,在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法,大概意思就是等beanDefinition加载完毕之后,对beanDefinition进行后置处理,可以在此进行调整IOC容器中的beanDefinition,从而干扰到后面进行初始化bean

    package com.tomdd.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    /**
     * 

    老师实体对象

    * * @author zx * @date 2022年10月27日 11:11 */
    @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class Teacher { /** * 老师名称 */ private String name; /** * 授课内容 */ private String content; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    自定义bean定义注册后置处理器:

    package com.tomdd.config;
    
    import com.tomdd.model.Teacher;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    
    /**
     * 

    自定义bean定义注册后置处理器

    * * @author zx * @date 2022年10月27日 11:10 */
    public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Teacher.class).getBeanDefinition(); registry.registerBeanDefinition("teacher", beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    测试:

    package com.tomdd;
    
    import com.tomdd.config.MyBeanDefinitionRegistryPostProcessor;
    import com.tomdd.model.Teacher;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.Arrays;
    
    /**
     * @author zx
     * @date 2022年10月27日 10:40
     */
    public class SpringBeanMainTest {
        public static void main(String[] args) {
            //AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
            //Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
    
            testBeanDefinitionRegistryPostProcessor();
        }
    
    
        public static void testBeanDefinitionRegistryPostProcessor(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
            applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
            applicationContext.refresh();
    
            Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
            System.out.println("----------------------");
            Teacher bean = applicationContext.getBean(Teacher.class);
            bean.setName("钟老师").setContent("授课内容:JAVA后端高级开发");
            System.out.println(bean);
        }
    }
    
    /*
    打印结果:
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    teacher
    ----------------------
    Teacher(name=钟老师, content=授课内容:JAVA后端高级开发)
    
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    这里没有选择加载哪个配置类,我们手动向beanDefinitionRegistry中注册了person的BeanDefinition。最终成功将teacher加入到applicationContext中,所以在打印bean定义名称的时候只有teacher。

  • 相关阅读:
    LinkList集合方法(自写)
    Autosar MCAL配置——SPI(EB)
    算法之回溯
    Armv8-R系列之何为MPU?
    11.10记录纪要
    花2个月时间学习,面华为测开岗要30k,面试官竟说:你不是在搞笑。。。
    NOIP2023模拟14联测35 charlotte
    C++知识精讲9——sqrt函数函数基本使用方法以及实战讲解
    NP3 格式化输出(一)
    C++学习笔记(17)
  • 原文地址:https://blog.csdn.net/zhongxu_yuan/article/details/127549017