文章目录

  • 将 Bean 放入 Spring 容器中的方式
    • 1.@Configuration + @Bean
    • 2.@Componet + @ComponentScan
    • 3.@Import注解导入
      • 3.1 @Import直接导入类
      • 3.2 @Import + ImportSelector
      • 3.3 @Import + ImportBeanDefinitionRegistrar
      • 3.4 通过bean定义注册后置处理器+ FactoryBean 创建代理进行ERP远程调用
        • 3.4.1 定义FactoryBean
        • 3.4.2 定义远程调用的接口
        • 3.4.3 自定义bean定义注册后置处理器
        • 3.4.4 定义代理执行的InvocationHandler
        • 3.4.5 定义远程调用httpclient
        • 3.4.6 轮询策略接口
        • 3.4.7 轮询策略的实现
        • 3.4.8 轮询工厂
        • 3.4.9 先关实体对象定义
        • 3.4.10 配置类
        • 3.4.11 META-INFO/spring.factory
      • 3.5 @Import + DeferredImportSelector
      • 3.6 扩展: springboot SPI加载配置鳄梨
      • 3.7 使用FactoryBean接口
      • 3.8 使用 BeanDefinitionRegistryPostProcessor

参考学习地址: 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;/*** <h1>spring bean注入容器中的启动类</h1>* <p>* 启动类需要扫描到配置类,即启动类包含配置类到配置类** @author zx* @date 2022年10月27日 9:05*/
@SpringBootApplication
public class SpringBeanApplication {public static void main(String[] args) {SpringApplication.run(SpringBeanApplication.class);}
}

1.@Configuration + @Bean

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

package com.tomdd.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;/*** <h1>用户实体</h1>** @author zx* @date 2022年10月27日 9:07*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {private String name;private Integer age;}
package com.tomdd.config;import com.tomdd.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}
/*** @author zx* @date 2022年10月27日 9:09*/
@RestController
@Api(tags = "用户服务接口")
public class UserService {@Autowiredprivate User user;@GetMapping("/getUser")@ApiOperation("从容器中获取用户信息")public BaseResponse<?> getUserInfo() {return BaseResponse.ok(user);}}

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;/*** <h1>订单实体对象</h1>** @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;
}

配置类上加上路径扫描:

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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}
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 {@Autowiredprivate Order order;@GetMapping("/getOrderInfo")@ApiOperation("从容器中获取订单信息")public BaseResponse<?> getOrderInfo() {order.setName("apple phone 13 pro").setPlaceOrderDate(new Date()).setPrice(5321.3);return BaseResponse.ok(order);}}

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();}

3.1 @Import直接导入类

package com.tomdd.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;/*** <h1>学生信息类</h1>** @author zx* @date 2022年10月27日 9:29*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class StudentInfo {/*** 学生名称*/private String name;/*** 班级*/private String classGrade;
}

在配置类上导入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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
@Import(StudentInfo.class)
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}

测试:

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;/*** <h1>通用的服务接口</h1>** @author zx* @date 2022年10月27日 9:31*/
@RestController
@Api(tags = "通用服务接口")
public class CommonService {@Autowiredprivate StudentInfo studentInfo;@GetMapping("/getSudentInfo")@ApiOperation("通过直接导入@Import 方式")public BaseResponse<?> getStudentInfo(){studentInfo.setName("tomdd").setClassGrade("Java高级培训班");return BaseResponse.ok(studentInfo);}
}

3.2 @Import + ImportSelector

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

package com.tomdd.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;/*** <h1>部分信息</h1>** @author zx* @date 2022年10月27日 9:41*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DepartmentInfo {/*** 部门名称*/private String name;/*** 部门经理*/private String manager;}
package com.tomdd.config;import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;/*** <h1>自定义导入选择器</h1>** @author zx* @date 2022年10月27日 9:40*/
public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.tomdd.model.DepartmentInfo"};}
}

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

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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
@Import({StudentInfo.class,MyImportSelector.class})
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}

