欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Boot?2.x升3.x的那些事

 更新時(shí)間:2024年01月26日 16:30:48   作者:arbiterlk  
最近項(xiàng)目需求,準(zhǔn)備從Spring Boot 2.x升級(jí)到3.x,升級(jí)后發(fā)現(xiàn)編譯器報(bào)了一堆錯(cuò)誤,本文主要介紹了Spring?Boot?2.x升3.x的那些事,具有一定的參考價(jià)值,感興趣的可以了解一下

序言

手頭上有個(gè)項(xiàng)目,準(zhǔn)備從Spring Boot 2.x升級(jí)到3.x,升級(jí)后發(fā)現(xiàn)編譯器報(bào)了一堆錯(cuò)誤。一般來(lái)說(shuō)大版本升級(jí),肯定會(huì)有諸多問(wèn)題,對(duì)于程序開(kāi)發(fā)來(lái)說(shuō)能不升就不升。但是對(duì)于系統(tǒng)架構(gòu)來(lái)說(shuō),能用最新的肯定是用最新的,實(shí)在不行再降回去嘛??墒悄兀恢朗前l(fā)布沒(méi)多久,還是我搜索技巧的問(wèn)題,很多問(wèn)題在網(wǎng)上找不到答案。沒(méi)辦法,還是得自己研究,所以呢這次我們就一起來(lái)研究一下Spring Boot 3.x究竟有什么改變。

一、關(guān)于Spring Session

一般來(lái)說(shuō),如果一個(gè)Spring Boot 2.x項(xiàng)目一開(kāi)始只需要單實(shí)例部署,用不上redis共享會(huì)話的話,會(huì)在application.properties里加上這個(gè)參數(shù)。

spring.session.store-type=none

當(dāng)需要改為多實(shí)例部署,需要redis共享會(huì)話的時(shí)候,只需要改為這樣就行了。

spring.session.store-type=redis

但是在Spring Boot 3.x項(xiàng)目里,這個(gè)參數(shù)就不復(fù)存在了。查了Spring Session的官方文檔也沒(méi)有收獲。于是去翻Spring Boot的官方文檔,在2.x的參考文檔中有這么一條提示“You can disable Spring Session by setting the store-type to none.”。而在3.x的文檔中,這個(gè)提示被刪掉了。好家伙,原來(lái)store-type=none是直接禁用整個(gè)Spring Session,而不是Api文檔中所說(shuō)的"No session data-store."

You can disable Spring Session by setting the store-type to none.

那么解決辦法就很簡(jiǎn)單了,單實(shí)例部署,不需要用redis的時(shí)候,刪掉pom.xml里org.springframework.session的依賴(lài)就好。需要redis共享會(huì)話的時(shí)候就要把依賴(lài)加回去了,就是沒(méi)有原來(lái)修改配置文件來(lái)得方便而已。

二、關(guān)于redis

在application.properties里關(guān)于redis的配置也有所變化。如果你是這么配置redis的:

spring.redis.host=127.0.0.1
spring.redis.port=6379

這時(shí)編譯器就會(huì)警告你:“Property ‘spring.redis.host’ is Deprecated: Use ‘spring.data.redis.host’ instead.”、“Property ‘spring.redis.password’ is Deprecated: Use ‘spring.data.redis.password’ instead.”按照警告所說(shuō)的,把“spring.redis”替換成“spring.data.redis”即可。

spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379

三、關(guān)于servlet

由于tomcat 10包名的更換,如果你的程序是這么寫(xiě)的:

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
...

那么編譯器就會(huì)報(bào)"The import javax.servlet cannot be resolved"錯(cuò)誤。原因是包名從javax.servlet 調(diào)整為了jakarta.servlet 。解決辦法很簡(jiǎn)單,把javax.servlet 替換為 jakarta.servlet 即可。

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
...

四、關(guān)于thymeleaf模板

當(dāng)你使用片段表達(dá)式(fragment expression)而沒(méi)有使用“~{}”時(shí),會(huì)獲得運(yùn)行警告。例如你模板里這么寫(xiě):

<footer th:replace="footer::copy"></footer>

則會(huì)得到這樣的運(yùn)行警告:“Deprecated unwrapped fragment expression “footer::copy” found in template index, line 7, col 9. Please use the complete syntax of fragment expressions instead (“~{footer::copy}”). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.”
原因是在thymeleaf 3.1中,未封裝的片段表達(dá)式不再被推薦。解決方法也很簡(jiǎn)單,按照警告所說(shuō)的改為完整版的片段表達(dá)式,即加上“~{}”即可。

