参考学习地址: https://mp.weixin.qq.com/s/OQPxh5Y9m-YAIOIyUIISiA
蚂蚁课堂: https://xiaoe.mayikt.com/
在开发中使用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);
}
}
@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;
}
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);
}
}
/**
* @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);
}
}
@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;
}
配置类上加上路径扫描:
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);
}
}
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);
}
}
在进行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();
}
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;
}
在配置类上导入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);
}
}
测试:
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);
}
}
实现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;
}
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"};
}
}
配置类中加入自定义选择器:
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);
}
}
测试访问:
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);
}
}
实现 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);
}
}
配置类导入注解中加入自定义导入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);
}
}
测试访问:
@Autowired
private EmployeeInfo employee;
@GetMapping("/getEmployeeInfo")
@ApiOperation("导入bean定义注册形式")
public BaseResponse<?> getEmployeeInfo(){
employee.setEmployeeNo("K0001").setDepartmentName("研发部");
return BaseResponse.ok(employee);
}
我在物流项目中真实案例,一个简单的ERP远程调用的一个starter插件案例
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;
}
}
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);
}
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
}
}
思考:多个访问接口如何取注册了? 通过反射指定包名,获取改包下的所有的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;
}
}
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;
}
}
简单实现一个轮询策略
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);
}
}
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);
}
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;
}
}
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 !!!!"));
}
}
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;
}
}
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;
}
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;
}
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;
/**
* 物流排队配置类
*
* @author zx
* @date 2022年09月27日 17:18
*/
@Configuration
@EnableConfigurationProperties(LogisticsQueueProperties.class)
@Import({LogisticsQueueBeanDefinitionRegister.class, LogisticsQueueHttpClient.class})
public class LogisticsQueueConfig {
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.logistics.config.LogisticsQueueConfig
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()};
}
}
配置类导入注解中加入延迟导入:
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);
}
}
测试:
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
*/
比如我们定义一个自动装配的接口,里面没有方法。其他的配置类实现改接口。使用延迟导入形式导入容器中
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]);
}
}
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;
}
定义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;
}
}
配置类中注入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();
}
}
测试访问:
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
*/
@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);
}
这种方式也是利用到了 BeanDefinitionRegistry
,在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor
的 postProcessBeanDefinitionRegistry
方法,大概意思就是等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;
}
自定义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 {
}
}
测试:
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。