企业开发中遇到的常用注解

Posted by Futari on 2022-07-07
Estimated Reading Time 8 Minutes
Words 2.2k In Total
Viewed Times

@Builder实现建造者模式

建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程,而不是一次性的完成对象构建。
这个注解提供了一种优雅的编程方式,用来创建对象,原理上通过以下来实现
(Ps:父类的属性不能产于builder)
1
2
3
4
5
6
7
8
@Builder
public class Demo {
private final int finalVal = 10;

private String name;
private int age;
}

经过编译之后的字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Demo {
private final int finalVal = 10;
private String name;
private int age;

Demo(String name, int age) {
this.name = name;
this.age = age;
}
//生成了一个静态的builder()和一个静态内部类
public static Demo.DemoBuilder builder() {
return new Demo.DemoBuilder();
}

public static class DemoBuilder {
private String name;
private int age;

DemoBuilder() {
}

public Demo.DemoBuilder name(String name) {
this.name = name;
return this;
}

public Demo.DemoBuilder age(int age) {
this.age = age;
return this;
}

public Demo build() {
return new Demo(this.name, this.age);
}

public String toString() {
String var10000 = this.name;
return this.age;
}
}
}

因此我们构造一个对象就可以优雅的这么来:

1
2
3
4
5
public static void main(String[] args) {
//不用写内部类的类名,直接外部类就能调
Demo demo = Demo.builder().name("aa").age(10).build();
System.out.println(demo);
}

里面有一些自定义参数,我表示,完全没有必要去自定义。直接调用@Builder.Default用默认

也就是在声明时就指定默认值(=),如下:

1
2
@Builder.Default
private DeviceType deviceType=DeviceType.ANDROID;

@WebFilter配置Servlet过滤器(不是拦截器,两者不同)

先搞清楚两者的区别,拦截器是实现接口之后并添加到拦截器集合中才会生效,Interceptor是Aop的一种实现方式,依赖于web框架,在底层是基于Java反射机制的,在访问前或者访问后加入某些操作,也就是实现了解耦合。

而过滤器Filter是基于Servlet的,一般是对请求进行一些过滤操作,想想之前的项目,Interceptor一般是为了控制用户的访问资源路径,而Filter一般是对字符进行过滤、修改HttpServletRequest的一些参数。
Filter并不能处理用户请求,主要是对httpServletRequest进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。

screenShot.png

总结区别

1
2
3
4
5
6
7
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对Controller请求起作用,而过滤器则可以对几乎所有的请求起作用。
作用范围上的区别只是一部分,最主要的是在操作的区别,拦截器在自己的拦截范围内加入操作,而Filter是对自己范围内的请求进行或者过滤,不是聚焦于增加业务和解耦上。
4、拦截器可以访问Controller上下文、值栈里的对象,而过滤器不能访问。
5、在Controller的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次(之后都是经过Filter方法进行过滤)。
6、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
为什么第六条Filter不能注入Service
web容器加载顺序导致, 加载顺序是listener——filter——servlet,当项目启动时,filter先于servlet初始化(init), 而Spring中默认bean(如@Service)的初始化是在Servlet后进行的,所以会注入失败,找不到相对应的Bean
具体实现Filter上要重写filter方法,如下:
1
2
3
4
5
6
7
8
9
10
11
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletContext = servletRequest.getServletContext();
Integer times = (Integer) servletContext.getAttribute("times");
if(null == times){
servletContext.setAttribute("times",1);
}else {
servletContext.setAttribute("times",++times);
}
filterChain.doFilter(servletRequest,servletResponse);
}

ServletContext是线程上下文,而FilterChain是过滤器执行链(对同一请求的),也就是有多个Filter,会按顺匈奴来执行,调用servletChain.doFilter()代表经过当前过滤器,然后调用下一个对该请求的过滤器,注意:请求完之后请求结果要进行返回,还是要从链的尾端在过滤回链的头,就像函数递归一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.bg.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "*")
public class FirstFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
System.out.println("FirstFilter before");
chain.doFilter(request, response);
System.out.println("FirstFilter after");
}
}