<footer th:replace="~{footer::copy}"></footer>

五、關(guān)于Spring Security

重點(diǎn)來(lái)了,隨著Spring Boot升級(jí)到3.x,Spring Security也升級(jí)到了6.x。話不多說(shuō),先來(lái)看看代碼,在6.x之前,如果你想要實(shí)現(xiàn)動(dòng)態(tài)權(quán)限,你的代碼可能會(huì)是這樣的:

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	MyUserService myUserService;
    @Autowired
    MyUrlFilter myUrlFilter;
    @Autowired
    MyDecisionManager myDecisionManager;
    
	@Bean
	protected PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	protected SessionRegistry sessionRegistry() {
		return new SessionRegistryImpl();
	}
    
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(myUserService);
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**");
    }
    
    @Override
    public void configure(HttpSecurity http) throws Exception{
        http.apply(new UrlAuthorizationConfigurer<>(http.getSharedObject(ApplicationContext.class)))
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                    o.setAccessDecisionManager(myDecisionManager);
                    o.setSecurityMetadataSource(myUrlFilter);
                    return o;
                }
            })
            .and().formLogin().loginProcessingUrl("/login/process").loginPage("/login/page")
            .and().logout().logoutUrl("/logout/page")
            .and().sessionManagement().maximumSessions(-1).expiredUrl("/login/page").sessionRegistry(sessionRegistry())
            .and().and().csrf().disable();
    }
}

如果要把上面的代碼改成可以在Spring Security 6.x里運(yùn)行,那么你需要這么寫(xiě):

@Configuration
public class MySecurityConfig {
	
    @Autowired
    MyAuthorizationManager myAuthorizationManager;
    
	@Bean
	protected PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	protected SessionRegistry sessionRegistry() {
		return new SessionRegistryImpl();
	}

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
    	return web -> web.ignoring().requestMatchers("/static/**");
    }
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        http.authorizeHttpRequests(authz -> authz.anyRequest().access(myAuthorizationManager))
            .formLogin(login -> login.loginProcessingUrl("/login/process").loginPage("/login/page").permitAll())
            .logout(logout -> logout.logoutUrl("/logout/page").permitAll())
            .sessionManagement(session -> session.maximumSessions(-1).expiredUrl("/login/page").sessionRegistry(sessionRegistry()))
            .csrf(csrf -> csrf.disable());
        return http.build();
    }
}

我們來(lái)逐個(gè)講解一下。

1.關(guān)于WebSecurityConfigurerAdapter

在Spring Security 6.x之前,我們通常是寫(xiě)一個(gè)配置類(lèi),繼承WebSecurityConfigurerAdapter 然后重寫(xiě)(@Override)對(duì)應(yīng)的方法來(lái)完成Security的配置的。而在Spring Security 6.x里WebSecurityConfigurerAdapter 已經(jīng)被棄用了,現(xiàn)在推薦使用的是基于組件的編碼方式,只要在配置類(lèi)里注冊(cè)對(duì)應(yīng)的組件(@Bean)即可。另外,使用組件配置時(shí)and()方法已經(jīng)不再推薦使用,官方建議使用lambda DSL。

2.關(guān)于UserDetailsService

按上面所說(shuō)的,下面這段代碼。

	@Autowired
	MyUserService myUserService;
	
	@Bean
	protected PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(myUserService);
    }

理論上是要改成這樣的。

 	@Autowired
	MyUserService myUserService;
	
	@Bean
	protected PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(myUserService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

但實(shí)際上只要你的用戶服務(wù)(MyUserService)實(shí)現(xiàn)了UserDetailsService接口,并且注冊(cè)到了Spring容器中(加了@Service或者@Component注解),Spring Security 6.x就會(huì)自動(dòng)綁定用戶服務(wù),只需注冊(cè)密碼加密組件即可。所以上面的代碼直接改成下面的就可以了。

	@Bean
	protected PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

(由于篇幅關(guān)系,這里就不貼MyUserService的代碼了,自己按實(shí)際情況實(shí)現(xiàn)對(duì)應(yīng)接口功能就好)

3.關(guān)于WebSecurity

WebSecurity可以控制哪些地址不進(jìn)入Security過(guò)濾器鏈。原來(lái)的代碼是這么寫(xiě)的。

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**");
    }