测试访问:

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;/*** <h1>通用的服务接口</h1>** @author zx* @date 2022年10月27日 9:31*/
@RestController
@Api(tags = "通用服务接口")
public class CommonService {@Autowiredprivate DepartmentInfo departmentInfo;@GetMapping("/getDepartmentInfo")@ApiOperation("通过自定义ImportSelector方式进行导入")public BaseResponse<?> getDepartmentInfo(){departmentInfo.setManager("zhongxu").setName("研发部");return BaseResponse.ok(departmentInfo);}
}

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;/*** <h1>自定义导入bean定义注册</h1>** @author zx* @date 2022年10月27日 10:03*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic 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);}
}

配置类导入注解中加入自定义导入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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
@Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}

测试访问:

@Autowired
private EmployeeInfo employee;@GetMapping("/getEmployeeInfo")
@ApiOperation("导入bean定义注册形式")
public BaseResponse<?> getEmployeeInfo(){employee.setEmployeeNo("K0001").setDepartmentName("研发部");return BaseResponse.ok(employee);
}

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;}@Overridepublic T getObject() {InvocationHandler handler = new ServiceProxy<>(interfaceType);return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),new Class[]{interfaceType}, handler);}@Overridepublic Class<T> getObjectType() {return interfaceType;}@Overridepublic boolean isSingleton() {return true;}
}

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;/*** <h1>物流排队访问客户端</h1>** @author zx* @date 2022年09月27日 17:28*/
public interface LogisticsQueueClient {/*** <h2>测试接口 Get请求方式</h2>** @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);}

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;/*** <h1>物流排队调用接口bean定义注册到容器[代理对象]</h1>** @author zx* @date 2022年09月27日 17:35*/
@Slf4j
public class LogisticsQueueBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor {/*** bean定义的后置处理器回调方法*/@Overridepublic 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);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//bean factory post process  , do not anyhing}}

思考:多个访问接口如何取注册了? 通过反射指定包名,获取改包下的所有的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;@Overridepublic 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;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

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;}@Overridepublic 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;}
}

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;@Overridepublic 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));}/*** <h2>根据负载策略获取访问的logistics server url</h2>** @return 根据负载策略返回具体的访问服务地址*/@SneakyThrowsprivate static String getUrl() {//find load balance strategy,// use simple factory handler load balanceLoadBalance loadBalance = LoadBalancFactory.getLoadBalanc(DEFAULT_LOAD_BALANC_STRATEGY);return loadBalance.choseServerUrl(serverUrlList);}}

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);
}

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;/*** <h1>默认的轮询负载</h1>** @author zx* @date 2022年09月28日 9:14*/
@Slf4j
public class DefaultPollLoadBalance implements LoadBalance {/*** 记录访问请求次数*/private AtomicInteger recordRequest = new AtomicInteger(0);@Overridepublic 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;}
}

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;/*** <h1>负载工厂得到具体的负载</h1>** @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 !!!!"));}}

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@NoArgsConstructorpublic static class TransInfo{/*** 物料编码*/private String materialCode;/*** 物料名称*/private String materalName;}}
package com.logistics.model.bo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;/*** <h1>排队请求业务对象</h1>* @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;
}
package com.logistics.model;import lombok.AllArgsConstructor;
import lombok.Getter;/*** <h1>类型枚举</h1>** @author zx* @date 2022年09月19日 14:12*/
@AllArgsConstructor
@Getter
public enum QueueTypeEnums {/*** (采购)*/PURCHASE,/*** 销售*/SALE;
}

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;}
}
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;/*** <h1>物流排队配置类</h1>** @author zx* @date 2022年09月27日 17:18*/
@Configuration
@EnableConfigurationProperties(LogisticsQueueProperties.class)
@Import({LogisticsQueueBeanDefinitionRegister.class, LogisticsQueueHttpClient.class})
public class LogisticsQueueConfig {}

3.4.11 META-INFO/spring.factory

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.logistics.config.LogisticsQueueConfig

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;/*** <h1>延迟导入</h1>** @author zx* @date 2022年10月27日 10:38*/
public class MyDeferredImportSelector implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{StudentInfo.class.getName()};}
}

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

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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
@Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyDeferredImportSelector.class})
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}
}

测试:

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*/

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;/*** <h1>批量导入配置类</h1>* 这个 @ImportSelect 也是导入功能* <p>* SpringBoot spring.factory  SPI机制** @author zx* @date 2022年09月24日 12:54*/
public class TomBatchImport implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {//基于java SPI 进行加载ServiceLoader<AutoConfiguration> serviceLoader = ServiceLoader.load(AutoConfiguration.class);ArrayList<String> list = new ArrayList<>();for (AutoConfiguration autoConfiguration : serviceLoader) {list.add(autoConfiguration.getClass().getName());}return list.toArray(new String[0]);}
}

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;/*** <h1>运单实体</h1>** @author zx* @date 2022年10月27日 10:57*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Waybill {/*** 车牌号*/private String carNo;/*** 运单号*/private String waybillNo;
}

定义WaybillFactoryBean:

package com.tomdd.model;import org.springframework.beans.factory.FactoryBean;/*** <h1>运单实体的工厂bean</h1>** @author zx* @date 2022年10月27日 10:58*/
public class WaybillFactoryBean implements FactoryBean<Waybill> {@Overridepublic Waybill getObject() throws Exception {return new Waybill();}@Overridepublic Class<?> getObjectType() {return Waybill.class;}
}

配置类中注入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;/*** <h1>bean 配置类</h1>** @author zx* @date 2022年10月27日 9:08*/
@Configuration
@ComponentScan("com.init")
@Import({StudentInfo.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyDeferredImportSelector.class})
public class SpringBeanConfig {@Beanpublic User user() {return new User("zhongxu", 34);}@Beanpublic WaybillFactoryBean waybillFactoryBean(){return new WaybillFactoryBean();}
}