结果:
FirstFilter before
SecondFilter before
ThreeFilter before
ThreeFilter after
SecondFilter after
FirstFilter after

SpringBoot利用@WebFilter配置Filter

第一步:利用@WebFilter创建Filter过滤器类

(从下面的配置代码可以看到主要方法有三个,和Interceptor数量上是相同的,不过过滤器不是在请求的前中后了,而是这三个方法:在init初始化时方法,doFilter过滤方法,destory(过滤器销毁方法),这三个方法是是在Filter接口中定义的,拦截器是在Interceptor接口中定义的。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//@Component//无需添加此注解,在启动类添加@ServletComponentScan注解后,会自动将带有@WebFilter的注解进行注入!
@WebFilter(urlPatterns = "/lvjia/carbodyad/api/*", filterName = "rest0PubFilter")
@Order(1)//指定过滤器的执行顺序,值越大越靠后执行
public class Rest0PubFilter implements Filter {


@Override
public void init(FilterConfig filterConfig) {//初始化过滤器
System.out.println("getFilterName:"+filterConfig.getFilterName());//返回<filter-name>元素的设置值。
System.out.println("getServletContext:"+filterConfig.getServletContext());//返回FilterConfig对象中所包装的ServletContext对象的引用。
System.out.println("getInitParameter:"+filterConfig.getInitParameter("cacheTimeout"));//用于返回在web.xml文件中为Filter所设置的某个名称的初始化的参数值
System.out.println("getInitParameterNames:"+filterConfig.getInitParameterNames());//返回一个Enumeration集合对象。
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
if(false){
response.sendRedirect("http://localhost:8081/demo/test/login");//重定向
}
filterChain.doFilter(servletRequest, servletResponse);//doFilter将请求转发给过滤器链下一个filter , 如果没有filter那就是你请求的资源

}

@Override
public void destroy() {
}

}

screenShot.png

注意:

@WebFilter(urlPatterns = “/lvjia/carbodyad/api/*”, filterName = “rest0PubFilter”)
filterName(命名)的首字母一定要小写!!!小写!!!小写!!!
我因为这个,导致配置的多个过滤器拦截url都失效了!不管啥路径,全给我拦截到Filter里去了

第二步;Application启动类添加@ServletComponentScan注解

1
2
3
4
5
6
7
@SpringBootApplication
@ServletComponentScan //Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Filter的优先级比Servlet高,通过FilterChain来进行到chain的下一个过滤器,在服务启动时组合链条,所以符合责任链模式。
责任链和普通的链肯定是不一样的

责任链执行只需要获取到第一个节点就能开始执行,所以必须是链表形式关联的,一个个地执行不一定算责任链,需要有关联关系,所以手动new各个handler然后一个个调方法不是责任链。

并且链上的任务的数量应该是便于修改的,并且顺序也应该是可以修改的,所以我们应该让handler的下一个handler的引用应该是相同的,所以我们需要一个抽象类。请求者也不需要知道后台执行链如何进行处理,每一个处理对象只需要负责自己的任务就好,其他的交给next节点。

在一般的网关中,都会经过 API 接口限流、黑名单拦截、用户会话、参数过滤等这么几个关键点,那么原理是不是相当于Filter呢?能不能使用责任链来进行实现

说白了责任链模式有什么用呢?

其实就是将请求与多个处理模块解耦合,仅仅像链条一样,只需要指出入口,不再关心之后如何处理以及处理的整个过程。

Lombok提供的@Data注解源码解读

点击@Data注解,可以发现其是一个复合注解,但是可以看到里面有一个构造器注解@RequiredArgsConstructor,该注解可以将带有final修饰(或者NotNull)的属性创建构造方法,注意该final属性如果没有赋值成常量的话,那么必须要有该注解才行,不然不赋值会报错。

所以可以发现@Data注解是不包括构造方法的,如果想要生成对应的构造方法,需要自己手动加@AllArgsConstructor等注解。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !