Merge branch 'develop' of https://gitee.com/easy-es/easy-es into develop

This commit is contained in:
邢鹏成 2022-03-07 10:37:37 +08:00
commit 9d95764196
16 changed files with 476 additions and 6 deletions

View File

@ -90,14 +90,14 @@ Easy-Es是一款简化ElasticSearch搜索引擎操作的开源框架,简化`CRUD
**Maven:** **Maven:**
``` xml ``` xml
<dependency> <dependency>
<groupId>com.github.xpc1024</groupId> <groupId>io.github.xpc1024</groupId>
<artifactId>easy-es-boot-starter</artifactId> <artifactId>easy-es-boot-starter</artifactId>
<version>Latest Version</version> <version>Latest Version</version>
</dependency> </dependency>
``` ```
**Gradle:** **Gradle:**
```groovy ```groovy
compile group: 'com.github.xpc1024', name: 'easy-es-boot-starter', version: 'Latest Version' compile group: 'io.github.xpc1024', name: 'easy-es-boot-starter', version: 'Latest Version'
``` ```
# 其他开源项目 | Other Project # 其他开源项目 | Other Project

View File

@ -85,14 +85,14 @@ String indexName = "document";
- Maven: - Maven:
```xml ```xml
<dependency> <dependency>
<groupId>com.github.xpc1024</groupId> <groupId>io.github.xpc1024</groupId>
<artifactId>easy-es-boot-starter</artifactId> <artifactId>easy-es-boot-starter</artifactId>
<version>Latest Version</version> <version>Latest Version</version>
</dependency> </dependency>
``` ```
- Gradle - Gradle
```groovy ```groovy
compile group: 'com.github.xpc1024', name: 'easy-es-boot-starter', version: 'Latest Version' compile group: 'io.github.xpc1024', name: 'easy-es-boot-starter', version: 'Latest Version'
``` ```
- Add mapper file extends BaseEsMapper interface - Add mapper file extends BaseEsMapper interface

View File

@ -1,5 +1,7 @@
package com.xpc.easyes.autoconfig.config; package com.xpc.easyes.autoconfig.config;
import com.xpc.easyes.core.plugin.interceptor.Interceptor;
import com.xpc.easyes.core.plugin.interceptor.InterceptorChain;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ -51,4 +53,22 @@ public class EsConfigProperties {
* 连接请求超时时间 * 连接请求超时时间
*/ */
private Integer connectionRequestTimeout; private Integer connectionRequestTimeout;
/**
* 拦截器链
*/
protected InterceptorChain interceptorChain;
public void initInterceptorChain(){
if(interceptorChain == null){
interceptorChain = new InterceptorChain();
}
}
/**
* 添加拦截器
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
} }

View File

@ -1,12 +1,18 @@
package com.xpc.easyes.autoconfig.register; package com.xpc.easyes.autoconfig.register;
import com.xpc.easyes.autoconfig.config.EsConfigProperties;
import com.xpc.easyes.core.cache.BaseCache; import com.xpc.easyes.core.cache.BaseCache;
import com.xpc.easyes.core.plugin.interceptor.Interceptor;
import com.xpc.easyes.core.plugin.interceptor.InterceptorChain;
import com.xpc.easyes.core.plugin.interceptor.Intercepts;
import com.xpc.easyes.core.proxy.EsMapperProxy; import com.xpc.easyes.core.proxy.EsMapperProxy;
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Map;
/** /**
* 代理类 * 代理类
@ -19,6 +25,12 @@ public class MapperFactoryBean<T> implements FactoryBean<T> {
@Autowired @Autowired
private RestHighLevelClient client; private RestHighLevelClient client;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private EsConfigProperties esConfigProperties;
public MapperFactoryBean() { public MapperFactoryBean() {
} }
@ -30,7 +42,9 @@ public class MapperFactoryBean<T> implements FactoryBean<T> {
public T getObject() throws Exception { public T getObject() throws Exception {
EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface); EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface);
BaseCache.initCache(mapperInterface, client); BaseCache.initCache(mapperInterface, client);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy); T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
InterceptorChain interceptorChain = this.initInterceptorChain();
return interceptorChain.pluginAll(t);
} }
@Override @Override
@ -42,4 +56,24 @@ public class MapperFactoryBean<T> implements FactoryBean<T> {
public boolean isSingleton() { public boolean isSingleton() {
return true; return true;
} }
private InterceptorChain initInterceptorChain(){
InterceptorChain interceptorChain = esConfigProperties.getInterceptorChain();
if(interceptorChain == null){
synchronized (this){
esConfigProperties.initInterceptorChain();
Map<String, Object> beansWithAnnotation = this.applicationContext.getBeansWithAnnotation(Intercepts.class);
if(beansWithAnnotation != null){
beansWithAnnotation.forEach((key, val) ->{
if(val instanceof Interceptor){
Interceptor interceptor = (Interceptor) val;
esConfigProperties.addInterceptor(interceptor);
}
});
}
}
}
return esConfigProperties.getInterceptorChain();
}
} }

View File

@ -0,0 +1,25 @@
package com.xpc.easyes.core.plugin.interceptor;
/**
* <p>
* Interceptor
* </p>
*
* @author lilu
* @since 2022/3/4
*/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
/**
* 代理
*
* @param t 泛型
* @return 泛型
*/
default <T> T plugin(T t) {
return Plugin.wrap(t, this);
}
}

View File

@ -0,0 +1,53 @@
package com.xpc.easyes.core.plugin.interceptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* <p>
* 拦截器链
* </p>
*
* @author lilu
* @since 2022/3/4
*/
public class InterceptorChain {
/**
* 拦截器集合
*/
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 装载拦截器
*
* @param t 泛型
* @return 泛型
*/
public <T> T pluginAll(T t) {
for (Interceptor interceptor : interceptors) {
t = interceptor.plugin(t);
}
return t;
}
/**
* 添加拦截器
*
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
/**
* 获取所有拦截器
*
* @return 拦截器集合
*/
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}

View File

@ -0,0 +1,20 @@
package com.xpc.easyes.core.plugin.interceptor;
import java.lang.annotation.*;
/**
* <p>
* 拦截器
* </p>
*
* @author lilu
* @since 2022/3/4
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}

View File

@ -0,0 +1,42 @@
package com.xpc.easyes.core.plugin.interceptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* <p>
* 执行器
* </p>
*
* @author lilu
* @since 2022/3/4
*/
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}

View File

@ -0,0 +1,84 @@
package com.xpc.easyes.core.plugin.interceptor;
import com.xpc.easyes.core.exception.EasyEsException;
import com.xpc.easyes.core.utils.ExceptionUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <p>
* 插件代理
* </p>
*
* @author lilu
* @since 2022/3/4
*/
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
/**
* 包装代理
*
* @param t 泛型
* @param interceptor 拦截器
* @return 泛型
*/
public static <T> T wrap(T t, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
return (T) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
new Plugin(t, interceptor, signatureMap));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// 检查类是否被@Intercepts标记
if (interceptsAnnotation == null) {
throw new EasyEsException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
// 检查被@Signature标记的方法是否存在
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new EasyEsException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
}

View File

@ -0,0 +1,42 @@
package com.xpc.easyes.core.plugin.interceptor;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>
* 签名定义
* </p>
*
* @author lilu
* @since 2022/3/4
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
/**
* Returns the java type.
*
* @return the java type
*/
Class<?> type();
/**
* Returns the method name.
*
* @return the method name
*/
String method();
/**
* Returns java types for method argument.
*
* @return java types for method argument
*/
Class<?>[] args();
}

View File

@ -0,0 +1,33 @@
package com.xpc.easyes.core.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
/**
* <p>
* ExceptionUtil
* </p>
*
* @author lilu
* @since 2022/3/4
*/
public class ExceptionUtil {
private ExceptionUtil() {
// Prevent Instantiation
}
public static Throwable unwrapThrowable(Throwable wrapped) {
Throwable unwrapped = wrapped;
while (true) {
if (unwrapped instanceof InvocationTargetException) {
unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
} else if (unwrapped instanceof UndeclaredThrowableException) {
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
} else {
return unwrapped;
}
}
}
}

View File

@ -1,18 +1,23 @@
package com.xpc.easyes.sample; package com.xpc.easyes.sample;
import com.xpc.easyes.autoconfig.annotation.EsMapperScan; import com.xpc.easyes.autoconfig.annotation.EsMapperScan;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
/** /**
* 启动类 * 启动类
* <p> * <p>
* Copyright © 2021 xpc1024 All Rights Reserved * Copyright © 2021 xpc1024 All Rights Reserved
**/ **/
@SpringBootApplication @SpringBootApplication(scanBasePackages = {"com.xpc.easyes"})
@EsMapperScan("com.xpc.easyes.sample.mapper") @EsMapperScan("com.xpc.easyes.sample.mapper")
public class EasyEsApplication { public class EasyEsApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(EasyEsApplication.class, args); SpringApplication.run(EasyEsApplication.class, args);
} }
} }

View File

@ -17,6 +17,7 @@ import java.util.List;
**/ **/
@RestController @RestController
public class TestController { public class TestController {
@Resource @Resource
private DocumentMapper documentMapper; private DocumentMapper documentMapper;
@ -36,4 +37,19 @@ public class TestController {
return documentMapper.selectList(wrapper); return documentMapper.selectList(wrapper);
} }
/**
* 演示根据title删除文章同时会被 DeleteInterceptor 拦截执行逻辑删除
*
* @param title
* @return
*/
@GetMapping("/deleteDocumentByTitle")
public Integer deleteDocumentByTitle(@RequestParam String title) {
// 实际开发中会把这些逻辑写进service层 这里为了演示方便就不创建service层了
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, title);
return documentMapper.delete(wrapper);
}
} }

View File

@ -0,0 +1,21 @@
package com.xpc.easyes.sample.entity;
import lombok.Data;
/**
* <p>
* GeneralBean
* </p>
*
* @author lilu
* @since 2022/3/4
*/
@Data
public class GeneralBean {
/**
* 删除状态
*/
private Boolean existStatus;
}

View File

@ -0,0 +1,37 @@
package com.xpc.easyes.sample.interceptor;
import com.xpc.easyes.core.conditions.LambdaEsQueryWrapper;
import com.xpc.easyes.core.conditions.interfaces.BaseEsMapper;
import com.xpc.easyes.core.plugin.interceptor.Interceptor;
import com.xpc.easyes.core.plugin.interceptor.Intercepts;
import com.xpc.easyes.core.plugin.interceptor.Invocation;
import com.xpc.easyes.core.plugin.interceptor.Signature;
import com.xpc.easyes.sample.entity.GeneralBean;
import org.springframework.stereotype.Component;
/**
* <p>
* 统一逻辑删除拦截器 demo
* </p>
*
* @author lilu
* @since 2022/3/4
*/
@Intercepts(
{
// 只是个例子如果要实现这个功能还需要拦截更多的方法
@Signature(type = BaseEsMapper.class, method = "delete", args = {LambdaEsQueryWrapper.class}),
}
)
@Component
public class DeleteInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 统一逻辑删除拦截
System.out.println("啊啊啊,我拦截到了删除,禁止删除");
// 这里直接改为update
return 0;
}
}

View File

@ -0,0 +1,38 @@
package com.xpc.easyes.sample.interceptor;
import com.xpc.easyes.core.conditions.LambdaEsQueryWrapper;
import com.xpc.easyes.core.conditions.interfaces.BaseEsMapper;
import com.xpc.easyes.core.plugin.interceptor.Interceptor;
import com.xpc.easyes.core.plugin.interceptor.Intercepts;
import com.xpc.easyes.core.plugin.interceptor.Invocation;
import com.xpc.easyes.core.plugin.interceptor.Signature;
import com.xpc.easyes.sample.entity.GeneralBean;
import org.springframework.stereotype.Component;
/**
* <p>
* 统一查询拦截器 demo
* </p>
*
* @author lilu
* @since 2022/3/4
*/
@Intercepts(
{
@Signature(type = BaseEsMapper.class, method = "selectList", args = {LambdaEsQueryWrapper.class}),
}
)
@Component
public class QueryInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("啊啊啊,我拦截到了查询,统一增加查询条件");
// 统一逻辑删除拦截
Object[] args = invocation.getArgs();
LambdaEsQueryWrapper<GeneralBean> arg = (LambdaEsQueryWrapper) args[0];
arg.eq(GeneralBean::getExistStatus, true);
return invocation.proceed();
}
}