現(xiàn)在,除了需要改為基于組件的寫(xiě)法外,antMatchers()方法也改成了requestMatchers()方法

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
    	return web -> web.ignoring().requestMatchers("/static/**");
    }

4.關(guān)于HttpSecurity

原來(lái)在HttpSecurity中實(shí)現(xiàn)動(dòng)態(tài)權(quán)限,是先要寫(xiě)一個(gè)訪問(wèn)地址過(guò)濾器(MyUrlFilter),來(lái)判斷當(dāng)前訪問(wèn)地址需要什么權(quán)限,然后將所需權(quán)限送給決策管理器(MyDecisionManager)進(jìn)行判斷是否有權(quán)限。

    @Autowired
    MyUrlFilter myUrlFilter;
    @Autowired
    MyDecisionManager myDecisionManager;
    
	@Bean
	protected SessionRegistry sessionRegistry() {
		return new SessionRegistryImpl();
	}
	
    @Override
    public void configure(HttpSecurity http) throws Exception{
        http.apply(new UrlAuthorizationConfigurer<>(http.getSharedObject(ApplicationContext.class)))
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                    o.setAccessDecisionManager(myDecisionManager);
                    o.setSecurityMetadataSource(myUrlFilter);
                    return o;
                }
            })
            .and().formLogin().loginProcessingUrl("/login/process").loginPage("/login/page")
            .and().logout().logoutUrl("/logout/page")
            .and().sessionManagement().maximumSessions(-1).expiredUrl("/login/page").sessionRegistry(sessionRegistry())
            .and().and().csrf().disable();
    }

MyUrlFilter.java

@Component
public class MyUrlFilter implements FilterInvocationSecurityMetadataSource {
		
    @Autowired
    AccessPermitService accessPermitService;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        //若當(dāng)前頁(yè)面是登錄頁(yè)面,則直接放行,否則會(huì)進(jìn)入死循環(huán),最終報(bào)重定向次數(shù)過(guò)多的錯(cuò)誤
        if(antPathMatcher.match("/login/**", requestUrl)) {
        	//權(quán)限數(shù)量為0時(shí),不會(huì)調(diào)用AccessDecisionManager.decide()方法,無(wú)需登錄,直接放行
            return SecurityConfig.createList(new String[0]);
        }
        
        //基于數(shù)據(jù)庫(kù)的動(dòng)態(tài)權(quán)限,獲取整個(gè)系統(tǒng)的訪問(wèn)路徑權(quán)限配置(建議緩存起來(lái))
        List<AccessPermit> accessPermits = accessPermitService.list();
		
        //遍歷訪問(wèn)路徑權(quán)限配置列表,判斷當(dāng)前請(qǐng)求url和哪個(gè)訪問(wèn)路徑配置匹配
        for (AccessPermit accessPermit : accessPermits) {
            //如果匹配上了,獲取這個(gè)訪問(wèn)路徑的角色
            if(antPathMatcher.match(accessPermit.getPattern(), requestUrl)){
                String roles = accessPermit.getRoles();
                //如果沒(méi)有設(shè)置角色,則視為需要登錄但不需要對(duì)應(yīng)權(quán)限,設(shè)置一個(gè)默認(rèn)權(quán)限給該訪問(wèn)地址;否則根據(jù)逗號(hào)切分,返回對(duì)應(yīng)的權(quán)限
                if(roles.equals("")) {
                	return SecurityConfig.createList("login_required");
                }
                else{
                    return SecurityConfig.createList(roles.split(","));
                }
            }
        }
        //沒(méi)有匹配上,則視為需要登錄但不需要對(duì)應(yīng)權(quán)限,設(shè)置一個(gè)默認(rèn)權(quán)限給該訪問(wèn)地址
        return SecurityConfig.createList("login_required");
    }
    
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

MyDecisionManager.java

