- 1、异常处理
- 2、 异步请求
- 3、发送邮件
- 4、跨域
- 5、获取文件
- 6、远程调试
- -agentlib:jdwp=transport=dt_socket,server=y,address=8000
- -agentlib:jdwp=transport=dt_socket,server=y,address=localhost:8000,timeout=5000
- -agentlib:jdwp=transport=dt_shmem,server=y,suspend=n
- -agentlib:jdwp=transport=dt_socket,address=myhost:8000
- -agentlib:jdwp=transport=dt_socket,server=y,address=8000,onthrow=java.io.IOException,launch=/usr/local/bin/debugstub
- 7、静态资源服务
- 8、Mybatis plus LocalTime
- 9、AOP
1、异常处理
1.1 全局异常
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String XXXExceptionHandler(Exception e) {
log.info(e.toString());
return "123";
}
}
1.2 自定义异常
// 异常直接抛给 tomcat, 如果后续处理异常,则显示错误页面
throw new CustomExceptionHandler("错误");
@NoArgsConstructor
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "拒绝访问") // 403
public class CustomExceptionHandler extends RuntimeException{
public CustomExceptionHandler(String message){
super(message);
}
}
@Order(value = Ordered.HIGHEST_PRECEDENCE) // 最高级别,处理所有异常(其他任何异常配置失效)
@Component
public class CustomerHandlerException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object handler, Exception e) {
try {
httpServletResponse.sendError(555,"wuwuwu异常"); // 抛给 tomcat
} catch (IOException ioException) {
ioException.printStackTrace();
}
return new ModelAndView();
}
}
2、 异步请求
启动类: @EnableAsync
@GetMapping("/callable")
private Callable<Object> getCallable(){
log.info(Thread.currentThread().getName() + " 进入 getCallable 方法");
Callable<Object> callable = new Callable<Object>() {
@Override
public Object call() throws Exception {
log.info(Thread.currentThread().getName() + " 进入 call 方法");
Object say = sayHello();
log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
return say;
}
};
log.info(Thread.currentThread().getName() + " 从 getCallable 方法返回");
return callable;
}
@GetMapping("/webAsyncTask")
private WebAsyncTask<Object> getWebAsyncTask(){
log.info(Thread.currentThread().getName() + " 进入 getWebAsyncTask 方法");
// 3s 没返回,则认为超时
WebAsyncTask<Object> webAsyncTask = new WebAsyncTask<>(3000, new Callable<Object>() {
@Override
public Object call() throws Exception {
log.info(Thread.currentThread().getName() + " 进入call方法");
Object say = sayHello();
log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
return say;
}
});
log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
webAsyncTask.onCompletion(new Runnable() {
@Override
public void run() {
log.info(Thread.currentThread().getName() + " 执行完毕");
}
});
webAsyncTask.onTimeout(new Callable<Object>() {
@Override
public Object call() throws Exception {
log.info(Thread.currentThread().getName() + " onTimeout");
// 超时的时候,直接抛异常,让外层统一处理超时异常
throw new TimeoutException("调用超时");
}
});
return webAsyncTask;
}
@GetMapping("/deferredResult")
private DeferredResult<Object> getDeferredResult(){
log.info(Thread.currentThread().getName() + " 进入 getDeferredResult 方法");
DeferredResult<Object> deferredResult = new DeferredResult<>(3000L);
// 调用长时间执行任务
execute(deferredResult);
// 当长时间任务中使用deferred.setResult("world");这个方法时,会从长时间任务中返回,继续controller里面的流程
log.info(Thread.currentThread().getName() + "从 execute 方法返回");
// 超时的回调方法
deferredResult.onTimeout(new Runnable(){
@Override
public void run() {
log.info(Thread.currentThread().getName() + " onTimeout");
// 返回超时信息
deferredResult.setErrorResult("time out!");
}
});
// 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
deferredResult.onCompletion(new Runnable(){
@Override
public void run() {
log.info(Thread.currentThread().getName() + " onCompletion");
}
});
return deferredResult;
}
@Async
public void execute(DeferredResult<Object> deferredResult) {
log.info(Thread.currentThread().getName() + "进入 execute 方法");
try {
// 模拟长时间任务调用,睡眠 2s
TimeUnit.SECONDS.sleep(5);
// 2s 后给 Deferred 发送成功消息,告诉 Deferred ,我这边已经处理完了,可以返回给客户端了
deferredResult.setResult("world");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Object sayHello() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map<String,Object> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
return map;
}
3、发送邮件
Pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
yaml
spring:
# 126邮箱SMTP服务器地址:smtp.126.com,端口号:465或者994
# 2163邮箱SMTP服务器地址:smtp.163.com,端口号:465或者994
# yeah邮箱SMTP服务器地址:smtp.yeah.net,端口号:465或者994
# qq邮箱SMTP服务器地址:smtp.qq.com,端口号465或587*
mail:
default-encoding: UTF-8
host: smtp.qq.com
username: 953598751@qq.com
password: xxx
port: 587
properties:
mail:
stmp:
socketFactoryClass: javax.net.ssl.SSLSocketFactory
#表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误
debug: true
Test
@Autowired
private JavaMailSender javaMailSender;
private final ClassPathResource classPathResource = new ClassPathResource("static/test.jpg");
@Test
public void sendString(){
// 构建一个邮件对象
SimpleMailMessage message = new SimpleMailMessage();
// 设置邮件主题
message.setSubject("这是一封测试邮件");
// 设置邮件发送者,这个跟 application.yml中设置的要一致
message.setFrom("953598751@qq.com");
// 设置邮件接收者,可以有多个接收者,中间用逗号隔开,以下类似
message.setTo("1349587649@qq.com");
// 设置邮件抄送人,可以有多个抄送人。
// 将邮件同时送给收信人以外的人,用户所写的邮件抄送一份给别人,对方可以看见该用户的E-mail发送给了谁。
// message.setCc("idu8972@dingtalk.com");
// 设置隐秘抄送人,可以有多个。
// 将邮件同时送给收信人以外的人,用户所写的邮件抄送一份给别人,但是对方不能查看到这封邮件同时还发送给了哪些人。
// message.setBcc("idu8972@dingtalk.com");
// 设置邮件发送日期
message.setSentDate(new Date());
// 设置邮件的正文
message.setText("这是测试邮件的正文");
// 发送邮件
javaMailSender.send(message);
}
/**
* 发送带附件的邮件
*/
@Test
public void sendAttachFileMail() throws MessagingException, IOException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
// true表示构建一个可以带附件的邮件对象
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
helper.setSubject("这是一封测试邮件");
helper.setFrom("953598751@qq.com");
helper.setTo("1349587649@qq.com");
helper.setSentDate(new Date());
helper.setText("这是测试邮件的正文");
// 第一个参数是自定义的名称,后缀需要加上,第二个参数是文件的位置
helper.addAttachment("报表.pdf",classPathResource.getFile());
javaMailSender.send(mimeMessage);
}
/**
* 正文中带图片的邮件
*/
@Test
public void sendImgResMail() throws MessagingException, IOException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("这是一封测试邮件");
helper.setFrom("953598751@qq.com");
helper.setTo("1349587649@qq.com");
helper.setSentDate(new Date());
// src='cid:p01' 占位符写法 ,第二个参数true表示这是一个html文本
helper.setText("<H1>hello 大家好,这是一封测试邮件,这封邮件包含两种图片,分别如下</H1>" +
"<H3>第一张图片:</H3><img src='cid:img01'/>",true);
// 第一个参数指的是 html 中占位符的名字,第二个参数就是文件的位置
helper.addInline("img01",new FileSystemResource(classPathResource.getFile()));
helper.addAttachment("test.jpg",classPathResource.getFile());
javaMailSender.send(mimeMessage);
}
4、跨域
@Configuration
@EnableWebMvc // 使用 swagger 不能有该注解
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST")
.allowCredentials(false).maxAge(3600);
}
}
@Configuration
class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//设置放行哪些原始域 SpringBoot2.4.4下低版本使用.allowedOrigins("*")
.allowedOriginPatterns("*")
//放行哪些请求方式
.allowedMethods("GET", "POST")
//.allowedMethods("*") //或者放行全部
//放行哪些原始请求头部信息
.allowedHeaders("*")
//暴露哪些原始请求头部信息
.exposedHeaders("*");
}
/** swagger 访问 **/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
WebMvcConfigurer.super.addResourceHandlers(registry);
}
}
public class MyCorsFilter extends CorsFilter {
public MyCorsFilter() {
super(configurationSource());
}
private static UrlBasedCorsConfigurationSource configurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("GET","POST");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
// 注解形式
@CrossOrigin(origins = "*",maxAge = 3600)
5、获取文件
// 获取 classpath 路径下的文件
ClassPathResource classPathResource = new ClassPathResource("static/index.html");
File file = classPathResource.getFile();
System.out.println(file.getName());
// 获取项目路径下的文件
FileSystemResource fileSystemResource = new FileSystemResource("pom.xml");
File file1 = fileSystemResource.getFile();
FileReader fileReader = new FileReader(file1);
char[] chars = new char[100];
if(fileReader.read(chars)!=-1){
System.out.println(String.valueOf(chars));
}
6、远程调试
- 启动命令: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar web.jar
-agentlib:jdwp=transport=dt_socket,server=y,address=8000
以 Socket 方式监听 8000 端口,程序启动阻塞(suspend 的默认值为y)直到被连接。
-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:8000,timeout=5000
以 Socket 方式监听 8000 端口,当程序启动后5秒无调试者连接的话终止,程序启动阻塞(suspend的默认值为y)直到被连接。
-agentlib:jdwp=transport=dt_shmem,server=y,suspend=n
选择可用的共享内存连接地址并使用 stdout 打印,程序启动不阻塞。
-agentlib:jdwp=transport=dt_socket,address=myhost:8000
以 socket 方式连接到 myhost:8000上的调试程序,在连接成功前启动阻塞。
-agentlib:jdwp=transport=dt_socket,server=y,address=8000,onthrow=java.io.IOException,launch=/usr/local/bin/debugstub
以 Socket 方式监听 8000 端口,程序启动阻塞(suspend的默认值为y)直到被连接。当抛出
IOException
时中断调试,转而执行usr/local/bin/debugstub
程序。
- 参数说明
| transport | 指定运行的被调试应用和调试者之间的通信协议,它由几个可选值: |
| —- | —- |
| |
dt_socket
:主要的方式,采用 socket 方式连接 | | server | 当前应用作为调试服务端还是客户端,默认为n
。
如果你想将当前应用作为被调试应用,设置该值为y
;如果你想将当前应用作为客户端,作为调试的发起者,设置该值为n
| | suspend | 当前应用启动后,是否阻塞应用直到被连接,默认值为y
。
在大部分的应用场景,这个值为n
,即不需要应用阻塞等待连接。
一个可能为y
的应用场景是,你的程序在启动时出现了一个故障,为了调试,必须等到调试方连接上来后程序再启动 | | address | 暴露的调试连接端口,默认值为8000
| | onthrow | 当程序抛出设定异常时,中断调试 | | onuncaught | 当程序抛出未捕获异常时,是否中断调试,默认值为n
| | launch | 当调试中断时,执行的程序。 | | timeout | 该参数限定为java -agentlib:jdwp=…
可用,单位为毫秒ms。
当 suspend = y 时,该值表示等待连接的超时;当 suspend = n 时,该值表示连接后的使用超时。 |
- IDEA 调试
Edit Configurations
>
添加一个 Remote JVM Debug>
配置 Host 和 Port>
启动该配置
7、静态资源服务
@Configuration
class CorsConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("file:D:/app_3d/fpt/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
WebMvcConfigurer.super.addResourceHandlers(registry);
}
}
8、Mybatis plus LocalTime
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Bean(name = "mapperObject")
public ObjectMapper getObjectMapper() {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
return new ObjectMapper().registerModule(javaTimeModule);
}
9、AOP
<!-- <dependency>-->
<!-- <groupId>org.aspectj</groupId>-->
<!-- <artifactId>aspectjweaver</artifactId>-->
<!-- <version>1.8.13</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Component
public class User {
public void add(String str){
System.out.println("add..........." + str);
}
}
@Component
@Aspect
@Order(1) // 设置增强顺序
public class UserProxy1 {
/**
* 前置通知
*/
@Before(value = "execution(* com.cug.aop.User.add(..))")
public void before(){
System.out.println("UserProxy1 before...........");
}
}
/**
* @Description 创建不同的方法,代表不同的通知类型
*/
@Aspect
@Component
@Order(2) // 设置增强顺序
public class UserProxy {
/**
* 相同切入点抽取
*/
@Pointcut(value = "execution(* com.cug.aop.User.add(..))")
public void pointDemo(){}
/**
* 前置通知
*/
@Before(value = "pointDemo()")
public void before(JoinPoint joinPoint){
System.out.println(Arrays.toString(joinPoint.getArgs()));
// 2
System.out.println("before...........");
}
@After(value = "execution(* com.cug.aop.User.add(..))")
public void after(){
// 4
System.out.println("after..........");
}
@AfterReturning(value = "execution(* com.cug.aop.User.add(..))")
public void afterReturning(){
// 3 增强的方法之后
System.out.println("AfterReturning..........");
}
@AfterThrowing(value = "execution(* com.cug.aop.User.add(..))")
public void afterThrowing(){
System.out.println("AfterThrowing..........");
}
@Around(value = "execution(* com.cug.aop.User.add(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 1
System.out.println("Around before..........");
// 被增强的方法执行
proceedingJoinPoint.proceed();
// 5
System.out.println("Around after..........");
return "around";
}
}
@SpringBootTest
class ApoApplicationTests {
@Autowired
private User user;
@Test
void contextLoads() {
user.add("111");
}
}