0%

前言

因为http是无状态的,所以产生了cookie,session机制来构造一个会话。

一般流程是这样:

  • 客户端发送请求
  • 服务端接收请求,并且设置Set-Cookie头,分配一个sessionId
  • 客户端存在cookie,下次发送时带上sessionid
  • 服务端解析sessionid,找到对应的session信息
    Read more »

主要的GC算法


  • 引用计数
    不赘述了,主要是无法解决互相引用的问题
  • 可达性分析
    就是从一些叫做GC Roots的引用开始,一步一步的找能达到的对象,如果没达到,那么就不可达的,就会开始回收它。

HotSpot采用的是可达性分析。
那么哪些可以作为GC Roots呢?

Read more »

同步,阻塞,非阻塞


先来谈谈这个。
很多人一直搞不清,从概念上去解释的话,我也很难解释清楚。
不过知乎上有个回答很nice。我引用过来。

Read more »

Java泛型的实现

提到Java的泛型一般人都会想到类型擦除(Type Erasure)机制。
如果你没想到,请去补一补。。。。

Read more »

前言

最近看《Java编程思想》,看了内部类的讲解,发现自己好多都没听过或者没注意到。

Read more »

最近想要自己写一个简单的http server,因为之前的那个是epoll的,只能运行在linux平台上。
于是我便找到了平台通用的libevent

libevent是事件驱动的,支持异步的网络框架。

十分值得学习。

Read more »

叶子

去帮室友拿快递的时候看到的。
暑假没人打扫。
落了满地。

项目源码

@PathVariable

在web后端框架中,路由设计是很重要的一步。
各个框架对接口设计的支持大多差不多,其中必然有

1
2
@GET("/users/{id}/")   
@GET("/users/:id/")

这样的标准。

其中{id}:id表示是一个不确定的值,这个随着rustful的兴起也变得常用起来
比如我想要查看id = 1的用户的信息,那么我的请求便是/users/1
如果查看id = 321的用户信息,请求的便是/users/321

那么这个id便是一个不确定量。

于是对于这个方法而言这个id值便是参数,在我们调用时需要传进去。

Spring MVC中,完整的方法是这样

1
2
3
4
@GetMapping("/employer/{phoneNumber}")  
public ResponseEntity<?> findByPhoneNumber(@PathVariable String phoneNumber) {

}

所以这就要求我们在设计框架时还需要利用反射把请求的uri的特定路径值作为参数传进方法中。

route设计

正常我们进行反射调用时,像这样

1
method().invoke(object(), paramters());

首先route中需要有这个对应的Method,其次对应的对象也应该在其中,然后是参数。

于是我设计为

1
2
3
4
5
6
7
8
9
10
11
12
class Route {
//对应的uri
private String path;
//method所在的对象
private Object object;
//对应的路由method
private Method method;
//对应的方法,比如GET,POST等
private HttpMethod httpMethod;
//对应的参数
private Object[] paramters;
}

路由匹配

那么问题来了,对于不同的uri,我们应该怎么匹配路由呢。

我想到的是用正则
设计一个这样的Map

1
Map<Pattern, Route> routeMap = new HashMap<>();

对于/users/{id}/这样的字符串,我们需要产生一个pattern出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//根据参数进行正则替换
public static Pattern pathCompiler(String path, Method method) {
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
if (parameter.getAnnotations() == null) {
continue;
}
Annotation annotation = parameter.getAnnotations()[0];
if (annotation instanceof PathVariable) {
//如果是字符串
if (parameter.getType() == String.class) {
path = path.replace("{" + parameter.getName()+"}","[0-9\\d\\D]*");
}
//如果是数字
else if (parameter.getType() == Integer.class
|| parameter.getType() == Long.class) {

path = path.replace("{" + parameter.getName()+"}","[0-9]*");
}

}
}
return Pattern.compile(path);
}

键为urlPattern,那么在匹配时只要遍历一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//保存着所有的路由
Map<Pattern, Route> routeMap = new HashMap<>();

public static Route findRoute(String path, HttpMethod method) {
for (Pattern pattern : routeMap.keySet()) {
if (pattern.matcher(path).matches()) {
if (routeMap.get(pattern).getHttpMethod().equals(method)) {
/* 进行参数的赋值 */
return routeMap.get(pattern);
}
}
}
return null;
}

我们在使用spring sec的时候,一般会继承WebSecurityConfigurerAdapter
然后选择覆盖

protected void configure(AuthenticationManagerBuilder auth)
protected void configure(HttpSecurity http)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(studentService).passwordEncoder(encoder);
auth.userDetailsService(teacherService).passwordEncoder(encoder);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated()
.and()
.formLogin().loginPage("/login").permitAll();
}

一般而言登录的数据我们在protected void configure(AuthenticationManagerBuilder auth)中,我们在studentService中配置一个

1
2
3
4
5
6
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Student student = studentRepository.findByStudentId(username)
.orElseThrow(() -> new UsernameNotFoundException("Not found: " + username));
return new StudentDetails(student);
}

方法就好。

但是遇到一个问题,这样的话用户名和密码都是定死的,我们拿不到form-data数据,如果因为前端的问题,这种密码登录方式以外,我们还要稍微修改提交给我们的form-data中的密码数据,做一下处理,自定义一个登录呢。

这个时候就需要用到AuthenticationProvider了。
这是一个接口,提供了两种方法

1
2
3
4
5
6
public interface AuthenticationProvider {

Authentication authenticate(Authentication authentication) throws AuthenticationException;

boolean supports(Class<?> authentication);
}

通过第一个方法我们可以拿到form-data的数据,并且返回一个UserDetails如果登录成功的话,或者返回null如果登录失败。

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = (String) authentication.getPrincipal(); //拿到username
String password = (String) authentication.getCredentials(); //拿到password

UserDetails userDetails = studentService.loadUserByUsername(userName);
if (/*自定义的验证通过*/) {
return new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
}
/*验证不通过*/
return null;
}

第二个方法是告诉spring sec我们这个验证支持哪种验证。

这种验证属于Dao验证。也就是DaoAuthenticationProvider

也就是UsernamePasswordAuthentication
所以在第二个方法中一般会这么写

1
2
3
4
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}

下面就是注册要configure方法中了
只要加入

1
auth.authenticationProvider(new MyAuthenticationProvider());

就好了。。