@Component
public class MyDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (ConfigAttribute configAttribute : configAttributes) {
        	//需要登錄但不需要權(quán)限時(shí),myUrlFilter過(guò)濾器默認(rèn)返回一個(gè)默認(rèn)權(quán)限,需要進(jìn)行特殊處理
        	if(configAttribute.getAttribute().equals("login_required")) {
        		//如果沒(méi)有登錄,則返回登錄頁(yè)面;否則用戶已登錄,直接放行
	        	if (authentication instanceof AnonymousAuthenticationToken) {
	        		throw new AccessDeniedException("沒(méi)有登錄,請(qǐng)登錄!");
	        	}
	        	else {
	        		return;
	        	}
        	}
        	//需要權(quán)限的情況
        	for (GrantedAuthority authority : authorities) {
        		//判斷當(dāng)前用戶是否有對(duì)應(yīng)權(quán)限,有則放行
                if(configAttribute.getAttribute().equals(authority.getAuthority())){
                    return;
                }
            }
        }
        //沒(méi)有權(quán)限則不放行
        throw new AccessDeniedException("權(quán)限不足,無(wú)法訪問(wèn)!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

LogoutController.java

@Controller
@RequestMapping("/logout")
public class LogoutController {

	@Autowired
	SessionRegistry sessionRegistry;
	
	@RequestMapping("/page")
	public String page(HttpSession session) {
		SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
		sessionInformation.expireNow();
		return "redirect:login?logout";
	}
}

現(xiàn)在改成這樣:

    @Autowired
    MyAuthorizationManager myAuthorizationManager;
	
	@Bean
	protected SessionRegistry sessionRegistry() {
		return new SessionRegistryImpl();
	}

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        http.authorizeHttpRequests(authz -> authz.anyRequest().access(myAuthorizationManager))
            .formLogin(login -> login.loginProcessingUrl("/login/process").loginPage("/login/page").permitAll())
            .logout(logout -> logout.logoutUrl("/logout/page").permitAll())
            .sessionManagement(session -> session.maximumSessions(-1).expiredUrl("/login/page").sessionRegistry(sessionRegistry()))
            .csrf(csrf -> csrf.disable());
        return http.build();
    }

MyAuthorizationManager.java

@Component
public class MyAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    @Autowired
    AccessPermitService accessPermitService;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

	@Override
	public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {

        String requestUrl = context.getRequest().getServletPath();
		Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
		
        //基于數(shù)據(jù)庫(kù)的動(dòng)態(tài)權(quán)限,獲取整個(gè)系統(tǒng)的訪問(wèn)路徑權(quán)限配置(建議緩存起來(lái))
        List<AccessPermit> accessPermits = accessPermitService.list();
		
        //遍歷訪問(wèn)路徑權(quán)限配置列表,判斷當(dāng)前請(qǐng)求url和哪個(gè)訪問(wèn)路徑配置匹配
        for (AccessPermit accessPermit : accessPermits) {
            //如果匹配上了,獲取這個(gè)訪問(wèn)路徑的角色
            if(antPathMatcher.match(accessPermit.getPattern(), requestUrl)){
                String roles = accessPermit.getRoles();
                //如果沒(méi)有設(shè)置角色,則視為需要登錄但不需要對(duì)應(yīng)權(quán)限;否則根據(jù)逗號(hào)切分,返回對(duì)應(yīng)的權(quán)限
                if(roles.equals("")) {
                	break;
                }
                else{
                    for(String role : roles.split(",")) {
                    	for (GrantedAuthority authority : authorities) {
                    		//判斷當(dāng)前用戶是否有對(duì)應(yīng)權(quán)限,有則放行
                            if(role.equals(authority.getAuthority())){
                            	return new AuthorizationDecision(true);
                            }
                        }
                    }
                    return new AuthorizationDecision(false);
                }
            }
        }

    	if (authentication.get() instanceof AnonymousAuthenticationToken) {
            return new AuthorizationDecision(false);
    	}
    	else return new AuthorizationDecision(true);
	}
}

這里改動(dòng)挺多的,一是使用lambda DSL的格式去寫(xiě)相關(guān)代碼。二是現(xiàn)在只需要使用authorizeHttpRequests()方法配置一個(gè)自定義的授權(quán)管理器(MyAuthorizationManager)就可以了??梢岳斫鉃?strong>這個(gè)授權(quán)管理器(MyAuthorizationManager)取代了原來(lái)的訪問(wèn)地址過(guò)濾器(MyUrlFilter)和決策管理器(MyDecisionManager)。三是現(xiàn)在的過(guò)濾器鏈?zhǔn)窍冉?jīng)過(guò)HttpSecurity 的過(guò)濾器再到授權(quán)管理器(MyAuthorizationManager)的,之前給登錄頁(yè)面放行的相關(guān)邏輯也不用自己實(shí)現(xiàn)了,但是formLogin和logout都要設(shè)置.permitAll()。四是logoutUrl(“/logout/page”)無(wú)需自行實(shí)現(xiàn)了,這個(gè)頁(yè)面與loginProcessingUrl(“/login/process”)一樣,已經(jīng)交由Security 托管了,自行實(shí)現(xiàn)也不會(huì)執(zhí)行。五是現(xiàn)在sessionRegistry會(huì)自動(dòng)銷(xiāo)毀登出的會(huì)話了,也無(wú)需自行實(shí)現(xiàn)了。
(由于篇幅關(guān)系,這里就不貼AccessPermitService 的相關(guān)代碼了,大家按自己實(shí)際情況去實(shí)現(xiàn)即可)

