diff --git a/ipowner/.gitignore b/ipowner/.gitignore new file mode 100644 index 0000000..682d4ed --- /dev/null +++ b/ipowner/.gitignore @@ -0,0 +1,27 @@ +# Created by .ignore support plugin (hsz.mobi) +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/.idea/ +/target/ diff --git a/ipowner/pom.xml b/ipowner/pom.xml new file mode 100644 index 0000000..86c23e0 --- /dev/null +++ b/ipowner/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.9.RELEASE + + + + com.ayi + woniu-web + 0.0.1-SNAPSHOT + woniu-web + create by woniu + + jar + + + 1.8 + + + + + com.google.guava + guava + 29.0-jre + + + + org.lionsoul + ip2region + 2.7.0 + + + + org.springframework.boot + spring-boot-starter-aop + + + cn.hutool + hutool-all + 5.3.3 + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-cache + + + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.1 + + + + + mysql + mysql-connector-java + 8.0.29 + + + + com.alipay.sofa + runtime-sofa-boot-starter + 3.18.0 + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.alibaba + fastjson + 1.2.76 + + + com.alibaba + easyexcel + 3.0.5 + + + org.testng + testng + RELEASE + compile + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/ipowner/src/main/java/com/woniu/WoNiuApplication.java b/ipowner/src/main/java/com/woniu/WoNiuApplication.java new file mode 100644 index 0000000..dfa2912 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/WoNiuApplication.java @@ -0,0 +1,20 @@ +package com.woniu; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@SpringBootApplication +@EnableTransactionManagement +@EnableAsync +public class WoNiuApplication { + + public static void main(String[] args) { + SpringApplication.run(WoNiuApplication.class, args); + } + + + +} diff --git a/ipowner/src/main/java/com/woniu/anno/MainTransaction.java b/ipowner/src/main/java/com/woniu/anno/MainTransaction.java new file mode 100644 index 0000000..138828c --- /dev/null +++ b/ipowner/src/main/java/com/woniu/anno/MainTransaction.java @@ -0,0 +1,17 @@ +package com.woniu.anno; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @className: MainTransaction + * @author: woniu + * @date: 2023/6/27 + **/ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MainTransaction { + int value();//子线程数量 +} diff --git a/ipowner/src/main/java/com/woniu/anno/SonTransaction.java b/ipowner/src/main/java/com/woniu/anno/SonTransaction.java new file mode 100644 index 0000000..cee8070 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/anno/SonTransaction.java @@ -0,0 +1,18 @@ +package com.woniu.anno; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 多线程事务注解: 子事务 + * + * @author zlj + * @since 2022/11/3 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SonTransaction { + String value() default ""; +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/aop/TransactionAop.java b/ipowner/src/main/java/com/woniu/aop/TransactionAop.java new file mode 100644 index 0000000..61dd4cd --- /dev/null +++ b/ipowner/src/main/java/com/woniu/aop/TransactionAop.java @@ -0,0 +1,125 @@ +package com.woniu.aop; + +import com.woniu.anno.MainTransaction; +import org.apache.commons.collections4.CollectionUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * SpringBoot 中的多线程事务处理太繁琐?一个自定义注解直接搞定! + */ +@Aspect +@Component +public class TransactionAop { + + //用来存储各线程计数器数据(每次执行后会从map中删除) + private static final Map map = new HashMap<>(); + + @Resource + private PlatformTransactionManager transactionManager; + + @Around("@annotation(mainTransaction)") + public void mainIntercept(ProceedingJoinPoint joinPoint, MainTransaction mainTransaction) throws Throwable { + //当前线程名称 + Thread thread = Thread.currentThread(); + String threadName = thread.getName(); + //初始化计数器 + CountDownLatch mainDownLatch = new CountDownLatch(1); + CountDownLatch sonDownLatch = new CountDownLatch(mainTransaction.value());//@MainTransaction注解中的参数, 为子线程的数量 + // 用来记录子线程的运行状态,只要有一个失败就变为true + AtomicBoolean rollBackFlag = new AtomicBoolean(false); + // 用来存每个子线程的异常,把每个线程的自定义异常向vector的首位置插入,其余异常向末位置插入,避免线程不安全,所以使用vector代替list + Vector exceptionVector = new Vector<>(); + + map.put(threadName + "mainDownLatch", mainDownLatch); + map.put(threadName + "sonDownLatch", sonDownLatch); + map.put(threadName + "rollBackFlag", rollBackFlag); + map.put(threadName + "exceptionVector", exceptionVector); + + try { + joinPoint.proceed();//执行方法 + } catch (Throwable e) { + exceptionVector.add(0, e); + rollBackFlag.set(true);//子线程回滚 + mainDownLatch.countDown();//放行所有子线程 + } + + if (!rollBackFlag.get()) { + try { + // sonDownLatch等待,直到所有子线程执行完插入操作,但此时还没有提交事务 + sonDownLatch.await(); + mainDownLatch.countDown();// 根据rollBackFlag状态放行子线程的await处,告知是回滚还是提交 + } catch (Exception e) { + rollBackFlag.set(true); + exceptionVector.add(0, e); + } + } + if (CollectionUtils.isNotEmpty(exceptionVector)) { + map.remove(threadName + "mainDownLatch"); + map.remove(threadName + "sonDownLatch"); + map.remove(threadName + "rollBackFlag"); + map.remove(threadName + "exceptionVector"); + throw exceptionVector.get(0); + } + } + + @Around("@annotation(com.woniu.anno.SonTransaction)") + public void sonIntercept(ProceedingJoinPoint joinPoint) throws Throwable { + Object[] args = joinPoint.getArgs(); + Thread thread = (Thread) args[args.length - 1]; + String threadName = thread.getName(); + CountDownLatch mainDownLatch = (CountDownLatch) map.get(threadName + "mainDownLatch"); + if (mainDownLatch == null) { + //主事务未加注解时, 直接执行子事务 + joinPoint.proceed(); + return; + } + CountDownLatch sonDownLatch = (CountDownLatch) map.get(threadName + "sonDownLatch"); + AtomicBoolean rollBackFlag = (AtomicBoolean) map.get(threadName + "rollBackFlag"); + Vector exceptionVector = (Vector) map.get(threadName + "exceptionVector"); + + //如果这时有一个子线程已经出错,那当前线程不需要执行 + if (rollBackFlag.get()) { + sonDownLatch.countDown(); + return; + } + + DefaultTransactionDefinition def = new DefaultTransactionDefinition();// 开启事务 + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 设置事务隔离级别 + TransactionStatus status = transactionManager.getTransaction(def); + + try { + joinPoint.proceed();//执行方法 + + sonDownLatch.countDown();// 对sonDownLatch-1 + mainDownLatch.await();// 如果mainDownLatch不是0,线程会在此阻塞,直到mainDownLatch变为0 + // 如果能执行到这一步说明所有子线程都已经执行完毕判断如果atomicBoolean是true就回滚false就提交 + if (rollBackFlag.get()) { + transactionManager.rollback(status); + } else { + transactionManager.commit(status); + } + } catch (Throwable e) { + exceptionVector.add(0, e); + // 回滚 + transactionManager.rollback(status); + // 并把状态设置为true + rollBackFlag.set(true); + mainDownLatch.countDown(); + sonDownLatch.countDown(); + } + } +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/config/MybatisPlusConfig.java b/ipowner/src/main/java/com/woniu/config/MybatisPlusConfig.java new file mode 100644 index 0000000..da6c0a0 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/config/MybatisPlusConfig.java @@ -0,0 +1,30 @@ +package com.woniu.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + + +@Configuration +@MapperScan("com.woniu") +@EnableTransactionManagement +public class MybatisPlusConfig { + + /** + * 新的分页插件,一缓和二缓遵循mybatis的规则, + * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false + * 避免缓存出现问题(该属性会在旧插件移除后一同移除) + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + +} diff --git a/ipowner/src/main/java/com/woniu/config/ThreadPoolTaskConfig.java b/ipowner/src/main/java/com/woniu/config/ThreadPoolTaskConfig.java new file mode 100644 index 0000000..d58d712 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/config/ThreadPoolTaskConfig.java @@ -0,0 +1,57 @@ +package com.woniu.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +@EnableAsync +public class ThreadPoolTaskConfig { + + @Bean("threadPoolTaskExecutor")//自定义线程池名称 + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + //线程池创建的核心线程数,线程池维护线程的最少数量,即使没有任务需要执行,也会一直存活 + executor.setCorePoolSize(16); + + //如果设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭 + //executor.setAllowCoreThreadTimeOut(true); + + //阻塞队列 当核心线程数达到最大时,新任务会放在队列中排队等待执行 + executor.setQueueCapacity(124); + + //最大线程池数量,当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务 + //任务队列已满时, 且当线程数=maxPoolSize,,线程池会拒绝处理任务而抛出异常 + executor.setMaxPoolSize(64); + + //当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize + //允许线程空闲时间30秒,当maxPoolSize的线程在空闲时间到达的时候销毁 + //如果allowCoreThreadTimeout=true,则会直到线程数量=0 + executor.setKeepAliveSeconds(30); + + //spring 提供的 ThreadPoolTaskExecutor 线程池,是有setThreadNamePrefix() 方法的。 + //jdk 提供的ThreadPoolExecutor 线程池是没有 setThreadNamePrefix() 方法的 + executor.setThreadNamePrefix("自定义线程池-"); + + // rejection-policy:拒绝策略:当线程数已经达到maxSize的时候,如何处理新任务 + // CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行, (个人推荐) + // AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。 + // DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常 + // DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + + //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁 + executor.setWaitForTasksToCompleteOnShutdown(true); + //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。 + executor.setAwaitTerminationSeconds(60); + + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/constant/Constants.java b/ipowner/src/main/java/com/woniu/constant/Constants.java new file mode 100644 index 0000000..68656f5 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/constant/Constants.java @@ -0,0 +1,8 @@ +package com.woniu.constant; + + +public class Constants { + + public static String ADDRESS = ""; + +} diff --git a/ipowner/src/main/java/com/woniu/controller/AddressController.java b/ipowner/src/main/java/com/woniu/controller/AddressController.java new file mode 100644 index 0000000..fe0639c --- /dev/null +++ b/ipowner/src/main/java/com/woniu/controller/AddressController.java @@ -0,0 +1,32 @@ +package com.woniu.controller; + +import com.woniu.service.MainService; +import com.woniu.vo.ApiResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +/** + * IP 归属地获取,一个依赖轻松搞定 + */ +@RequestMapping("/api") +@RestController +@Slf4j +public class AddressController { + + + @Autowired + MainService mainService; + + + @GetMapping("/get2") + public ApiResponse get() { + mainService.test1(); + return ApiResponse.ok(null); + } + + + + +} diff --git a/ipowner/src/main/java/com/woniu/enumd/AnimalEnum.java b/ipowner/src/main/java/com/woniu/enumd/AnimalEnum.java new file mode 100644 index 0000000..b4b37bf --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumd/AnimalEnum.java @@ -0,0 +1,24 @@ +package com.woniu.enumd; + +public enum AnimalEnum implements Eat { + Dog(){ + @Override + public void eat() { + System.out.println("吃骨头"); + } + }, + + Cat() { + @Override + public void eat() { + System.out.println("吃鱼干"); + } + }, + + Sheep() { + @Override + public void eat() { + System.out.println("吃草"); + } + } +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/enumd/Eat.java b/ipowner/src/main/java/com/woniu/enumd/Eat.java new file mode 100644 index 0000000..e457a11 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumd/Eat.java @@ -0,0 +1,6 @@ +package com.woniu.enumd; + + +public interface Eat { + void eat(); +} diff --git a/ipowner/src/main/java/com/woniu/enumd/SingletonExample.java b/ipowner/src/main/java/com/woniu/enumd/SingletonExample.java new file mode 100644 index 0000000..d50bb4e --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumd/SingletonExample.java @@ -0,0 +1,32 @@ +package com.woniu.enumd; + +/** + * @Author: woniu + * @Description: 枚举 线程安全 + */ +public class SingletonExample { + + /** + * 构造函数私有化,避免外部创建实例 + */ + private SingletonExample() { + } + + public static SingletonExample getInstance() { + return Singleton.INSTANCE.getInstance(); + } + + private enum Singleton { + INSTANCE; + private SingletonExample instance; + + // JVM 保证这个方法绝对只调用一次 + Singleton() { + instance = new SingletonExample(); + } + + public SingletonExample getInstance() { + return instance; + } + } +} diff --git a/ipowner/src/main/java/com/woniu/enumd/TestMain.java b/ipowner/src/main/java/com/woniu/enumd/TestMain.java new file mode 100644 index 0000000..a0f0470 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumd/TestMain.java @@ -0,0 +1,142 @@ +package com.woniu.enumd; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +//提升Java代码简洁度的小技巧 +public class TestMain { + /** + * 先引入guava依赖 + * + * com.google.guava + * guava + * 29.0-jre + * + * + * @param args + */ + public static void main(String[] args) { + //创建一个list集合并赋值 + List stringList = new ArrayList<>(); + stringList.add("jack"); + stringList.add("pony"); + stringList.add("ekko"); + + //简单改造: + List stringList2 = new ArrayList(4) {{ + add("jack"); + add("pony"); + add("ekko"); + }}; + + + List stringList3 = ImmutableList.of("jack", "pony", "ekko"); + // 类似于 Arrays.asList("jack", "pony", "ekko"); + stringList3.add("maomao"); + System.out.println(stringList3); + + // 终极改造: + List stringList4 = Lists.newArrayList("jack", "pony", "ekko"); + stringList4.add("hahah"); + System.out.println(stringList4); + + + //去除list中的空值 + List nameList = new ArrayList<>(); + List noNullList = new ArrayList<>(); + nameList.add("jack"); + nameList.add("pony"); + nameList.add("ekko"); + nameList.add(null); + for (String o : nameList) { + if (o != null) { + noNullList.add(o); + } + } + + + //使用lamda简化后的写法 + List noNullListFun = nameList + .stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + + //list中的值求和 + List numList = ImmutableList.of(BigDecimal.valueOf(111L) + , BigDecimal.valueOf(8888.22), BigDecimal.valueOf(333.22) + , BigDecimal.valueOf(857857.22), BigDecimal.valueOf(5331.22)); + + BigDecimal total = BigDecimal.ZERO; + for (BigDecimal num : numList) { + total = total.add(num); + } + System.out.println(total); + + + // 简化一下: + List numListSimple = ImmutableList.of(BigDecimal.valueOf(111L) + , BigDecimal.valueOf(8888.22), BigDecimal.valueOf(333.22) + , BigDecimal.valueOf(857857.22), BigDecimal.valueOf(5331.22)); + + BigDecimal reduce = numListSimple.stream().reduce(BigDecimal.ZERO, BigDecimal::add); + System.out.println(reduce); + + + + /* + * 从多个集合中取匹配的值 + * 匹配到姓马的并去重然后放入一个新的集合中 + */ + List nameListA = ImmutableList.of("素云云", "马云云", "腾花花", "阿娇娇", "马飞飞", "廖妹妹"); + List nameListB = ImmutableList.of("素云涛", "唐三三", "小五五", "马中山", "马僻静", "马肥羊"); + List nameListC = ImmutableList.of("张三", "李四", "王二", "麻子", "上官玩儿", "马可菠萝"); + Set nameSet = new HashSet<>(); + nameListA.forEach(n -> { + if (n.startsWith("马")) { + nameSet.add(n); + } + }); + nameListB.forEach(n -> { + if (n.startsWith("马")) { + nameSet.add(n); + } + }); + nameListC.forEach(n -> { + if (n.startsWith("马")) { + nameSet.add(n); + } + }); + System.out.println(nameSet.toString()); + + //改造后的代码: + Set set = Stream.of(nameListA, nameListB, nameListC) + .flatMap(list -> list.stream().filter(name -> name.startsWith("马"))) + .collect(Collectors.toSet()); + + System.out.println(set); + + + } + + private static void eatWhat(String animalType) { +// if ("Dog".equals(animalType)) { +// System.out.println("吃骨头"); +// } else if ("Cat".equals(animalType)) { +// System.out.println("我想吃鱼了"); +// } else if ("Sheep".equals(animalType)) { +// System.out.println("吃草"); +// } else { +// System.out.println("不知道吃啥"); +// } + + + } + + +} diff --git a/ipowner/src/main/java/com/woniu/enumdemo/Test.java b/ipowner/src/main/java/com/woniu/enumdemo/Test.java new file mode 100644 index 0000000..2661176 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumdemo/Test.java @@ -0,0 +1,12 @@ +package com.woniu.enumdemo; + +public class Test { + public static void main(String[] args) { +// Week w = Week.TUESDAY; +// w.getWeekNum(); + for (Week w : Week.values()) { + System.out.println(w); + } + System.out.println("星期天:" + Week.valueOf("SUNDAY")); + } +} diff --git a/ipowner/src/main/java/com/woniu/enumdemo/Week.java b/ipowner/src/main/java/com/woniu/enumdemo/Week.java new file mode 100644 index 0000000..537d24c --- /dev/null +++ b/ipowner/src/main/java/com/woniu/enumdemo/Week.java @@ -0,0 +1,61 @@ +package com.woniu.enumdemo; + +public enum Week { +// SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; +// +// Week(){ +// System.out.println("hello"); +// } + +// SUNDAY(7), MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6); +// +// Week(int weekNum){ +// System.out.println(weekNum); +// } + + +// SUNDAY(7), MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6); +// +// private int weekNum; +// +// Week(int weekNum){ +// this.weekNum = weekNum; +// } +// +// public int getWeekNum() { +// return weekNum; +// } + + + SUNDAY(){ + @Override + public void getWeekNum() { + System.out.println(7); + } + }, + MONDAY() { + @Override + public void getWeekNum() { + System.out.println("星期一"); + } + }, + + TUESDAY(){ + @Override + public void getWeekNum() { + System.out.println("礼拜二"); + } + }, + WEDNESDAY(){ + @Override + public void getWeekNum() { + System.out.println("周三"); + } + }; + + public abstract void getWeekNum(); + + + + +} diff --git a/ipowner/src/main/java/com/woniu/interceptor/IpUrlLimitInterceptor.java b/ipowner/src/main/java/com/woniu/interceptor/IpUrlLimitInterceptor.java new file mode 100644 index 0000000..609fcad --- /dev/null +++ b/ipowner/src/main/java/com/woniu/interceptor/IpUrlLimitInterceptor.java @@ -0,0 +1,45 @@ +package com.woniu.interceptor; + +import com.woniu.ip.AddressUtils; +import com.woniu.ip.IPUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * IP 归属地获取,一个依赖轻松搞定 + */ +@Slf4j +@Configuration +public class IpUrlLimitInterceptor implements HandlerInterceptor { + + + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) { + //通过本地获取 +// 获得ip + String ip = IPUtil.getIpAddr(httpServletRequest); +// 解析具体地址 + String addr = IPUtil.getAddr("2.58.179.255"); + + //通过在线库获取 + String ip2 = IPUtil.getIpAddr(httpServletRequest); + String ipaddr2 = AddressUtils.getRealAddressByIP(ip2); + log.info("IP >> {},Address >> {}", ip2, 2); + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) { + + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { + + } +} diff --git a/ipowner/src/main/java/com/woniu/interceptor/WebConfig.java b/ipowner/src/main/java/com/woniu/interceptor/WebConfig.java new file mode 100644 index 0000000..7dbc8b9 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/interceptor/WebConfig.java @@ -0,0 +1,21 @@ +package com.woniu.interceptor; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Autowired + IpUrlLimitInterceptor ipUrlLimitInterceptor; + + //执行ip拦截器 + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(ipUrlLimitInterceptor) + // 拦截所有请求 + .addPathPatterns("/**"); + } +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/ip/AddressUtils.java b/ipowner/src/main/java/com/woniu/ip/AddressUtils.java new file mode 100644 index 0000000..ac8ba1f --- /dev/null +++ b/ipowner/src/main/java/com/woniu/ip/AddressUtils.java @@ -0,0 +1,89 @@ +package com.woniu.ip; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; + +@Slf4j +public class AddressUtils { + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + /** + * 离线IP库总会出现更新不及时的问题, + * 如果想要获取更加全面的ip 地址信息, + * 可使用在线数据库,这里提供的是 whois.pconline.com的IP解析, + * 该IP解析在我的使用过程中表现非常流畅,而且只有少数的ip 存在无法解析的情况。 + * @param ip + * @return + */ + public static String getRealAddressByIP(String ip) { + String address = UNKNOWN; + if (true) { + try { + String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true", "GBK"); + if (StrUtil.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSONObject.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + String addr = obj.getString("addr"); + return String.format("%s %s %s", region, city, addr); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + } + return address; + } + + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try { + String urlNameString = url + "?" + param; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (Exception ex) { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } +} diff --git a/ipowner/src/main/java/com/woniu/ip/IPUtil.java b/ipowner/src/main/java/com/woniu/ip/IPUtil.java new file mode 100644 index 0000000..82e47a5 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/ip/IPUtil.java @@ -0,0 +1,65 @@ +package com.woniu.ip; + + +import lombok.extern.slf4j.Slf4j; +import org.lionsoul.ip2region.xdb.Searcher; + +import javax.servlet.http.HttpServletRequest; + +@Slf4j +public class IPUtil { + + private static final String UNKNOWN = "unknown"; + + protected IPUtil() { + } + + /** + * 获取 IP地址 + * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址, + * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + + public static String getAddr(String ip) { + String dbPath = "D:\\java project\\fanfu-web-master\\woniu-web-master\\src\\main\\resources\\ip2region\\ip2region.xdb"; + // 1、从 dbPath 加载整个 xdb 到内存。 + byte[] cBuff; + try { + cBuff = Searcher.loadContentFromFile(dbPath); + } catch (Exception e) { + log.info("failed to load content from `%s`: %s\n", dbPath, e); + return null; + } + + // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 + Searcher searcher; + try { + searcher = Searcher.newWithBuffer(cBuff); + } catch (Exception e) { + log.info("failed to create content cached searcher: %s\n", e); + return null; + } + // 3、查询 + try { + String region = searcher.search(ip); + return region; + } catch (Exception e) { + log.info("failed to search(%s): %s\n", ip, e); + } + return null; + } +} diff --git a/ipowner/src/main/java/com/woniu/service/MainService.java b/ipowner/src/main/java/com/woniu/service/MainService.java new file mode 100644 index 0000000..ddfa720 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/service/MainService.java @@ -0,0 +1,27 @@ +package com.woniu.service; + + +import com.woniu.anno.MainTransaction; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; + + +@Service +public class MainService { + + @Resource + private SonService sonService; + + @MainTransaction(3) //调用的方法中sonMethod1/sonMethod2/sonMethod3使用@Async开启了线程, 所以参数为: 3 + @Transactional(rollbackFor = Exception.class) + public void test1() { + sonService.sonMethod1("蜗牛", Thread.currentThread()); + sonService.sonMethod2("乾隆", "山治", Thread.currentThread()); + sonService.sonMethod3("小宝", Thread.currentThread()); + sonService.sonMethod4("罗宾"); + } + + +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/service/SonService.java b/ipowner/src/main/java/com/woniu/service/SonService.java new file mode 100644 index 0000000..55bb3c6 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/service/SonService.java @@ -0,0 +1,49 @@ +package com.woniu.service; + +import com.woniu.anno.SonTransaction; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author woniu + * @since 2023/06/28 + */ +@Service +public class SonService { + + /** + * 参数说明: 以下4个方法参数和此相同 + * + * @param args 业务中需要传递的参数 + * @param thread 调用者的线程, 用于aop获取参数, 不建议以方法重写的方式简略此参数, + * 在调用者方法中可以以此参数为标识计算子线程的个数作为注解参数,避免线程参数计算错误导致锁表 + * 传参时参数固定为: Thread.currentThread() + */ + @Transactional(rollbackFor = Exception.class) + @Async("threadPoolTaskExecutor") + @SonTransaction + public void sonMethod1(String args, Thread thread) { + System.out.println(args + "开启了线程"); + } + + @Transactional(rollbackFor = Exception.class) + @Async("threadPoolTaskExecutor") + @SonTransaction + public void sonMethod2(String args1, String args2, Thread thread) { + System.out.println(args1 + "和" + args2 + "开启了线程"); + } + + @Transactional(rollbackFor = Exception.class) + @Async("threadPoolTaskExecutor") + @SonTransaction + public void sonMethod3(String args, Thread thread) { + System.out.println(args + "开启了线程"); + } + + //sonMethod4方法没有使用线程池 + @Transactional(rollbackFor = Exception.class) + public void sonMethod4(String args) { + System.out.println(args + "没有开启线程"); + } +} \ No newline at end of file diff --git a/ipowner/src/main/java/com/woniu/thread/FastJonTest.java b/ipowner/src/main/java/com/woniu/thread/FastJonTest.java new file mode 100644 index 0000000..d5fd144 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/thread/FastJonTest.java @@ -0,0 +1,20 @@ +package com.woniu.thread; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; + + +/** + * 加了一行log日志,结果引发了线上事故 + */ +@Slf4j +public class FastJonTest { + + public static void main(String[] args) { + StudentDTO studentDTO = new StudentDTO(); + String str = JSON.toJSONString(studentDTO); + log.info(str); + } + + +} diff --git a/ipowner/src/main/java/com/woniu/thread/StudentDTO.java b/ipowner/src/main/java/com/woniu/thread/StudentDTO.java new file mode 100644 index 0000000..73e6791 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/thread/StudentDTO.java @@ -0,0 +1,21 @@ +package com.woniu.thread; + + +import com.alibaba.fastjson.annotation.JSONField; + +public class StudentDTO { + private String studentName; + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + @JSONField(serialize = false) + public Boolean isChinaName() { + return this.studentName.equals("孔子"); + } +} + diff --git a/ipowner/src/main/java/com/woniu/vo/ApiResponse.java b/ipowner/src/main/java/com/woniu/vo/ApiResponse.java new file mode 100644 index 0000000..fd91231 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/vo/ApiResponse.java @@ -0,0 +1,26 @@ +package com.woniu.vo; + +import lombok.Data; + +@Data +public class ApiResponse { + + private T data; + private String message; + private String code; + + public ApiResponse(T data, String message, String code) { + this.data = data; + this.message = message; + this.code = code; + } + + + public static ApiResponse fail(T data, MessageEnum messageEnum) { + return new ApiResponse(data, messageEnum.getMessage(), messageEnum.getCode()); + } + + public static ApiResponse ok(T data) { + return new ApiResponse(data, "响应正常", "200"); + } +} diff --git a/ipowner/src/main/java/com/woniu/vo/MessageEnum.java b/ipowner/src/main/java/com/woniu/vo/MessageEnum.java new file mode 100644 index 0000000..2baa785 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/vo/MessageEnum.java @@ -0,0 +1,28 @@ +package com.woniu.vo; + +/** + * @className: MessageEnum + * @author: woniu + * @date: 2023/6/25 + **/ + + +public enum MessageEnum { + ParamException("400","参数错误!"); + + private String code; + private String message; + + MessageEnum(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/ipowner/src/main/java/com/woniu/write/MultiThreadFileWriter.java b/ipowner/src/main/java/com/woniu/write/MultiThreadFileWriter.java new file mode 100644 index 0000000..bc4a320 --- /dev/null +++ b/ipowner/src/main/java/com/woniu/write/MultiThreadFileWriter.java @@ -0,0 +1,67 @@ +package com.woniu.write; + + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class MultiThreadFileWriter { + + private File file; + private FileWriter fileWriter; + private Lock lock; + + public MultiThreadFileWriter(String filePath) throws IOException { + this.file = new File(filePath); + if (!file.exists()) { + file.createNewFile(); + } + this.fileWriter = new FileWriter(file, true); // 追加写入 + this.lock = new ReentrantLock(); + } + + public void write(String content) { + lock.lock(); + try { + fileWriter.write(content); + fileWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + } + + public void close() { + try { + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) throws IOException { + MultiThreadFileWriter writer = new MultiThreadFileWriter("test.txt"); + Thread thread1 = new Thread(() -> { + for (int i = 0; i < 100; i++) { + writer.write("Thread 1: " + i + "\n"); + } + }); + Thread thread2 = new Thread(() -> { + for (int i = 0; i < 100; i++) { + writer.write("Thread 2: " + i + "\n"); + } + }); + thread1.start(); + thread2.start(); + try { + thread1.join(); + thread2.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + writer.close(); + } +} diff --git a/ipowner/src/main/resources/application.properties b/ipowner/src/main/resources/application.properties new file mode 100644 index 0000000..0a75ef4 --- /dev/null +++ b/ipowner/src/main/resources/application.properties @@ -0,0 +1,12 @@ +spring.datasource.username=root +spring.datasource.password=123456 +spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +mybatis.mapper-locations=classpath:/mapper/*.xml + +spring.cache.type=simple +spring.application.name=yyyy +#spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +#spring.jackson.time-zone=GMT+8 + diff --git a/ipowner/src/main/resources/ip2region/ip2region.xdb b/ipowner/src/main/resources/ip2region/ip2region.xdb new file mode 100644 index 0000000..9f6502b Binary files /dev/null and b/ipowner/src/main/resources/ip2region/ip2region.xdb differ diff --git a/ipowner/src/main/resources/logback-spring.xml b/ipowner/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..9b029bd --- /dev/null +++ b/ipowner/src/main/resources/logback-spring.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + + + + + + diff --git a/ipowner/src/main/resources/mybatis-config.xml b/ipowner/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..b099e5d --- /dev/null +++ b/ipowner/src/main/resources/mybatis-config.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ipowner/woniu-web.iml b/ipowner/woniu-web.iml new file mode 100644 index 0000000..d08d461 --- /dev/null +++ b/ipowner/woniu-web.iml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file