在 Java 中维护可扩展性的几种例程和实现方法
最编程
2024-03-24 20:12:17
...
在日常的开发中,作者总结了平常用到的一些低成本、保持扩展性的套路,分享出来,欢迎大家讨论。
前言
SOLID(单一、开闭、里式替换、接口隔离、依赖倒置)五大原则和23种设计模式(常见的单例、构建者、装饰、适配、代理、组合、模板等等),小伙伴们对这些肯定都很熟悉。这些原则和设计模式能够辅助我们,让我们在设计的时候有所抉择,从而达到高内聚、低耦合的目的。
那说到设计,肯定会提到架构两个字,常见的架构名词:分层架构、六边形架构、SOA架构、CQRS架构、EDA架构等等。我个人对架构的理解是边界的确认以及边界内部元素的组合,其实只要是程序员,他就一定是架构师,只不过是好的架构师还是不那么好的架构师;人人都是架构师,我认为这句话是没有问题的,区别在于他的认知决定了他所能确认的边界、如何更高效的组合内部元素;技术的架构师肯定侧重于技术,业务的架构师肯定侧重于业务,商品的架构师所能看到的边界大概率还是局限在商品域,ICBU架构组的架构师更多考虑是横向的业务支撑。以上是我个人对架构两个字的理解,今天我们不讨论具体架构,我们讨论一些套路,在日常的开发中,我总结了些我平常用到的一些低成本、保持扩展性的套路,分享出来,欢迎大家讨论。
基于管道(pipeline)的套路
关键点
- 管道(Pipeline)----用于串联阀门的管道通路
- 阀门(PipelineValue)----用于每一个节点处理实际业务诉求
- 管道上下文(PipelineContext)----用于管道上下文中数据的扭转
适用场景
- 当你的数据流需要经过很多同等逻辑处理时,可以考虑使用此套路,便于后续扩展
实现代码
- Pipeline/StandardPipeline
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:46
*/
public interface Pipeline {
/**
* 执行
*
* @return
*/
boolean invoke(PipelineContext pipelineContext);
/**
* 添加值
*
* @param pipelineValue
* @return
*/
boolean addValue(PipelineValue pipelineValue);
/**
* 移除值
*
* @param pipelineValue
* @return
*/
boolean removeValue(PipelineValue pipelineValue);
}
package com.example.ownertest.dm.pipelline;
import java.util.List;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:46
*/
@Data
@Slf4j
public class StandardPipeline implements Pipeline {
private List<PipelineValue> pipelineValueList = Lists.newArrayList();
@Override
public boolean invoke(PipelineContext pipelineContext) {
boolean isResult = true;
for (PipelineValue pipelineValue :
pipelineValueList) {
try {
isResult = pipelineValue.execute(pipelineContext);
if (!isResult) {
log.error("{},exec is wrong", pipelineValue.getClass().getSimpleName());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
return isResult;
}
@Override
public boolean addValue(PipelineValue pipelineValue) {
if (pipelineValueList.contains(pipelineValue)) {
return true;
}
return pipelineValueList.add(pipelineValue);
}
@Override
public boolean removeValue(PipelineValue pipelineValue) {
return pipelineValueList.remove(pipelineValue);
}
}
- PipelineContext/StandardPipelineContext
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:47
*/
public interface PipelineContext {
String FOR_TEST = "forTest";
/**
* 设置
*
* @param contextKey
* @param contextValue
*/
void set(String contextKey, Object contextValue);
/**
* 获取值
*
* @param contextKey
* @return
*/
Object get(String contextKey);
}
package com.example.ownertest.dm.pipelline;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:47
*/
public class StandardPipelineContext implements PipelineContext {
private Map<String, Object> contentMap = Maps.newConcurrentMap();
@Override
public void set(String contextKey, Object contextValue) {
contentMap.put(contextKey, contextValue);
}
@Override
public Object get(String contextKey) {
return contentMap.get(contextKey);
}
}
- PipelineValue/AbstractPipelineValue/GraySwitchValue/ForTestValue
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:47
*/
public interface PipelineValue {
/**
* 节点执行
*
* @param pipelineContext
* @return
*/
boolean execute(PipelineContext pipelineContext);
}
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:48
*/
public abstract class AbstractPipelineValue implements PipelineValue {
@Override
public boolean execute(PipelineContext pipelineContext) {
System.out.println(this.getClass().getSimpleName() + " start ");
boolean result = doExec(pipelineContext);
System.out.println(this.getClass().getSimpleName() + " end ");
return result;
}
protected abstract boolean doExec(PipelineContext pipelineContext);
}
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:48
*/
public class GraySwitchValue extends AbstractPipelineValue {
@Override
public boolean doExec(PipelineContext pipelineContext) {
pipelineContext.set(PipelineContext.FOR_TEST, true);
return true;
}
}
package com.example.ownertest.dm.pipelline;
/**
* @Author: linear.zw
* @Date: 2023/10/25 19:48
*/
public class ForTestValue extends AbstractPipelineValue {
@Override
public boolean doExec(PipelineContext pipelineContext) {
return true;
}
}
- PipelineClient
package com.example.ownertest.dm.pipelline;
/**
* 入口类
*
* @Author: linear.zw
* @Date: 2023/10/25 19:48
*/
public class PipelineClient {
public static void main(String[] args) {
// 管道初始化
Pipeline pipeline = new StandardPipeline();
// value扩展
PipelineValue pipelineValue = new GraySwitchValue();
PipelineValue pipelineValue2 = new ForTestValue();
// 上下文
PipelineContext pipelineContext = new StandardPipelineContext();
pipeline.addValue(pipelineValue);
pipeline.addValue(pipelineValue2);
// 调用管道
pipeline.invoke(pipelineContext);
}
}
常见框架中的应用
- 网络层的扛把子netty框架中,例如ChannelPipeline、ChannelHandler、ChannelHandlerContext,分别用于处理tcp拆包、加解码等等之类。
基于责任链(filterchain)的套路
关键点
来源--https://www.oracle.com/java/technologies/intercepting-filter.html
- 过滤器(Filter)----实际处理业务的节点
- 过滤链(FilterChain)----串联过滤器的链条
适用场景
- 例如常见的web请求场景
实现代码
- Filter/ForTest1Filter/ForTest2Filter
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:22
*/
public interface Filter {
void doFilter(HttpRequest httpRequest,FilterChain filterChain);
}
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:22
*/
public class ForTest1Filter implements Filter {
@Override
public void doFilter(HttpRequest httpRequest, FilterChain filterChain) {
// do
System.out.println(this.getClass().getSimpleName() + " before " + System.currentTimeMillis());
filterChain.doFilter(httpRequest);
// after
System.out.println(this.getClass().getSimpleName() + " end " + System.currentTimeMillis());
}
}
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:22
*/
public class ForTest2Filter implements Filter {
@Override
public void doFilter(HttpRequest httpRequest, FilterChain filterChain) {
// do
System.out.println(this.getClass().getSimpleName() + " before " + System.currentTimeMillis());
filterChain.doFilter(httpRequest);
// after
System.out.println(this.getClass().getSimpleName() + " end " + System.currentTimeMillis());
}
}
- FilterChain/StandardFilterChain
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:23
*/
public interface FilterChain {
void doFilter(HttpRequest httpRequest);
void addFilter(Filter filter);
}
package com.example.ownertest.dm.filter;
import java.util.List;
import com.google.common.collect.Lists;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:24
*/
public class StandardFilterChain implements FilterChain {
private List<Filter> filterList = Lists.newArrayList();
private int currentIndex = 0;
@Override
public void doFilter(HttpRequest httpRequest) {
if (currentIndex == filterList.size()) { return; }
Filter filter = filterList.get(currentIndex);
currentIndex = currentIndex + 1;
filter.doFilter(httpRequest, this);
}
@Override
public void addFilter(Filter filter) {
if (filterList.contains(filter)) {
return;
}
filterList.add(filter);
}
}
- HttpRequest/StandardHttpRequest
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:24
*/
public interface HttpRequest {
}
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:24
*/
public class StandardHttpRequest implements HttpRequest {
}
- FilterClient----入口测试
package com.example.ownertest.dm.filter;
/**
* @Author: linear.zw
* @Date: 2023/10/26 19:25
*/
public class FilterClient {
public static void main(String[] args) {
FilterChain filterChain = new StandardFilterChain();
filterChain.addFilter(new ForTest1Filter());
filterChain.addFilter(new ForTest2Filter());
filterChain.doFilter(new StandardHttpRequest());
}
}
常见框架中的应用
- hsf的filter机制,服务端扩展的ServerFilter和客户端扩展的ClientFilter;
- 开发过java web的小伙伴都知道的servlet,servlet的入口即是FilterChain、Filter;
基于组合/模板的套路
关键点
- 处理器注册器----用于存储处理器的集合
- 处理器工厂----用于创建处理器
- 处理器----实际的处理器以及扩展的实现
- 处理器上下文----处理器上下文,用于参数的传递
适用场景
- 适合于有共性、后续持续扩展的场景
实现代码
- PiiHandlerRegistry----处理器注册器
package com.example.ownertest.dm.comp;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:45
*/
@Slf4j
public class PiiHandlerRegistry {
private static Map<String, PiiDomainFieldHandler> piiDomainFieldHandlerMap = Maps.newHashMap();
public static void putHandler(String piiDomainFieldName, PiiDomainFieldHandler piiDomainFieldHandler) {
if (StringUtils.isEmpty(piiDomainFieldName)) {
log.warn(" piiDomainFieldName is null,continue");
return;
}
if (piiDomainFieldHandler == null) {
log.warn(piiDomainFieldName + " piiDomainFieldHandler is null,continue");
return;
}
if (!piiDomainFieldHandlerMap.containsKey(piiDomainFieldName)) {
piiDomainFieldHandlerMap.put(piiDomainFieldName, piiDomainFieldHandler);
}
}
public static <T extends Object> int handlerRead(T domain, Field domainField, PiiContent piiContent) {
int num = 0;
for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :
piiDomainFieldHandlerMap.entrySet()) {
if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {
piiDomainFieldHandlerEntry.getValue().handlerRead(domain, domainField, piiContent);
}
}
return num;
}
public static <T extends Object> int handlerWrite(T domain, Field domainField, PiiContent piiContent) {
int num = 0;
for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :
piiDomainFieldHandlerMap.entrySet()) {
if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {
piiDomainFieldHandlerEntry.getValue().handlerWrite(domain, domainField, piiContent);
}
}
return num;
}
public static Map<String, PiiDomainFieldHandler> getPiiDomainFieldHandlerMap() {
return piiDomainFieldHandlerMap;
}
public static void init() {
List<PiiDomainFieldHandler> piiDomainFieldHandlerList = PiiDomainFieldHandlerFactory
.createPiiDomainFieldHandler();
if (CollectionUtils.isNotEmpty(piiDomainFieldHandlerList)) {
for (PiiDomainFieldHandler piiDomainFieldHandler :
piiDomainFieldHandlerList) {
putHandler(piiDomainFieldHandler.getPiiDomainMeta(), piiDomainFieldHandler);
}
}
}
}
- PiiDomainFieldHandlerFactory----处理器工厂
package com.example.ownertest.dm.comp;
import java.util.List;
import com.google.common.collect.Lists;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:46
*/
public class PiiDomainFieldHandlerFactory {
/**
* 创建领域处理器
*
* @return
*/
public static List<PiiDomainFieldHandler> createPiiDomainFieldHandler() {
List<PiiDomainFieldHandler> piiDomainFieldHandlerList = Lists.newArrayList();
//
piiDomainFieldHandlerList.add(new ForTestSupportFieldHandler());
piiDomainFieldHandlerList.add(new ForTestNotSupportFieldHandler());
return piiDomainFieldHandlerList;
}
}
- PiiDomainFieldHandler/PiiDomainFieldHandlerBase/ForTestNotSupportFieldHandler/ForTestSupportFieldHandler----处理器
package com.example.ownertest.dm.comp;
import java.lang.reflect.Field;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:46
*/
public interface PiiDomainFieldHandler {
/**
* 处理实际操作
* 读----从PiiContent获取数据回填domain
*
* @param domain
* @param domainField
* @param piiContent
* @param <T>
* @return
*/
<T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent);
/**
* 处理实际操作
* 写----将domain中需要写入pii的字段数据写入PiiContent
*
* @param domain
* @param domainField
* @param piiContent
* @param <T>
* @return
*/
<T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent);
/**
* 当前处理器是否支持该领域对象
*
* @param domain
* @param domainField
* @param <T>
* @return
*/
<T extends Object> boolean isSupport(T domain, Field domainField);
/**
* 获取处理器对应的元信息
*
* @return
*/
String getPiiDomainMeta();
}
package com.example.ownertest.dm.comp;
import java.lang.reflect.Field;
import lombok.extern.slf4j.Slf4j;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:47
*/
@Slf4j
public abstract class PiiDomainFieldHandlerBase implements PiiDomainFieldHandler {
@Override
public <T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {
// to do business read
return true;
}
@Override
public <T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent) {
// to do business write
return true;
}
}
package com.example.ownertest.dm.comp;
import java.lang.reflect.Field;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:47
*/
public class ForTestSupportFieldHandler extends PiiDomainFieldHandlerBase {
@Override
public <T> boolean isSupport(T domain, Field domainField) {
if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {
// to do business
System.out.println(this.getClass().getSimpleName() + " is support, to do some business");
return true;
}
return false;
}
@Override
public String getPiiDomainMeta() {
return this.getClass().getSimpleName();
}
}
package com.example.ownertest.dm.comp;
import java.lang.reflect.Field;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:48
*/
public class ForTestNotSupportFieldHandler extends PiiDomainFieldHandlerBase {
@Override
public <T> boolean isSupport(T domain, Field domainField) {
if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {
// to do business
System.out.println(this.getClass().getSimpleName() + " is support, to do some business");
return true;
}
return false;
}
@Override
public String getPiiDomainMeta() {
return this.getClass().getSimpleName();
}
}
- PiiContent----上下文
package com.example.ownertest.dm.comp;
import java.util.Map;
import com.google.common.collect.Maps;
import lombok.Data;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:48
*/
@Data
public class PiiContent {
public static String FORTEST="fortest";
private Map<String, Object> piiDataMap = Maps.newHashMap();
private Map<String, Object> piiContextMap = Maps.newHashMap();
public void putPiiData(String domainFieldName, Object domainFieldValue) {
piiDataMap.put(domainFieldName, domainFieldValue);
}
public Object getPiiData(String domainFieldName) {
return piiDataMap.get(domainFieldName);
}
public void putPiiContext(String contextName, Object contextNameValue) {
piiContextMap.put(contextName, contextNameValue);
}
public Object getPiiContext(String contextName) {
return piiContextMap.get(contextName);
}
}
- PiiClient----入口的测试类
package com.example.ownertest.dm.comp;
import java.util.Map;
/**
* @Author: linear.zw
* @Date: 2023/10/31 20:48
*/
public class PiiClient {
public static void main(String[] args) {
PiiHandlerRegistry.init();
// 遍历处理器
for (Map.Entry<String, PiiDomainFieldHandler> entryHandler :
PiiHandlerRegistry.getPiiDomainFieldHandlerMap().entrySet()) {
System.out.println(entryHandler.getKey() + "\t" + entryHandler.getValue().getPiiDomainMeta());
}
//
PiiContent piiContent = new PiiContent();
piiContent.putPiiContext(PiiContent.FORTEST, PiiContent.FORTEST);
// 请求处理
System.out.println("ForTestSupportFieldHandler start");
PiiHandlerRegistry.handlerRead(new ForTestSupportFieldHandler(), null, piiContent);
System.out.println("ForTestSupportFieldHandler end");
// 请求处理
System.out.println("ForTestNotSupportFieldHandler start");
PiiHandlerRegistry.handlerRead(new ForTestNotSupportFieldHandler(), null, piiContent);
System.out.println("ForTestNotSupportFieldHandler end");
}
}
常见框架中的应用
- 这个就太多了,例如spring最核心的BeanPostProcessor机制,通过org.springframework.beans.factory.support.AbstractBeanFactory#beanPostProcessors管理一些列的beanPostProcessors,在spring上下文org.springframework.context.support.AbstractApplicationContext#refresh的时候,进行bean的init(InitDestroyAnnotationBeanPostProcessor)、解析注解(ScheduledAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor)、解析aop(AnnotationAwareAspectJAutoProxyCreator)等等。
基于注解的套路
关键点
- 注解元定义----用来定义通用的元信息;
- 注解解析器----解析类上是否有指定的注解,进而进行对应的扩展操作;
- spring的BeanPostProcessor----这里是借用spring的BeanPostProcessor机制,在spring容器初始化的时候,进行回调,完成预期的扩展行为;
适用场景
- 简化内部使用
实现代码
- ForTestAnnotation----注解元定义
package com.example.ownertest.dm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* 用于测试的标识注解
*
* @Author: linear.zw
* @Date: 2023/11/1 10:21
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ForTestAnnotation {
}
- ForTestAnnotationProcessor----注解解析器
package com.example.ownertest.dm.annotation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
/**
* 注解解析器
* @Author: linear.zw
* @Date: 2023/11/1 10:25
*/
@Component
public class ForTestAnnotationProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 获取目标类是否有ForTestAnnotation注解
ForTestAnnotation annotation = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean),
ForTestAnnotation.class);
if (annotation == null) {
return bean;
}
// 处理想要的扩展
System.out.println(beanName + " has ForTestAnnotation");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
- ForTestBean----测试bean
package com.example.ownertest.dm.annotation;
/**
* @Author: linear.zw
* @Date: 2023/11/1 10:26
*/
@ForTestAnnotation
public class ForTestBean {
public ForTestBean() {
System.out.println(ForTestBean.class.getSimpleName() + " init");
}
}
- ForTestClient---测试入口
package com.example.ownertest.dm.annotation;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @Author: linear.zw
* @Date: 2023/11/1 10:26
*/
public class ForTestClient {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
"com.example.ownertest.dm.annotation");
System.out.println(ForTestClient.class.getSimpleName());
}
}
常见框架中的应用
- 例如集团内部的spring-boot-alibaba-diamond-autoconfigure
基于事件分发的套路
关键点
- 事件源--事件触发者
- 事件--标识产生的来源
- 事件监听器--事件的关注者,即处理者
- 事件分发器--用于将事件源的事件转发给事件监听器
实现代码
- EventSource/EventSourceForTest/EventSourceForTest2
package com.example.ownertest.dm.event;
/**
* 发出事件
* @Author: linear.zw
* @Date: 2023/11/1 14:12
*/
public interface EventSource {
/**
* 发出事件
*
* @return
*/
Event fireEvent();
}
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:14
*/
public class EventSourceForTest implements EventSource {
@Override
public Event fireEvent() {
Event event = new EventForTest();
System.out.println(getClass().getSimpleName() + " \t fireEvent " + event.getName());
return event;
}
}
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:15
*/
public class EventSourceForTest2 implements EventSource {
@Override
public Event fireEvent() {
Event event = new EventForTest2();
System.out.println(getClass().getSimpleName() + " \t fireEvent " + event.getName());
return event;
}
}
- Event/EventForTest/EventForTest2
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:15
*/
public interface Event {
/**
* 事件名称
*
* @return
*/
String getName();
}
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:17
*/
public class EventForTest implements Event {
@Override
public String getName() {
return getClass().getSimpleName();
}
}
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:17
*/
public class EventForTest2 implements Event {
@Override
public String getName() {
return getClass().getSimpleName();
}
}
- EventListener/EventListenerForTest
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:17
*/
public interface EventListener {
/**
* 是否支持此事件
*
* @param event
* @return
*/
boolean supportEvent(Event event);
/**
* 处理事件
*
* @return
*/
boolean handlerEvent(Event event);
}
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:18
*/
public class EventListenerForTest implements EventListener {
@Override
public boolean supportEvent(Event event) {
return event.getName().contains("Test");
}
@Override
public boolean handlerEvent(Event event) {
System.out.println(this.getClass().getSimpleName() + "\t handler " + event.getName());
return true;
}
}
- EventDispatcher/EventListenerManager
package com.example.ownertest.dm.event;
import org.apache.commons.collections4.CollectionUtils;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:18
*/
public class EventDispatcher {
/**
* 单例模式
*/
private static EventDispatcher eventDispatcher = new EventDispatcher();
private EventDispatcher() {
}
/**
* 分发事件
*
* @param event
* @return
*/
public static boolean dispatchEvent(Event event) {
if (CollectionUtils.isNotEmpty(EventListenerManager.getEventListenerList())) {
for (EventListener eventListener :
EventListenerManager.getEventListenerList()) {
if (eventListener.supportEvent(event)) {
eventListener.handlerEvent(event);
}
}
}
return true;
}
}
package com.example.ownertest.dm.event;
import java.util.List;
import com.google.common.collect.Lists;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:18
*/
public class EventListenerManager {
private static List<EventListener> eventListenerList = Lists.newArrayList();
/**
* 添加事件监听器
*
* @param eventListener
* @return
*/
public static boolean addEventListener(EventListener eventListener) {
if (!eventListenerList.contains(eventListener)) {
return eventListenerList.add(eventListener);
}
return true;
}
/**
* 移除事件监听器
*
* @param eventListener
* @return
*/
public static boolean removeEventListener(EventListener eventListener) {
if (eventListenerList.contains(eventListener)) {
return eventListenerList.remove(eventListener);
}
return true;
}
public static List<EventListener> getEventListenerList() {
return eventListenerList;
}
}
- EventClient
package com.example.ownertest.dm.event;
/**
* @Author: linear.zw
* @Date: 2023/11/1 14:19
*/
public class EventClient {
public static void main(String[] args) {
// 创建事件源
EventSource eventSourceForTest = new EventSourceForTest();
EventSource eventSourceForTest2 = new EventSourceForTest2();
// 创建事件监听器
EventListener eventListener = new EventListenerForTest();
EventListenerManager.addEventListener(eventListener);
// 发布事件
EventDispatcher.dispatchEvent(eventSourceForTest.fireEvent());
EventDispatcher.dispatchEvent(eventSourceForTest2.fireEvent());
}
}
基于SPI机制的套路
关键点
- 服务调用方
- 服务实现方----以接口名称为文件名称,放在META-INF/services,值为该接口的实现
- 标准服务接口
适用场景
实现代码
- SpiServiceLoaderHelper
package com.example.ownertest.dm.spi;
import java.util.Iterator;
import java.util.Objects;
import java.util.ServiceLoader;
/**
* @Author: linear.zw
* @Date: 2023/11/1 15:32
*/
public class SpiServiceLoaderHelper {
public static ProductPackageRemoteServiceInterface getProductPackageRemoteServiceInterface() {
// 先从缓存中加载
Object serviceCache = DependServiceRegistryHelper.getDependObject(ProductPackageRemoteServiceInterface.class);
if (serviceCache != null) {
return (ProductPackageRemoteServiceInterface) serviceCache;
}
// spi 方式加载
ProductPackageRemoteServiceInterface serviceInterface = loadSpiImpl(ProductPackageRemoteServiceInterface.class);
// 防止注入的bean为空 提前进行判断 以免业务执行出现问题
boolean isExist = true;
if (Objects.isNull(serviceInterface)) {
isExist = false;
} else if (Objects.isNull(serviceInterface.getProductPackageRemoteService())) {
isExist = false;
}
if (!isExist) {
throw new RuntimeException("getProductPackageRemoteService load impl failed,please check spi service");
}
// 添加进统一的依赖管理
DependServiceRegistryHelper.registry(ProductPackageRemoteServiceInterface.class, serviceInterface);
return serviceInterface;
}
/**
* 以spi的方式加载实现类
*
* @param cls
* @param <P>
* @return
*/
private static <P> P loadSpiImpl(Class<P> cls) {
ServiceLoader<P> spiLoader = ServiceLoader.load(cls);
Iterator<P> iaIterator = spiLoader.iterator();
if (iaIterator.hasNext()) {
return iaIterator.next();
}
return null;
}
}
- DependServiceRegistryHelper
package com.example.ownertest.dm.spi;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* @Author: linear.zw
* @Date: 2023/11/1 15:35
*/
public class DependServiceRegistryHelper {
/**
* 存储策略依赖的服务,统一管理
*/
private static Map<String, Object> dependManagerMap = Maps.newHashMap();
public static boolean registryMap(Map<Class, Object> dependManagerMap) {
for (Map.Entry<Class, Object> dependEntry :
dependManagerMap.entrySet()) {
registry(dependEntry.getKey(), dependEntry.getValue());
}
return true;
}
public static boolean registry(Class cls, Object dependObject) {
dependManagerMap.put(cls.getCanonicalName(), dependObject);
return true;
}
public static Object getDependObject(Class cls) {
return dependManagerMap.get(cls.getCanonicalName());
}
}
- SpiServiceLoaderClientTest
package com.example.ownertest.dm.spi;
/**
* @Author: linear.zw
* @Date: 2023/11/1 15:37
*/
public class SpiServiceLoaderClientTest {
public static void main(String[] args) {
ProductPackageRemoteServiceInterface productPackageRemoteServiceInterface
= SpiServiceLoaderHelper.getProductPackageRemoteServiceInterface();
}
}
常见框架中的应用
- 目前大多数中台的策略包,都是基于spi方式的,动态加载业务的实现,进而达到扩展的目的;
- 例如google开源的auto-service,通过注解的方式,自动生成spi的实现目录;
最后
程序员大多数都是实干派,所以,你的套路有哪些,评论区有你的位置,Show me the code。
作者|高止
原文链接
本文为阿里云原创内容,未经允许不得转载。
推荐阅读
-
一种结构设计模式,允许在对象中动态添加新行为。它通过创建一个封装器来实现这一目的,即把对象放入一个装饰器类中,然后把这个装饰器类放入另一个装饰器类中,以此类推,形成一个封装器链。这样,我们就可以在不改变原始对象的情况下动态添加新行为或修改原始行为。 在 Java 中,实现装饰器设计模式的步骤如下: 定义一个接口或抽象类作为被装饰对象的基类。 公共接口 Component { void operation; } } 在本例中,我们定义了一个名为 Component 的接口,该接口包含一个名为 operation 的抽象方法,该方法定义了被装饰对象的基本行为。 定义一个实现基类方法的具体装饰对象。 公共类 ConcreteComponent 实现 Component { public class ConcreteComponent implements Component { @Override public void operation { System.out.println("ConcreteComponent is doing something...") ; } } 定义一个抽象装饰器类,该类继承于基类,并将装饰对象作为一个属性。 公共抽象类装饰器实现组件 { protected Component 组件 public Decorator(Component component) { this.component = component; } } @Override public void operation { component.operation; } } } 在这个示例中,我们定义了一个名为 Decorator 的抽象类,它继承了 Component 接口,并将被装饰对象作为一个属性。在操作方法中,我们调用了被装饰对象上的同名方法。 定义一个具体的装饰器类,继承自抽象装饰器类并实现增强逻辑。 公共类 ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component 组件) { super(component); } } public void operation { super.operation System.out.println("ConcreteDecoratorA 正在添加新行为......") ; } } 在本例中,我们定义了一个名为 ConcreteDecoratorA 的具体装饰器类,它继承自装饰器抽象类,并实现了操作方法的增强逻辑。在操作方法中,我们首先调用被装饰对象上的同名方法,然后添加新行为。 使用装饰器增强被装饰对象。 公共类 Main { public static void main(String args) { Component 组件 = new ConcreteComponent; component = new ConcreteDecoratorA(component); 组件操作 } } 在这个示例中,我们首先创建了一个被装饰对象 ConcreteComponent,然后通过 ConcreteDecoratorA 类创建了一个装饰器,并将被装饰对象作为参数传递。最后,调用装饰器的操作方法,实现对被装饰对象的增强。 使用场景 在 Java 中,装饰器模式被广泛使用,尤其是在 I/O 中。Java 中的 I/O 库使用装饰器模式实现了不同数据流之间的转换和增强。 让我们打开文件 a.txt,从中读取数据。InputStream 是一个抽象类,FileInputStream 是专门用于读取文件流的子类。BufferedInputStream 是一个支持缓存的数据读取类,可以提高数据读取的效率,具体代码如下: @Test public void testIO throws Exception { InputStream inputStream = new FileInputStream("C:/bbb/a.txt"); // 实现包装 inputStream = new BufferedInputStream(inputStream); byte bytes = new byte[1024]; int len; while((len = inputStream.read(bytes)) != -1){ System.out.println(new String(bytes, 0, len)); } } } } 其中 BufferedInputStream 对读取数据进行了增强。 这样看来,装饰器设计模式和代理模式似乎有点相似,接下来让我们讨论一下它们之间的区别。 第三,与代理模式的区别: 代理模式的目的是控制对对象的访问,它在对象外部提供一个代理对象来控制对原对象的访问。代理对象和原始对象通常实现相同的接口或继承相同的类,以确保两者可以相互替换。 装饰器模式的目的是动态增强对象的功能,而这是通过对象内部的包装器来实现的。在装饰器模式中,装饰器类和被装饰对象通常实现相同的接口或继承自相同的类,以确保两者可以相互替代。装饰器模式也被称为封装器模式。 在代理模式中,代理类附加了与原类无关的功能。
-
在 Java 中维护可扩展性的几种例程和实现方法
-
反传销网8月30日发布:视频区块链里的骗子,币里的韭菜,杜子建骂人了!金融大V周召说区块链!——“一小帮骗子玩一大帮小白,被割韭菜,小白还轮流被割,割的就是你!” 什么区块链,统统是骗子 作者:周召(知乎金融领域大V,毕业于上海财经大学,目前任职上海某股权投资基金合伙人) 有人问我,区块链现在这么火,到底是不是骗局? 我的回答是: 是骗局。而且我并不是说数字货币是骗局,而是说所有搞区块链的都是骗局。 -01- 区块链是一种鸡肋技术 人类社会任何技术的发明应用,本质都是为了提高社会的生产效率。而所谓区块链技术本质不过是几种早已成熟的技术的大杂烩,冗余且十分低效,除了提高了洗钱和诈骗的效率以外,对人类社会的进步毫无贡献。 真正意义上的区块链得包含三个要素:分布式系统(包括记账和存储),无法篡改的数据结构,以及共识算法,三者互为基础和因果,就像三体世界一样。看上去挺让人不明觉厉的,而经过几年的瞎折腾,稍微懂点区块链的碰了几次壁后都已经渐渐明白区块链其实并没有什么卵用,区块链技术已经名存实亡,沦为了营销工具和传销组织的画皮。 因为符合上述定义的、以比特币为代表的原教旨区块链技术,是反效率的,从经济学角度来说,不但不是一种帕累托改进,甚至还可以说是一种帕累托倒退。 原教旨区块链技术的效率十分低下,因为要遍历所有节点,只能做非常轻量级的数据应用,一旦涉及到大量的数据传输与更新,区块链就瞎了。 一方面整条链交易速度会极慢,另一方面数据库容量极速膨胀,考虑到人手一份的存储机制,区块链其实是对存储资源和能源的一种极大的浪费。 这里还没有加上为了取得所谓的共识和挖矿消耗的巨大的能源,如果说区块链技术是屎,那么这波区块链投机浪潮可谓人类历史上最大规模的搅屎运动。 区块链也验证不了任何东西。 所谓的智能合约,即不智能,也非合约。我看有人还说,如果有了智能合约,就可以跟老板签一份放区块链上,如果明年销售业绩提升30%,就加薪10%,由于区块链不能篡改,不能抵赖,所以老板必须得执行,说得有板有眼,不懂行的愣一看,好像还真是那么回事。 但仔细一想,问题就来了。首先,在区块链上如何证明你真的达到了30%业绩提升?即便真的达到老板耍赖如何执行? 也就是说,如果区块链真这么厉害,要法院和仲裁干什么。 人类社会真正的符合成本效益原则的是代理制度。之前有人说要用区块链改造注册会计师行业,我不知道他准备怎么设计,我猜想他思路大概是这样的,首先肯定搞去中心化,让所有会计师到链上来,然后一个新人要成为注册会计师就要所有会计师同意并记录在链上。 那我就请问了,我每天上班累死累活,为什么还要花时间去验证一个跟我无关的的人的专业能力?最优做法当然是组织一个委员会,让专门的人来负责,这不就是现在注册会师协会干的事儿吗?区块链的逻辑相当于什么事情都要拿出来公投,这个绝对是扯淡的。 当然这么说都有点抬举区块链了,区块链技术本身根本没有判断是非能力,如果这么高级的人工智能,靠一个无脑分布式记账就能实现的话,我们早就进入共产主义社会了。 虽然EOS等数字货币采用了超级节点,通过再中心化的方式提高效率,有点行业协会的意思,是对区块链原教旨主义的一种修正,但是依然无法突破区块链技术最本质的局限性。有人说,私有链和联盟链是区块链技术的未来,也是扯淡,因为区块链技术没有未来。如果有,说明他是包装成区块链的伪区块链技术。 区块链所涉及的所有底层技术,不管是分布式数据库技术,加密技术,还是点对点传输技术等,基本都是早已存在没什么秘密可言的技术。 比特币系统最重要的特性是封闭性和自洽性,他验证不了任何系统自身以外产生的信息的真实性。 所谓系统自身产生的信息,就是数据库数据的变动信息,有价值的基本上有且只有交易信息。所以说比特币最初不过是中本聪一种炫技的产物,来证明自己对几种技术的掌握,你看我多牛逼,设计出了一个像三体一样的系统。因此,数字货币很有可能是区块链从始至终唯一的杀手应用。 比特币和区块链概念从诞生到今天已经快10年了,很多人说区块链技术在爆发的前夜,但这个前夜好像是不是有点过长了啊朋友,跟三体里的长夜有一拼啊。都说区块链技术像是90年代初的互联网,可是90年代初的互联网在十年发展后,已经出现了一大批伟大的公司,阿里巴巴在99年都成立了,区块链怎么除了币还是币呢? 正规的数字货币未来发展的形式无外乎几种,要么就是论坛币形式,或者类似股票的权益凭证等。问题是论坛币和股票之前,本来也都电子化了,区块链来了到底改变了什么呢? 所有想把TOKEN和应用场景结合起来的人最后都很痛苦,最后他们会发现区块链技术就是脱裤子放屁,自己辛苦搞半天,干嘛不自己作为中心关心门来收钱?最后这些人都产生了价值的虚无感,最终精神崩溃,只能发币疯狂收割韭菜,一边嘴里还说着我是个好人之类的奇怪的话。 因此,之前币圈链圈还泾渭分明,互相瞧不起,但这两年链圈逐渐坐不住了,想着是不是趁着泡沫没彻底破灭之前赶快收割一波,不然可能什么都捞不着了。 前段时间和一个名校毕业的链圈朋友瞎聊天,他说他们“致力于用区块链技术解决数字版权保护问题”,我就问他一个问题,你们如何保证你链的版权所有权声明是真实的,万一盗版者抢先一步把数据放在链上怎么办。他说他们的解决方案是连入国家数字版权保护中心的数据库进行验证…… 所以说区块链技术就是个鸡肋,研究到最后都会落入效率与真实性的黑洞,很多人一头扎进链圈后才发现,真正意义上的区块链技术,其实什么都干不了。 -02- 不是蠢就是坏的区块链媒体 空气币和区块链的造富神话,让区块链自媒体也开始迎风乱扭。一群群根本不知道区块链为何物的妖魔鬼怪纷纷进驻区块链自媒体战场,开始大放厥词胡编乱造。 任何东西,但凡只要和区块,链,分,分布式,记账,加密,验证,可追溯等等这些个关键词沾到哪怕一点点,这些所谓的区块链媒体人就会像狗闻到了屎了一样疯狂地把区块链概念往上套。 这让我想起曾经一度也是热闹非凡的物联网,我曾经去看过江苏一家号称要改变世界的“物联网”企业,过去一看是生产路由器的,我黑人问号脸,对方解释说没有路由器万物怎么互联,我觉得他说得好有道理,竟无言以对。 好,下面让我们进入奇葩共赏析时间,来看看区城链媒体经常有哪些危言耸听的奇谈怪论 区块链(分布式记账)的典型应用是*?? 正如前面所说,真正意义上的区块链分布式记账,不光包括“记”这个动作,还包括分布式存储和共识机制等。而*诞生远远早于区块链这个词的出现,勉强算是“分布式编辑”吧,就被很多区块链媒体拿来强行充当区块链技术应用的典范。 其实事实恰恰相反,*恰恰是去中心化失败的典范,现在如果没有精英和专业人士的编辑和维护,*早就没法看了。 区块链会促进社会分工?? 罗振宇好像就说过类似的话,虽然罗振宇说过很多没有逻辑的话,但这句话绝对是最没逻辑思维的。很多区块链自媒体也常常用这句话来忽悠老百姓,说分工代表效率提高社会进步,而区块链“无疑”会促进分工,他们的理由仅仅是分工和分布式记账都共用一个“分”字,就强行把他们扯到一起。 实际情况恰恰相反,区块链是逆分工的,区块链精神是号召所有人积极地参与到他不擅长也不想掺合的事情里面去。 区块链不能像上帝一样许诺他的子民死后上天国,只能给他们许诺你们是六度人脉中的第一级,我可以赚后面五级人的钱,你处于金字塔的顶端。
-
详细解释在 Matlab 中实现和使用神经网络的几种方法,并提供代码供下载!
-
澎湃新闻对话腾讯丁珂:从 "治已病 "到 "治未病",企业需快速构建 "安全免疫力"--丁珂指出,对企业而言,安全不是成本而是生命线 丁珂指出,对企业而言,安全不是成本而是生命线,也是商业 "硬币 "的另一面。在数字智能化的新阶段,发展驱动安全建设已成为普遍共识,企业需要转变安全思维,从被动建设到主动防御,构建一套新的安全范式和框架,以更加积极、主动的安全观来提升数字安全免疫力,以 "治未病 "的理念取代 "治已病",前置安全,快速构建 "安全免疫力"。对 "已病",前置预判,及时应对处置安全风险,才能维护品牌价值,保障健康发展。 与此同时,安全建设还普遍存在 "不知道往哪投、怎么投 "的痛点。对此,腾讯安全提出,企业可以按照数字安全免疫模型的框架进行安全全局部署,重点在业务安全、数据安全、安全运维管理、边界安全、终端安全、应用开发安全等薄弱环节的关键领域注入 "免疫增强针"。 今年进入公众视野的AIGC还在产业化、产品化的过程中,但大量攻击者已经利用它生成攻击脚本、钓鱼邮件,甚至伪造身份进行诈骗。"人工智能本身是否安全,会不会让网络更不安全? 腾讯安全研究认为,AIGC的风险主要集中在 "无法解释 "和 "无法追踪 "的特点上,但这在技术上是能够找到应对方法的。丁珂谈到,AIGC作为生产力的巨大提升,确实会带来更复杂的攻防态势和更大的防御难度。但任何新技术都要经历这样的周期。而法律法规也会随着技术的演进而不断更新,使新技术的发展更加规范和健全。 丁珂认为,随着我国网络安全法律法规体系的不断完善,合规性将给企业推进网络安全带来很大的推动力,并很直观地展现在需求端。未来,伴随着数据要素市场的建立或企业对数据价值的挖掘,也将带动数据安全市场的快速增长。 对于腾讯安全的商业逻辑和运营,丁珂表示,不谋求建立竞争壁垒,而是期望与生态共同发展,腾讯安全希望通过能力开放,实现安全与业务相伴的生态模式。 谈到未来,丁磊表示,安全领域已经进入加速发展期,在蓝海中会持续关注很多新的业务领域,希望孵化出新的商业模式,腾讯安全团队也会持续关注并抓住机会做好产品。 以下为采访实录(在不改变原意的基础上略有删减): 冲浪新闻:当前,以人工智能、大数据等新技术为驱动的第四次工业革命正向纵深推进,给人类生产生活带来深刻变革。而互联网作为新技术的载体,面临的安全挑战不仅数量越来越多,形式也越来越复杂。从互联网安全从业者的角度,腾讯观察到近年来国内外网络安全形势发生了哪些变化?这些变化呈现出怎样的趋势?
-
在 Java 中实现异步到同步的几种方法
-
41 个下载免费 3D 模型的最佳网站-使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 17. Clara.io Clara.io 是一个创建 3D 内容的全球平台,也是一个培养新 3D 艺术家的社区。Clara.io 提供+100,000个免费的3D模型,包括OBJ,Blend,STL,FBX,DAE,Babylon.JS,Three.JS格式,用于 Clara.io,Unity 3D,Blender,Sketchup,Cinema 4D,3DS Max和Maya。 使用说明:免费,标准和专业帐户仅供个人使用,如果您需要将 clara.io 用于商业用途,请与销售团队联系。 18. 3DExport 3DExport是一个市场,您可以在其中购买和销售用于CG项目的3D模型,3D打印模型和纹理。它提供15 +不同的3D格式供下载,如3DS MAX(.max),Cinema4D(.c4d),Maya(.mb,.ma),Lightwave(.lwo),Softimage(.xsi),Wavefront OBJ(.obj),Autodesk FBX(.fbx)等。它还提供15种不同的语言! 使用说明:免费下载仅供个人和非商业用途。 19. 3D Warehouse 3D Warehouse是一个开放的库,允许用户共享和下载SketchUp 3D模型,用于建筑,设计,施工和娱乐!任何人都可以免费制作,修改和重新上传内容到3D仓库,您可以找到任何您能想到的东西,如家具,电子产品,室内产品等。 使用说明:3D Warehouse中的所有模型都是免费的,因此任何人都可以下载文件以用于SketchUp甚至其他软件,如AutoCAD,Revit和ArchiCAD。 20. CadNav.com CadNav是CGI平面设计师和CAD / CAM / CAE工程师的在线3D模型库,我们提供超过50000 +免费3D模型和CAD模型下载。在CadNav网站上,您可以下载高质量的多边形网格3D模型,3D CAD实体对象,纹理,Vray材料,3D作品,CAD图纸等。 使用说明:免费下载仅供个人和非商业用途。 21. All3dfree.net 就像网站名称一样,它提供免费的3D模型,还包括Vray材料,CAD块,2d和3d纹理集合,无需注册即可免费下载。它是不断更新的,因此您可以查找或请求3DS,MAX,C4D,skp,OBJ,FBX,MTL等格式的模型。 使用说明:所有资源均不允许用于商业用途,否则您将承担责任。 22. Hum3D 自2005年以来,Hum3D帮助来自3多个国家的80D艺术家节省3D建模时间,并制作逼真的3D模型,用于电影,视频游戏,AR应用程序和可视化。所有模型均由首席3D艺术家进行验证,他们检查其是否符合专业要求和最新的3D建模标准。 使用说明:免费下载仅供个人和非商业用途。 23. Artist-3D.com 艺术家-3D 库存的免费 3D 模型下载按通用类别排序。它为人体解剖学、汽车、家具、火箭、卫星等模型提供 AutoDesk 3DS Max 格式。您还可以在浏览他们的网站时找到教程和类似类型的建模。 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 24. Free the models 就像本网站的标题一样,它为3d应用程序和3d游戏引擎提供免费的内容模型。您可以为您的任何项目找到许多有趣且有用的模型!它提供3ds,wavefront,bryce,poser,lightwave,md2和unity3d格式的模型。还有一个很棒的纹理集合,可以在您最喜欢的建模和渲染程序中使用。 使用说明:您从这里下载的所有内容都可以免费使用,除非它不能包含在另一个免费的网络或CD收藏中,也不能单独出售。否则,您可以在商业游戏,3D应用程序或渲染作品中使用它。您不必提供信用,但如果您这样做,那就太好了。 25. Resources.blogscopia 本网站由一家名为Scopia的公司创建。他们制作3D图像和视频,您可以找到许多为CGI工作的信息架构设计的模型,所有这些都可以在现实生活中使用。您可以免费下载它们,但是,如果您想一次下载它们,您可以支付 3 到 9 欧元。 使用说明:您可以免费下载模型部分的所有文件。每个压缩文件都包含您也可以在此处找到的许可证。基本上,您可以对文件执行任何操作。唯一的限制是不归属于Scopia的重新分发。 26.ambientCG 1000+公共领域PBR材料适合所有人!环境CG是使用许多不同的方法和资产类型创建的,例如照片纹理(PBR),贴花(PBR),图集(PBR),照片纹理(普通),物质存档(SBSAR),雕刻画笔,3D模型和地形。您可以在所有项目中*使用它们! 使用说明:在 ambientCG 上提供下载的所有 PBR 材料、画笔、照片和 3D 模型均根据知识共享 CC0 1.0 通用许可提供。您可以复制、修改、分发和执行作品,即使是出于商业目的,也无需征得许可。信用将不胜感激。 不要满足于平庸的大理石纹理 - 立即使用我们的免费PBR大理石纹理升级您的3D设计。 27.Pixar One Twenty Eight 这是一个提供官方动画行业经典纹理的网站:皮克斯,创建于 1993 年,该纹理库包括 128 个重复纹理,现在免费提供。 它包含您来到的纹理,包括砖块和动物毛皮。肯定会有一些你可以使用的东西。 使用说明:皮克斯动画工作室的《Pixar One Twenty Eight》根据知识共享署名4.0国际许可协议进行许可。即使出于商业目的,您也可以重新混合、调整和构建您的作品,只要您以相同的条款对新创作进行信用和许可。 访问数以千计的免费纹理并提升您的设计游戏 - 立即开始下载! 28. 3DXO 即使有近 620 个免费贴纸可供下载,3DXO 也不是最大的资源,但它的内容非常有用,不需要注册。无论是简单的墙壁或地板,还是一些奇怪的小东西,您都需要的纹理都可以在此网站上看到。 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 29. 3DModelsCC0 3DModelsCC0 与其他产品的不同之处在于它包含超过 250+ 个高质量 3D 模型,并且本网站上的所有内容都是免费的,完全是公共领域!使用我们的模型时无需信用或归属! 使用说明:为每个人提供完全免费的公共领域内容。 30.Sketch up texture club Sketchup Texture Club是一个非营利性的教育和信息门户网站,由3D社区的图像促进协会管理,特别强调面向学生和建筑和室内设计专业人士的可视化和渲染技术,以及所有正在学习3D可视化的人。 使用说明:您无需支付版税或使用费。纹理可以免费下载和使用。不允许将纹理作为竞争产品出售或重新分发,即使图像被修改也是如此。 31. FlippedNormals FlippedNormal 是一个提供计算机图形和 3D 资产的市场,您可以找到许多用于雕刻、建模、纹理、概念艺术、3D 模型、游戏资产或课程的高级资产! 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 32. NASA 3D NASA 3D网站是一个在线门户,提供与太空和各种NASA任务相关的大量三维模型和模拟。该网站是用户友好的,并提供有关每个型号的详细信息。该网站允许用户探索和下载几种不同格式的模型,包括 OBJ、STL 和 FBX,只需单击下载按钮即可。 使用说明: 要下载模型,只需单击模型页面上的下载按钮并选择所需的格式。 33. 3DAGOGO (Astroprint) 3DAGOGO 是一个提供广泛 3D 模型的网站,包括角色、车辆和建筑物。3DAGOGO 的独特功能之一是它专注于适合 3D 打印的模型,使其成为希望创建物理原型或模型的设计师的绝佳资源。要使用 3DAGOGO,设计师只需在网站上搜索他们正在寻找的模型类型,然后下载 STL 格式的文件。 使用说明: 要使用 3DAGOGO,只需搜索所需的 3D 模型类型并下载 STL 格式的文件。根据需要自定义模型,并确保在将其用于商业目的之前检查使用权限。 34. FreeCAD FreeCAD是一款了不起的3D建模软件,可让您在计算机上创建令人难以置信的3D设计。该软件可免费下载和使用,它提供了广泛的工具和功能,可用于创建用于各种目的的3D模型。 该网站易于浏览,您可以找到开始使用FreeCAD的所有必要信息。此外,该网站还提供一系列教程和指南,可帮助您了解 3D 建模的来龙去脉。 使用说明: 要下载模型,请访问网站并从库中选择所需的模型。该网站还提供了一系列使用该软件的教程和指南。 35. Pinshape Pinshape是一个提供一系列3D打印模型的网站。网站上提供的型号质量很高,因此您可以确保您的最终印刷产品看起来很棒。该网站提供了广泛的模型,包括从家居用品到小雕像和珠宝的所有物品。 但这还不是Pinshape所能提供的全部!该网站还允许用户上传和共享自己的3D模型。这意味着您不仅可以下载出色的模型,还可以通过分享自己的设计为社区做出贡献。此外,Pinshape 提供了一系列自定义选项,因此您可以调整和调整模型以满足您的特定需求。 使用说明: 要下载模型,请在网站上创建一个帐户,搜索所需的模型,然后单击下载按钮。该网站还为每种型号提供了一系列定制选项。 36.Yeggi Yeggi 提供了大量免费的 3D 模型,您可以下载各种格式的模型,例如 STL、OBJ 和 FBX。该网站易于使用,您可以按关键字、类别或特定网站搜索模型。 Yeggi 对于任何寻找 3D 模型的人来说都是一个很好的资源。它提供了大量的模型集合,从日常物品到复杂的机械,以及介于两者之间的一切。该网站的收藏量在不断增长,每天都有新的型号增加。 使用说明: 要下载模型,请在网站上搜索所需的模型,然后单击下载按钮。该网站还提供指向托管模型的原始网站的链接。 37. Open3DModel 来自开放3D模型的图像 Open3DModel具有各种类别的模型,包括建筑,车辆和角色。无论您需要建筑物,汽车还是人的3D模型,都可以在此网站上找到。 该网站易于浏览,您可以按类别或关键字搜索模型。每个模型都附带预览图像和详细信息,例如文件格式、大小和多边形数量。此信息可以帮助您选择适合您需求的模型。 使用说明: 要下载模型,请访问网站,从库中选择所需的模型,然后单击下载按钮。 使用最好的 3D 资产管理工具简化您的 3D 制作流程。立即试用它们,将您的 3D 项目提升到一个新的水平! 38. 3DExport 对于那些为其 3D 设计项目寻找 3D 模型、纹理和其他资源的人来说,该平台是一个很好的资源。该网站有大量模型可供选择,包括 3D 打印对象、游戏资产等。用户可以按类别、文件格式或价格范围浏览,以找到适合其项目的完美资源。此外,3DExport 还提供一系列教程和其他 3D 资源,以帮助用户提高技能并创建更令人印象深刻的设计。 使用说明: 要使用 3DExport,只需创建一个帐户并浏览可用型号。您可以按类别、格式和价格进行搜索,以找到所需的型号。找到喜欢的模型后,只需下载它并开始在您的项目中使用它。 39.Blend Swap Blend Swap是一个社区驱动的市场,提供与Blender软件兼容的各种免费3D模型。该平台允许用户共享和下载模型、纹理和其他资产,以便在他们的项目中使用。 使用说明: 创建免费帐户后,您可以浏览社区上传的大量3D模型。当您找到要使用的一个时,只需下载它并将其导入您选择的 3D 软件即可。 40. 3DShook 3DShook 是一个高级 3D 模型市场,提供一系列用于建筑、游戏等各个行业的高质量模型。该平台提供基于订阅的模型,具有不同的定价计划,允许用户访问一系列模型。 使用说明: 注册免费帐户后,只需浏览3D模型库,选择您喜欢的模型,然后以您需要的格式下载它们。 41. Smithsonian X 3D 史密森尼 X 3D 对于正在寻找历史文物和文物的高质量 3D 模型的设计师来说,这是一个独特的资源。该平台提供了大量3D模型,这些模型是根据史密森尼博物馆和研究中心中的真实物体扫描创建的。 使用说明:
-
windows下进程间通信的(13种方法)-摘 要 本文讨论了进程间通信与应用程序间通信的含义及相应的实现技术,并对这些技术的原理、特性等进行了深入的分析和比较。 ---- 关键词 信号 管道 消息队列 共享存储段 信号灯 远程过程调用 Socket套接字 MQSeries 1 引言 ---- 进程间通信的主要目的是实现同一计算机系统内部的相互协作的进程之间的数据共享与信息交换,由于这些进程处于同一软件和硬件环境下,利用操作系统提供的的编程接口,用户可以方便地在程序中实现这种通信;应用程序间通信的主要目的是实现不同计算机系统中的相互协作的应用程序之间的数据共享与信息交换,由于应用程序分别运行在不同计算机系统中,它们之间要通过网络之间的协议才能实现数据共享与信息交换。进程间通信和应用程序间通信及相应的实现技术有许多相同之处,也各有自己的特色。即使是同一类型的通信也有多种的实现方法,以适应不同情况的需要。 ---- 为了充分认识和掌握这两种通信及相应的实现技术,本文将就以下几个方面对这两种通信进行深入的讨论:问题的由来、解决问题的策略和方法、每种方法的工作原理和实现、每种实现方法的特点和适用的范围等。 2 进程间的通信及其实现技术 ---- 用户提交给计算机的任务最终都是通过一个个的进程来完成的。在一组并发进程中的任何两个进程之间,如果都不存在公共变量,则称该组进程为不相交的。在不相交的进程组中,每个进程都独立于其它进程,它的运行环境与顺序程序一样,而且它的运行环境也不为别的进程所改变。运行的结果是确定的,不会发生与时间相关的错误。 ---- 但是,在实际中,并发进程的各个进程之间并不是完全互相独立的,它们之间往往存在着相互制约的关系。进程之间的相互制约关系表现为两种方式: ---- (1) 间接相互制约:共享CPU ---- (2) 直接相互制约:竞争和协作 ---- 竞争——进程对共享资源的竞争。为保证进程互斥地访问共享资源,各进程必须互斥地进入各自的临界段。 ---- 协作——进程之间交换数据。为完成一个共同任务而同时运行的一组进程称为同组进程,它们之间必须交换数据,以达到协作完成任务的目的,交换数据可以通知对方可以做某事或者委托对方做某事。 ---- 共享CPU问题由操作系统的进程调度来实现,进程间的竞争和协作由进程间的通信来完成。进程间的通信一般由操作系统提供编程接口,由程序员在程序中实现。UNIX在这个方面可以说最具特色,它提供了一整套进程间的数据共享与信息交换的处理方法——进程通信机制(IPC)。因此,我们就以UNIX为例来分析进程间通信的各种实现技术。 ---- 在UNIX中,文件(File)、信号(Signal)、无名管道(Unnamed Pipes)、有名管道(FIFOs)是传统IPC功能;新的IPC功能包括消息队列(Message queues)、共享存储段(Shared memory segment)和信号灯(Semapores)。 ---- (1) 信号 ---- 信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断控制。例如在发生浮点错、非法内存访问、执行无效指令、某些按键(如ctrl-c、del等)等都会产生一个信号,操作系统就会调用有关的系统调用或用户定义的处理过程来处理。 ---- 信号处理的系统调用是signal,调用形式是: ---- signal(signalno,action) ---- 其中,signalno是规定信号编号的值,action指明当特定的信号发生时所执行的动作。 ---- (2) 无名管道和有名管道 ---- 无名管道实际上是内存中的一个临时存储区,它由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出方式管理,并严格按顺序操作,例如不能对管道进行搜索,管道中的信息只能读一次。 ---- 无名管道只能用于两个相互协作的进程之间的通信,并且访问无名管道的进程必须有共同的祖先。 ---- 系统提供了许多标准管道库函数,如: pipe——打开一个可以读写的管道; close——关闭相应的管道; read——从管道中读取字符; write——向管道中写入字符; ---- 有名管道的操作和无名管道类似,不同的地方在于使用有名管道的进程不需要具有共同的祖先,其它进程,只要知道该管道的名字,就可以访问它。管道非常适合进程之间快速交换信息。 ---- (3) 消息队列(MQ) ---- 消息队列是内存中独立于生成它的进程的一段存储区,一旦创建消息队列,任何进程,只要具有正确的的访问权限,都可以访问消息队列,消息队列非常适合于在进程间交换短信息。 ---- 消息队列的每条消息由类型编号来分类,这样接收进程可以选择读取特定的消息类型——这一点与管道不同。消息队列在创建后将一直存在,直到使用msgctl系统调用或iqcrm -q命令删除它为止。 ---- 系统提供了许多有关创建、使用和管理消息队列的系统调用,如: ---- int msgget(key,flag)——创建一个具有flag权限的MQ及其相应的结构,并返回一个唯一的正整数msqid(MQ的标识符); ---- int msgsnd(msqid,msgp,msgsz,msgtyp,flag)——向队列中发送信息; ---- int msgrcv(msqid,cmd,buf)——从队列中接收信息; ---- int msgctl(msqid,cmd,buf)——对MQ的控制操作; ---- (4) 共享存储段(SM) ---- 共享存储段是主存的一部分,它由一个或多个独立的进程共享。各进程的数据段与共享存储段相关联,对每个进程来说,共享存储段有不同的虚拟地址。系统提供的有关SM的系统调用有: ---- int shmget(key,size,flag)——创建大小为size的SM段,其相应的数据结构名为key,并返回共享内存区的标识符shmid; ---- char shmat(shmid,address,flag)——将当前进程数据段的地址赋给shmget所返回的名为shmid的SM段; ---- int shmdr(address)——从进程地址空间删除SM段; ---- int shmctl (shmid,cmd,buf)——对SM的控制操作; ---- SM的大小只受主存限制,SM段的访问及进程间的信息交换可以通过同步读写来完成。同步通常由信号灯来实现。SM非常适合进程之间大量数据的共享。 ---- (5) 信号灯 ---- 在UNIX中,信号灯是一组进程共享的数据结构,当几个进程竞争同一资源时(文件、共享内存或消息队列等),它们的操作便由信号灯来同步,以防止互相干扰。 ---- 信号灯保证了某一时刻只有一个进程访问某一临界资源,所有请求该资源的其它进程都将被挂起,一旦该资源得到释放,系统才允许其它进程访问该资源。信号灯通常配对使用,以便实现资源的加锁和解锁。 ---- 进程间通信的实现技术的特点是:操作系统提供实现机制和编程接口,由用户在程序中实现,保证进程间可以进行快速的信息交换和大量数据的共享。但是,上述方式主要适合在同一台计算机系统内部的进程之间的通信。 3 应用程序间的通信及其实现技术 ---- 同进程之间的相互制约一样,不同的应用程序之间也存在竞争和协作的关系。UNIX操作系统也提供一些可用于应用程序之间实现数据共享与信息交换的编程接口,程序员可以通过自己编程来实现。如远程过程调用和基于TCP/IP协议的套接字(Socket)编程。但是,相对普通程序员来说,它们涉及的技术比较深,编程也比较复杂,实现起来困难较大。 ---- 于是,一种新的技术应运而生——通过将有关通信的细节完全掩盖在某个独立软件内部,即底层的通讯工作和相应的维护管理工作由该软件内部来实现,用户只需要将通信任务提交给该软件去完成,而不必理会它的具体工作过程——这就是所谓的中间件技术。 ---- 我们在这里分别讨论这三种常用的应用程序间通信的实现技术——远程过程调用、会话编程技术和MQSeries消息队列技术。其中远程过程调用和会话编程属于比较低级的方式,程序员参与的程度较深,而MQSeries消息队列则属于比较高级的方式,即中间件方式,程序员参与的程度较浅。 ---- 4.1 远程过程调用(RPC)
-
在Java中运用Ognl、SpEL、Groovy和Jexl3:动态表达式语句的前置、中置和后置实现方法探讨
-
玩转Java底层:JMX详解 - jconsole与自定义MBean监控工具的实际应用与区别" 在日常JVM调优中,我们熟知的jconsole工具通过JMX包装的bean以图形化形式展示管理数据,而像jstat和jmap这类内建监控工具则由JVM直接支持。本文将以jconsole为例,深入讲解其实质——基于JMX的MBean功能,包括可视化界面上的bean属性查看和操作调用。 MBeans在jconsole中的体现是那些可观察的组件属性和方法,如上图所示,通过名为"Verbose"的属性能看到其值为false,同时还能直接操作该bean的方法,例如"closeJerryMBean"。 尽管jconsole给我们提供了直观的可视化界面,但请注意,这里的MBean并非固定不变,开发者可根据JMX提供的接口将自己的自定义bean展示到jconsole。以下步骤展示了如何创建并注册一个名为"StudyJavaMBean"的自定义MBean: 1. 首先定义接口`StudyJavaMBean`,接口需遵循MBean规范,即后缀为"MBean"且包含getter方法代表属性,如`getApplicationName`,和无返回值的setter方法代表操作,如`closeJerryMBean`。 ```java public interface StudyJavaMBean { String getApplicationName(); void closeJerryMBean(); } ``` 2. 编写接口的实现类`StudyJavaMBeanImpl`,实现接口中的方法: ```java public class StudyJavaMBeanImpl implements StudyJavaMBean { @Override public String getApplicationName() { return "每天学Java"; } @Override public void closeJerryMBean() { System.out.println("关闭Jerry应用"); } } ``` 3. 在代码中注册自定义MBean,涉及的关键步骤包括: - 获取平台MBeanServer - 定义ObjectName,指定唯一的MBean标识符 - 注册MBean到服务器 - 启动RMI连接器服务,以便jconsole能够访问 ```java public void registerMBean() throws Exception { // ... 具体实现省略 ... } ``` 实际运行注册后的MBean,您将在jconsole中发现并查看自定义bean的属性和调用相关方法。然而,这种方式相较于传统的属性/日志查看和HTTP接口,实用性相对有限,可能存在潜在的安全风险。但不可否认的是,JMX及其MBean机制对于获取操作系统信息、内存状态等关键性能指标仍然具有重要价值。例如: 1. **获取操作系统信息**:通过JMX MBean,可以直接获取到诸如CPU使用率、操作系统版本等系统级信息,这对于资源管理和优化工作具有显著帮助。