總結(jié)

從Spring Boot 2.x升級(jí)到3.x肯定還有很多改動(dòng),是我這里沒(méi)列舉的,雖然改動(dòng)挺多的,但還是建議能用最新的版本就用最新的版本。特別是Spring Security 升級(jí)到了6.x之后,代碼邏輯清晰了許多,不會(huì)像之前那樣繞到云里霧里,僅這點(diǎn)就值得了。

參考資料

Spring Session #2.7.15-SNAPSHOT

Spring Session #3.0.10-SNAPSHOT

Thymeleaf #3.0

Thymeleaf #3.1

Spring Security without the WebSecurityConfigurerAdapter

到此這篇關(guān)于Spring Boot 2.x升3.x的那些事的文章就介紹到這了,更多相關(guān)Spring Boot 2.x升3.x的那些事內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SptingBoot過(guò)濾器Filter的使用詳解

    SptingBoot過(guò)濾器Filter的使用詳解

    這篇文章主要介紹了SptingBoot過(guò)濾器Filter的使用詳解,Filter屬于Servlet規(guī)范,并不是Spring獨(dú)有的,Filter主要用于攔截請(qǐng)求,做一些業(yè)務(wù)邏輯操作,然后可以決定請(qǐng)求是否可以繼續(xù)往下分發(fā),落到其他的Filter或者對(duì)應(yīng)的Servlet,需要的朋友可以參考下
    2023-09-09
  • Struts2 使用OGNL遍歷map方法詳解

    Struts2 使用OGNL遍歷map方法詳解

    這篇文章主要介紹了Struts2 使用OGNL遍歷map方法詳解,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • 詳解springboot + profile(不同環(huán)境讀取不同配置)

    詳解springboot + profile(不同環(huán)境讀取不同配置)

    本篇文章主要介紹了springboot + profile(不同環(huán)境讀取不同配置),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Java8?函數(shù)式編程stream流使用詳解

    Java8?函數(shù)式編程stream流使用詳解

    這篇文章主要介紹了Java8?函數(shù)式編程stream流使用詳解的相關(guān)資料,需要的朋友可以參考下
    2023-07-07
  • Java中StringBuffer和StringBuilder區(qū)別

    Java中StringBuffer和StringBuilder區(qū)別

    這篇文章主要介紹了Java中StringBuffer和StringBuilder區(qū)別,本文只介紹了它們之間的核心區(qū)別,需要的朋友可以參考下
    2015-06-06
  • SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳功能

    SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳功能

    這篇文章主要介紹了SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Java selenium截圖操作的實(shí)現(xiàn)

    Java selenium截圖操作的實(shí)現(xiàn)

    這篇文章主要介紹了Java selenium截圖操作的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Log4j.properties配置及其使用

    Log4j.properties配置及其使用

    本文主要介紹了Log4j.properties配置及其使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java替換(新增)JSON串里面的某個(gè)節(jié)點(diǎn)操作

    Java替換(新增)JSON串里面的某個(gè)節(jié)點(diǎn)操作

    這篇文章主要介紹了Java替換(新增)JSON串里面的某個(gè)節(jié)點(diǎn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • SpringMVC請(qǐng)求、響應(yīng)和攔截器的使用實(shí)例詳解

    SpringMVC請(qǐng)求、響應(yīng)和攔截器的使用實(shí)例詳解

    攔截器(Interceptor) 它是一個(gè)Spring組件,并由Spring容器管理,并不依賴(lài)Tomcat等容器,是可以單獨(dú)使用的,這篇文章給大家介紹SpringMVC請(qǐng)求、響應(yīng)和攔截器的使用,感興趣的朋友一起看看吧
    2024-03-03

最新評(píng)論