package com.orchard.userimpl;
import com.orchard.common.annotation.EnableRedissionConfig;
import com.orchard.common.config.ServerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableRedissionConfig
public class PomeloUserImplApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
ServerConfig.initServerConfig();
return builder.sources(PomeloUserImplApplication.class);
}
public static void main(String[] args) {
ServerConfig.initServerConfig();
SpringApplication.run(PomeloUserImplApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.orchard.common.annotation;
import com.orchard.common.support.lock.RedissionLocak;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({RedissionLocak.class})
@Documented
@Inherited
public @interface EnableRedissionConfig {
}
singleServerConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
subscriptionsPerConnection: 5
clientName: null
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 10
connectionPoolSize: 64
database: 1
dnsMonitoring: false
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
useLinuxNativeEpoll: false
package com.orchard.common.support.lock;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
@Configuration
public class RedissionLocak {
@Value("classpath:/redisson-conf.yml")
Resource configFile;
@Bean(destroyMethod = "shutdown")
RedissonClient redisson()
throws IOException {
Config config = Config.fromYAML(configFile.getInputStream());
return Redisson.create(config);
}
@Bean
DistributedLockTemplate distributedLockTemplate(RedissonClient redissonClient) {
return new SingleDistributedLockTemplate(redissonClient);
}
}
package com.orchard.common.support.lock;
import java.util.concurrent.TimeUnit;
public interface DistributedLockTemplate {
long DEFAULT_WAIT_TIME = 30;
long DEFAULT_TIMEOUT = 5;
TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
<T> T lock(DistributedLockCallback<T> callback, boolean fairLock);
<T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock);
<T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock);
<T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock);
}
package com.orchard.common.support.lock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class SingleDistributedLockTemplate implements DistributedLockTemplate {
private RedissonClient redisson;
public SingleDistributedLockTemplate(RedissonClient redisson) {
this.redisson = redisson;
}
@Override
public <T> T lock(DistributedLockCallback<T> callback, boolean fairLock) {
return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
}
@Override
public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
RLock lock = getLock(callback.getLockName(), fairLock);
if(!Thread.currentThread().isInterrupted()) {
try {
lock.lock(leaseTime, timeUnit);
return callback.process();
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
}
return null;
}
@Override
public <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock) {
return tryLock(callback, DEFAULT_WAIT_TIME, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
}
@Override
public <T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
RLock lock = getLock(callback.getLockName(), fairLock);
try {
if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
return callback.process();
}
} catch (InterruptedException e) {
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
return null;
}
private RLock getLock(String lockName, boolean fairLock) {
RLock lock;
if (fairLock) {
lock = redisson.getFairLock(lockName);
} else {
lock = redisson.getLock(lockName);
}
return lock;
}
}
package com.orchard.common.support.lock;
public interface DistributedLockCallback<T> {
public T process();
public String getLockName();
}
package com.orchard.common.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
String lockName() default "";
String lockNamePre() default "";
String lockNamePost() default "lock";
String separator() default ".";
String param() default "";
int argNum() default 0;
int[] argNums() default {};
boolean fairLock() default false;
boolean tryLock() default false;
long waitTime() default 30L;
long leaseTime() default 5L;
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
package com.orchard.common.config.aop;
import com.orchard.common.annotation.DistributedLock;
import com.orchard.common.support.lock.DistributedLockCallback;
import com.orchard.common.support.lock.DistributedLockTemplate;
import com.orchard.common.support.redis.RedisUtil;
import com.orchard.common.util.ValidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Slf4j
public class LogAspectServiceApi {
@Autowired
private DistributedLockTemplate lockTemplate;
@Autowired
private RedisUtil redisUtil;
@Pointcut("execution(* com.orchard..*.*(..))")
private void controllerAspect() {
}
@Around(value = "controllerAspect()")
public Object around(ProceedingJoinPoint pjd) throws Throwable{
methodBefore(pjd);
Object result = annotatedBus(pjd);
Method method = ((MethodSignature) pjd.getSignature()).getMethod();
try {
if(ValidUtil.isEmpty(result)) {
result = pjd.proceed();
}
} catch (Exception e) {
log.error("###LogAspectServiceApi.class afterThrowing() ### ERROR-method:{}, exception:{}", method, e);
} finally {
methodAfter(result, pjd);
return result;
}
}
private Object annotatedBus(ProceedingJoinPoint pjd) {
Signature signature = pjd.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
log.info("@Around 获取方法注解: {}", annotation.toString());
if(annotation instanceof DistributedLock) {
Object[] arguments = pjd.getArgs();
final String lockName = getLockName(method, arguments);
return lock(pjd, method, lockName);
}
}
return null;
}
public String getLockName(Method method, Object[] args) {
Objects.requireNonNull(method);
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
String lockName = annotation.lockName(),
param = annotation.param();
if (isEmpty(lockName)) {
if (args.length > 0) {
if (isNotEmpty(param)) {
Object arg;
if (annotation.argNum() > 0) {
arg = args[annotation.argNum() - 1];
} else {
arg = args[0];
}
lockName = String.valueOf(getParam(arg, param));
} else if (annotation.argNum() > 0) {
lockName = args[annotation.argNum() - 1].toString();
} else if (annotation.argNums().length > 0) {
int length = annotation.argNums().length;
for(int i = 0; i < length; i++) {
lockName += args[annotation.argNums()[i]].toString();
}
}
}
}
if (isNotEmpty(lockName)) {
String preLockName = annotation.lockNamePre(),
postLockName = annotation.lockNamePost(),
separator = annotation.separator();
StringBuilder lName = new StringBuilder();
if (isNotEmpty(preLockName)) {
lName.append(preLockName).append(separator);
}
lName.append(lockName);
if (isNotEmpty(postLockName)) {
lName.append(separator).append(postLockName);
}
lockName = lName.toString();
return lockName;
}
throw new IllegalArgumentException("Can't get or generate lockName accurately!");
}
public Object getParam(Object arg, String param) {
if (isNotEmpty(param) && arg != null) {
try {
Object result = PropertyUtils.getProperty(arg, param);
return result;
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(arg + "没有属性" + param + "或未实现get方法。", e);
} catch (Exception e) {
throw new RuntimeException("", e);
}
}
return null;
}
public Object lock(ProceedingJoinPoint pjp, Method method, final String lockName) {
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
boolean fairLock = annotation.fairLock();
boolean tryLock = annotation.tryLock();
if (tryLock) {
return tryLock(pjp, annotation, lockName, fairLock);
} else {
return lock(pjp,lockName, fairLock);
}
}
public Object lock(ProceedingJoinPoint pjp, final String lockName, boolean fairLock) {
return lockTemplate.lock(new DistributedLockCallback<Object>() {
@Override
public Object process() {
return proceed(pjp);
}
@Override
public String getLockName() {
return lockName;
}
}, fairLock);
}
public Object tryLock(ProceedingJoinPoint pjp, DistributedLock annotation, final String lockName, boolean fairLock) {
long waitTime = annotation.waitTime(),
leaseTime = annotation.leaseTime();
TimeUnit timeUnit = annotation.timeUnit();
return lockTemplate.tryLock(new DistributedLockCallback<Object>() {
@Override
public Object process() {
return proceed(pjp);
}
@Override
public String getLockName() {
return lockName;
}
}, waitTime, leaseTime, timeUnit, fairLock);
}
public Object proceed(ProceedingJoinPoint pjp) {
try {
return pjp.proceed();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
private boolean isEmpty(Object str) {
return str == null || "".equals(str);
}
private boolean isNotEmpty(Object str) {
return !isEmpty(str);
}
private void methodAfter(Object result, ProceedingJoinPoint pjd) {
Signature signature = pjd.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (result == null) {
return;
}
if(!(result instanceof ModelMap)) {
log.info("### method:{}, result:{}", method, result.toString());
return;
}
ModelMap map = (ModelMap) result;
if(!map.containsKey("SUCCESS")) {
return;
}
}
private void methodBefore(ProceedingJoinPoint pjd) {
String methodName = "class:" + pjd.getTarget().getClass().getName() + " method:" + pjd.getSignature().getName();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
javax.servlet.http.HttpServletRequest request = requestAttributes.getRequest();
log.info("请求地址:" + request.getRequestURL().toString()
+"请求方式:" + request.getMethod()
+"请求类方法:" + methodName
+"请求类方法参数:" + Arrays.toString(pjd.getArgs())
);
}
}