测试访问:

  • 查看容器中注入的类型是什么:
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
*/
  • 使用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);
}

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;/*** <h1>老师实体对象</h1>** @author zx* @date 2022年10月27日 11:11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Teacher {/*** 老师名称*/private String name;/*** 授课内容*/private String content;
}

自定义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;/*** <h1>自定义bean定义注册后置处理器</h1>** @author zx* @date 2022年10月27日 11:10*/
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Teacher.class).getBeanDefinition();registry.registerBeanDefinition("teacher", beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

测试:

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后端高级开发)*/

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

将 Bean 放入 Spring 容器中的方式相关推荐

  1. 将Bean放入Spring容器中的五种方式

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/weixin_43741092/ article/details/120176466 将bean放 ...

  2. SpringBoot教程(十一)——将Bean放入Spring容器中的五种方式

    将bean放入Spring容器中有哪些方式? 我们知道平时在开发中使用Spring的时候,都是将对象交由Spring去管理,那么将一个对象加入到Spring容器中,有哪些方式呢,下面我就来总结一下 1 ...

  3. Bean放入Spring容器,你知道几种方式?

    作者:三尺微命  一介书生 来源:blog.csdn.net/weixin_43741092/article/details/120176466 我们知道平时在开发中使用Spring的时候,都是将对象 ...

  4. 如何把对象放入spring容器

    在我们开发中,很常见的会遇见我们自己的对象依赖于Spring容器中的对象,此时需要将我们的对象托管给Spring容器,否则我们将无法使用依赖的对象. 下面介绍一下将我们的对象托管给Spring容器的三 ...

  5. 小孩的奇思妙想:将枚举注入到spring容器中

    背景 笔者目前做的项目中有许多根据前台传参,然后根据参数获取不同实现类,取执行不同的代码逻辑,代码类似如下: 下面展示一些. 现有实现 /*** 执行逻辑** @param type : 实现类类型* ...

  6. Spring容器中获取Bean实例的七种方式(附实战源码)

    目录 写作说明 一:写作原因 二:源码出处 实现方式 一:使用BeanFactory直接获取(不推荐) 二:在初始化时保存ApplicationContext对象 三:继承自抽象类Applicatio ...

  7. 【SpringBoot】在普通类中获取spring容器中的bean

    这段时间公司搞封闭开发,做一个联通总部的客服系统项目,是基于springboot的.在开发工程中遇到一个页面datagrid数据排序的功能,因为有多个表的数据都要用到排序功能,于是我就写了一个排序功能 ...

  8. Spring注解驱动开发第11讲——面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?

    写在前面 经过前面的学习,我们知道可以通过多种方式向Spring容器中注册bean.可以使用@Configuration注解结合@Bean注解向Spring容器中注册bean:可以按照条件向Sprin ...

  9. Spring容器中获取 Bean 实例的七种方式

    点击关注公众号,利用碎片时间学习 说明 一.写作原因 首先解释一下写这篇博文的原因,因为在使用spring框架的过程中获取bean是非常常见的操作,但是网上非常的博文大多承自一家之言,因此很多可操作性 ...

最新文章

  1. R语言deLong‘s test:通过统计学的角度来比较两个ROC曲线、检验两个ROC曲线的差异是否具有统计显著性
  2. Python Xml类
  3. 网银和银企直联的区别
  4. 机器学习理论与实战(十五)概率图模型03
  5. Eclipse 常用快捷键及使用技巧!
  6. css3魔方3乘3每层旋转_纯CSS做3D旋转魔方
  7. Fail: Failover,Failfast,Failback,Failsafe
  8. 计算机专业考研是英语几,计算机考研考英语一还是英语二
  9. Win10怎么添加开机启动项?Win10添加开机自动运行软件三种方法
  10. mysql 内置函数大全 mysql内置函数大全
  11. 字节跳动2023届校招薪资盘点!
  12. Android R上展讯平台CameraAPP的Settings设置项管理
  13. 图神经网络-图与图学习笔记-1
  14. python文件调用python文件_自己写的python文件如何相互调用
  15. 分享139个ASP源码,总有一款适合您
  16. 影响中国历史的五场战争
  17. 推荐系统的矩阵分解和FM模型
  18. 绝地求生国际服请求超时服务器未响应,绝地求生connection timeout 3.6.7解决方法/游戏连接超时怎么办...
  19. Certificates 证书
  20. 高逼格PPT技能以及普通PPT技能档次提升学习

热门文章

  1. 数据包分析(wireshark常用)
  2. 什么是高质量的赞美呢?
  3. Vue简单的实现五星级评分
  4. 知识点整理,Java基础面试题(一)
  5. 南卡与vivo蓝牙耳机哪个好?南卡小音舱与vivo TWS 2对比评测
  6. Ws91.cn短链接精准计算群发短信营销转化率
  7. uni-app.05.底部按钮fixed定位后被软键盘推起的解决办法
  8. 块元素 行内元素 行内块元素各自的特点
  9. oracle 19c rpm安装
  10. 输出图案(一)----输出三角形图案:(难度系数:小于半颗星)