In the development of .Net, Microsoft’s official framework class can solve most of the problems well. Developers can safely and arrogantly sneak out in one acre. Occasionally, some excellent (zhao) show (chao) open source The library, the focus of each library is basically not repeated; so .Neter just step by step.
Java likes to define various specifications, and each god has its own implementation. Therefore, a concept often has many third-party libraries. Although there is a killer framework like Spring, the Spring family has become abnormal based on the settings of IOC and AOP. Large, in the coding needs to introduce a large number of annotations to weave the logic; although it seems to decouple the components to the greatest extent, but the code is very readable and debuggable, fragmentation is very serious.
But because of this, the Java community has become a breeding ground for design ideas, and there are often some design patterns that are striking.
The concept spread to the next door. The .Net circle, the white inside the circle is often a slap in the face, and a few big cockroaches, regardless of the use, are often used wrongly, or people do not know why.

In general, the .Net framework hides details, is simple and clear, and has a single routine, but often falls into a situation where you know nothing about it; Java&Spring annotations hide details, have many concepts, have no sense of direction, or are at risk of being stunned, but Once the position is broken, the size of the world can be arbitrarily extended to other platforms.
However, the difference between the two is slowly disappearing with the invisible speed of .Net. Especially in recent years, .Net has surpassed Java in the grammar level. Although Java will not be able to erase the face for a while, it has been improving. .
At the time of writing this article, I borrowed the unknown netizen: “C# syntax has reached Java20, user volume has died Java7, eco Java1.4”.

The competition between the two is mainly concentrated in the field of web development.
Currently in this field, Spring Boot has basically become the “official framework” of the Java platform. I think most developers will not care about the implementation details behind it. From this aspect, the development mode of the two platforms has a certain degree. Similar.

Data persistence layer

Why is this section not an ORM?
After all, ORM is now an industry standard. It is hard to imagine that this era requires handwritten SQL and manual operation of JDBC/ADO. If you plan to do this, you will be despised by the younger generation:)


ORM: Hibernate began to emerge more than a decade ago, providing semi-objective HQL and full object-oriented QBC.
There have also been other ORMs such as TopLink.

JPA: JDK5 was introduced, which is the ORM specification proposed by Sun Company to unify many current ORMs (and has also defined the addiction of definitions).
After this specification came out, many ORMs expressed support, but the previous ones had to be maintained, so another branch like Hibernate was built called Hibernate JPA.
Netizenbenjaminlee1said: “The emergence of JPA is only used to standardize the existing ORM technology. It cannot replace the existing ORM framework such as Hibernate. On the contrary, when using JPA development, we will still use these ORM frameworks, but only at this time. The application does not depend on a persistent provider. The application can be run in any JPA environment without modifying the code, truly low-coupling, extensible programming. Similar to JDBC, before the advent of JDBC, our The program is programmed for the database API of the feature, but for now we only need to program the JDBC API so that it can be replaced with other databases without changing the code.”

Spring Data JPA: With JPA, we can not care which ORM to use, but Spring Data JPA goes one step further (to add to the Spring family), automatically generate persistent code for us in a contractual way, of course, it depends on each Road ORM.
Related information:

Simplifying JPA development with Spring Data JPA

Mybatis: As time goes by, the glory that Hibernate has brought has been overwhelmed by bloated ugly configuration files and incomprehensible queries.
Many people began to miss the era of data manipulation, so Mybatis appeared.
Mybatis is not a complete ORM, it only completes the mapping of the database return results to the object, and the access logic is still SQL, written in the Mapper file, the syntax provided by it simplifies the writing of SQL to a certain extent, and finally Mybatis will The SQL logic is mapped to the interface method (specify <mapper namespace=”xxx”> in the Mapper file, where xxx is the mapped DAO interface).
Mapper SQL for common table deletions and changes for each table is both boring and error-prone, so there is a code generation tool such as Mybatis-Generator, which can generate entity classes, basic CRUD Mapper files, and corresponding DAOInterface based on data tables.


: Based on Mybatis, it provides functions such as paging, complex conditional query, etc. The basic CRUD operation does not need to write SQL Mapper extra, as long as the DAO interface inherits the BaseMapper interface.
Of course, for convenience, it also provides its own code generator.


ORM: The mainstream Entity Framework, in addition to the ORM function, it also provides features such as Code first, DB first, and T4 code generation.
Performance and Hibernate a level, but the convenience of use and comprehensive features, not to mention the linq blessing.

Certification & Authorization & Authentication

Authentication is to detect whether a user/request is legal. Authorization is to give the legitimate user the corresponding authority. Authentication is to authenticate whether the user has the right to request a certain resource (authentication and authorization are generally completed at the same time).
Let’s take the web as an example.

C#/Asp.net mvc

Two Filters are provided: IAuthenticationFilter and AuthorizeAttribute, the former for authentication and the latter for authentication.

 1 //IAuthenticationFilter 
 2 public class AdminAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
 3 {
 4     public void OnAuthentication(AuthenticationContext filterContext)
 5     {
 6         IPrincipal user = filterContext.Principal;
 7         if (user == null || !user.Identity.IsAuthenticated)
 8         {
 9             HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
10             if (authCookie != null)
11             {
12                 FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
13                 if (ticket != null && !string.IsNullOrEmpty(ticket.UserData))
14                 {
15                     var userId = Convert.ToInt32(ticket.UserData);
16                     user = EngineContext.Resolve<PFManagerService>().GetManager(userId);
17                     filterContext.Principal = user; //HttpContext.Current.User
18                 }
19             }
20         }
21     }
23     public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
24     {
25         // fail
26     }
27 }

View Code

After the authentication succeeds, user is assigned to filterContext.Principal (line 17). filterContext.Principal receives an IPrincipal interface object. The interface has a bool IsInRole(string role) method for subsequent authentication.

 1 public class AdminAuthorizationFilter : AuthorizeAttribute
 2 {        
 3     public override void OnAuthorization(AuthorizationContext filterContext)
 4     {
 5         //childaction no authorize
 6         if (filterContext.IsChildAction)
 7             return;
 9         if (!filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) && !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
10         {           
11             if (filterContext.HttpContext.User != null && filterContext.HttpContext.User.Identity.IsAuthenticated)
12             {
13                 var controllerName = filterContext.RouteData.Values["controller"].ToString().ToLower();
14                 var actionName = filterContext.RouteData.Values["action"].ToString().ToLower();
15                 //
16                 if (controllerName.ToLower() == "home" && actionName.ToLower() == "index")
17                     this.Roles = string.Empty;
18                 else
19                 {
20                     var roleIds = EngineContext.Resolve<BEModuleService>().GetRoleIdsHasModuleAuthorization(controllerName, actionName, MasonPlatformType.AdminPlatform);
21                     if (roleIds == null)
22                     {
23                         filterContext.Result = new HttpNotFoundResult();
24                         return;
25                     }
26                     //
27                     this.Roles = string.Join(",", roleIds);
28                 }
29             }
30         }
32         base.OnAuthorization(filterContext);
33     }
34 }

View Code

Note that on line 27, we assign all the permissions of the resource to Roles. Then AuthorizeAttribute will loop the Roles, and then call the IsInRole method of the current user (filterContext.Principal above). If one of them returns true, the user has access to the current resource. permission.

Java/Spring Security

Two classes are also provided, a Filter and an Interceptor: AuthenticationProcessingFilter for user authentication and authorization, and AbstractSecurityInterceptor for authentication.
Spring Security also encapsulates several classes based on them, mainly: WebSecurityConfigurerAdapter, FilterInvocationSecurityMetadataSource, AccessDecisionManager, UserDetailsService.
There are also various annotations such as @EnableGlobalMethodSecurity.
(The following code contains a bit of jwt logic)



 1 @Configuration
 2 @EnableWebSecurity
 3 @EnableGlobalMethodSecurity(prePostEnabled = true)
 4 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 5     @Autowired
 6     private JwtAuthenticationEntryPoint unauthorizedHandler;
 8     @Autowired
 9     private UserDetailsService userDetailsService;
11     @Autowired
12     private CustomPostProcessor postProcessor;
14     @Autowired
15     public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
16         authenticationManagerBuilder
17                 .userDetailsService(this.userDetailsService)
18                 .passwordEncoder(passwordEncoder());
19     }
21     @Bean
22     public PasswordEncoder passwordEncoder() {
23         return new BCryptPasswordEncoder();
24     }
26     @Bean
27     public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
28         return new JwtAuthenticationTokenFilter();
29     }
31     @Override
32     protected void configure(HttpSecurity httpSecurity) throws Exception {
33         httpSecurity
34                 // we don't need CSRF because our token is invulnerable
35                 .csrf().disable()
36                 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
37                 // don't create session
38                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
39                 .authorizeRequests()
40                 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
41                 .anyRequest().authenticated().withObjectPostProcessor(postProcessor);
43         // Custom JWT based security filter
44         httpSecurity
45                 .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
46     }
47 }

View Code

The main focus is on two methods configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) and configure(HttpSecurity httpSecurity).
configureAuthentication is mainly used to set UserDetailsService, which needs to be used to load user data; configure is used to set the security level of resources and global security policies.
Line 41 withObjectPostProcessor is used to set the FilterInvocationSecurityMetadataSource and AccessDecisionManager, both of which are used for authentication, as we will see below.

 1 @Component
 2 public class CustomPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {
 3     @Autowired
 4     private CustomFilterSecurityMetadataSource customFilterSecurityMetadataSource;
 6     @Autowired
 7     private CustomAccessDecisionManager customAccessDecisionManager;
 9     @Override
10     public <T extends FilterSecurityInterceptor> T postProcess(T fsi) {
11         fsi.setSecurityMetadataSource(customFilterSecurityMetadataSource);
12         fsi.setAccessDecisionManager(customAccessDecisionManager); 
13         return fsi;
14     }
15 }

View Code

UserDetailService (obtained from the database here):


 1 @Service
 2 public class JwtUserDetailsServiceImpl implements UserDetailsService {
 4     @Autowired
 5     private UserRepository userRepository;
 7     @Override
 8     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 9         User user = userRepository.findByUsername(username);
11         if (user == null) {
12             throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
13         } else {
14             return JwtUserFactory.create(user);
15         }
16     }
17 }

View Code

Note that the parameter name username required by loadUserByUsername is a good contract, defined in UsernamePasswordAuthenticationFilter, and value is obtained from HttpServletRequest.

FilterInvocationSecurityMetadataSource (used to get the permissions required to request the resource currently):


 1 /**
  2 * Path interception processing class
  3 * <p>
  4 * If the path belongs to the allowed access list, do not intercept and release the access;
  5 * <p>
  6 * Otherwise, get the path access required role and return; if the path is not found, the access is denied.
  7 */
 8 @Component
 9 public class CustomFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
10     @Autowired
11     private ApiRepository apiRepository;
13     @Override
14     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
15         FilterInvocation fi = (FilterInvocation) object; 
17         List<ConfigAttribute> configAttributes = getMatcherConfigAttribute(fi.getRequestUrl(), fi.getRequest().getMethod()); // 获得访问当前路径所需要的角色
19         return configAttributes.size() > 0 ? configAttributes : deniedRequest();
20     }
22     @Override
23     public Collection<ConfigAttribute> getAllConfigAttributes() {
24         return null;
25     }
27     @Override
28     public boolean supports(Class<?> clazz) {
29         return FilterInvocation.class.isAssignableFrom(clazz);
30     }
32 /**
33 * Get the current path and request mode to get the required role
34 *
35 * @param url current path
36 * @return Required role collection
37 */
38     private List<ConfigAttribute> getMatcherConfigAttribute(String url, String method) {
39         Set<Authority> authorities = new HashSet<>();
40         // 1.According to the beginning of the url, go to the database fuzzy query corresponding api
42         String prefix = url.substring(0, url.lastIndexOf("/"));
44         prefix = StringUtil.isEmpty(prefix) ? url : prefix + "%";
46         List<Api> apis = apiRepository.findByUriLikeAndMethod(prefix, method);
48         // 2.Find the exact match api, if not, compare the result of the match pathMatcher
49         apis.forEach(api -> {
50             String pattern = api.getUri();
52             if (new AntPathMatcher().match(pattern, url)) {
53                 List<Resource> resources = api.getResources();
55                 resources.forEach(resource -> {
56                     authorities.addAll(resource.getAuthorities());
57                 });
58             }
59         });
61         return authorities.stream().map(authority -> new SecurityConfig(authority.getId().toString())).collect(Collectors.toList());
62     }
64     /**
65      * @return Default access denied configuration
66      */
67     private List<ConfigAttribute> deniedRequest() {
68         return Collections.singletonList(new SecurityConfig("ROLE_DENIED"));
69     }
70 }

View Code



1 /**
  2 * permission decision processing class
  3 *
  4 * Determine the role of the user, if it is empty, then refuse access;
  5 *
  6 * Determine whether any of the user's roles are included in the set of roles allowed by the access path;
  7 *
  8 * If yes, let go; otherwise refuse access;
  9  */
10 @Component
11 public class CustomAccessDecisionManager implements AccessDecisionManager {
12     @Override
13     public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
14         if (authentication == null) {
15             throw new AccessDeniedException("permission denied");
16         }
18         //The set of roles owned by the current user
19         List<String> roleCodes = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
21         //The set of roles required to access the path
22         List<String> configRoleCodes = configAttributes.stream().map(ConfigAttribute::getAttribute).collect(Collectors.toList());
23         for (String roleCode : roleCodes) {
24             if (configRoleCodes.contains(roleCode)) {
25                 return;
26             }
27         }
29         throw new AccessDeniedException("permission denied");
30     }
32     @Override
33     public boolean supports(ConfigAttribute attribute) {
34         return true;
35     }
37     @Override
38     public boolean supports(Class<?> clazz) {
39         return true;
40     }
41 }


Lines 19 and 22 above are the permissions owned by the user obtained by the UserDetailService and the permissions required by the FilterInvocationSecurityMetadataSource to access the resources. If the two are compared, the user has the right to access the resource.
Specifically, the entire process of authentication is: when accessing resources, it will be intercepted by the AbstractSecurityInterceptor interceptor, which will call the FilterInvocationSecurityMetadataSource method to get all the permissions required to intercept the url, and then call the authorization manager AccessDecisionManager, the authorization manager Will get the user’s permission information through spring’s global cache SecurityContextHolder, and also get the intercepted url and all the permissions required to intercept the url, and then according to the assigned strategy (there are: one vote, one vote negative, the minority obeys the majority Etc.) If the permission is sufficient, it will return. If the permission is not enough, the error will be reported and the insufficient permission page will be called.

Off-topic, login authentication can be considered not part of the authentication authorization, but the process of issuing the identity token to the client. After the client takes the identity token to request the resource, it enters the above authentication and authorization link.
However, the authentication methods involved in Spring Secuity can simplify the writing of login authentication code:

1 final Authentication authentication = authenticationManager.authenticate(
2 new UsernamePasswordAuthenticationToken(username, password)
5 SecurityContextHolder.getContext().setAuthentication(authentication);

The authenticationManager is provided by the framework. The framework provides the appropriate AuthenticationManager instance according to the configureAuthentication mentioned above. When the authentication fails, an exception is thrown. Otherwise, the Authenticatio token is returned and the token is set for the user-related SecurityContext.
It should be noted that the SecurityContext is stored in the ThreadLocal, and each time the permission is authenticated, the permissions of the corresponding Authentication in the SecurityContext are obtained from the ThreadLocal, and the different requests are different threads, why each time Can I get the SecurityContext corresponding to the current user from ThreadLocal?
In the web application, this is implemented by SecurityContextPersistentFilter. By default, it will get the SecurityContext from the session at the beginning of each request, and then set it to the SecurityContextHolder. After the request ends, the SecurityContext will be saved in the session. And cleared in the SecurityContextHolder.
When the user accesses the system for the first time,
the user does not have a SecurityContext
. After the login is successful, each subsequent request can obtain the SecurityContext from the session and assign it to the SecurityContextHolder, since the SecurityContextHolder already has the authenticated The Authentication object is gone, so login authentication is no longer required for the next visit.

The jwt mentioned above is a cookie/session black.
Its mechanism is token authentication at the http request header.
We can use it to authenticate and authorize after the session expires without requiring the user to log in again.

 1 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
 3     private final Log logger = LogFactory.getLog(this.getClass());
 5     @Autowired
 6     private UserDetailsService userDetailsService;
 8     @Autowired
 9     private JwtTokenUtil jwtTokenUtil;
11     @Value("${jwt.header}")
12     private String tokenHeader;
14     @Override
15     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
16         final String requestHeader = request.getHeader(this.tokenHeader);
18         String username = null;
19         String authToken = null;
20         if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
21             authToken = requestHeader.substring(7);
22             try {
23                 username = jwtTokenUtil.getUsernameFromToken(authToken);
24             } catch (IllegalArgumentException e) {
25                 logger.error("an error occured during getting username from token", e);
26             } catch (Exception e1) {
27                 logger.error(e1.getMessage());
28             }
29         }
31         if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
33             // It is not compelling necessary to load the use details from the database. You could also store the information
34             // in the token and read it from it. It's up to you ;)
35             UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
37             // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
38             // the database compellingly. Again it's up to you ;)
39             if (jwtTokenUtil.validateToken(authToken, userDetails)) {
40                 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
41                 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
42                 logger.info("authenticated user " + username + ", setting security context");
43                 SecurityContextHolder.getContext().setAuthentication(authentication);
44             }
45         }
47         chain.doFilter(request, response);
48     }
49 }


Of course, you can also implement jwt without using Spring Security, and you need to implement the authentication and authorization process yourself.

In Spring Boot 1.5, we can add custom interceptors, message converters, etc. by rewriting the WebMvcConfigurerAdapter method; after Spring Boot 2.0, the class is marked @Deprecated.
The method is changed to implement the WebMvcConfigurer interface.
In Java, the Interceptor differs from the Filter in that the former is closer to the AOP concept, while the latter has only pre-execution.


: Asp.net mvc is relatively clear and controllable; Spring Security hides the logical order, involves more classes, and the key steps are scattered everywhere, the level is unclear, and it is easy for newcomers to be confused.

There are other Java authentication frameworks such as Shiro, which are also very popular.

Non-blocking programming

In the field of web development, the traditional way of implementing asynchronous is more complicated. For example, NIO in Java needs to understand the concepts of channel, selector, and buffer, or use a network framework such as netty.
c/c++ For asynchronous/non-blocking programming, you need to understand concepts such as select, poll, and epoll, and the development and maintenance threshold is high.
Moreover, the development of this part has nothing to do with the business, so the necessity of encapsulating the underlying mechanism and launching a development framework is obvious.
Conceptually, .Net is called Asynchronous programming, which Java calls Reactive Programming.

.Net/Asynchronous programming

Starting with .Net4.5 (C# 5.0, 2012), the async/await keyword was introduced to make asynchronous programming as clear and smooth as synchronous processing, and to introduce asynchronous support for mainstream databases in a short time. Component.
From receiving requests to data operations, developers can easily migrate traditional synchronization code to asynchronous mode.
In the following years, such as Python (3.5), Nodejs (7.6) and so on, have become the de facto grammar standard.

Java/Reactive Programming

We have to start with


, Stream itself and responsive programming does not matter, but later Reactive Streams inherit some of its concepts to some extent.
Java 8 introduces Stream, which is convenient for aggregate operations of collections. It also supports lambda expressions as operation parameters, which can be thought of as Iterator.
A similar syntax is also available in C#, except that C# provides a non-intrusive way, and the collection itself supports it, not to mention the confusion of the concept of Stream.
Related information:

Detailed Streams API in Java 8

Stream’s mapping operations are map and flatmap, similar to the difference between Select and SelectMany in C#.

Reactive Streams


Responsive streaming began in 2013 as an initiative to provide asynchronous flow handling standards for non-blocking backpressure.

In 2015, a specification and Java API for handling responsive flows was published.
The responsive stream in the Java API consists of four interfaces: Publisher<T>, Subscriber<T>, Subscription, and Processor<T,R>.

JDK 9 provides a responsive stream-compatible API in the java.util.concurrent package, which is in the java.base module.
The API consists of two classes: Flow and SubmissionPublisher<T>.
The Flow class encapsulates the Responsive Streaming Java API.
The four interfaces specified by the Responsive Streaming Java API are included as nested static interfaces in the Flow class: Flow.Processor<T,R>, Flow.Publisher<T>, Flow.Subscriber<T>, and Flow.Subscription.


is an implementation library for Reactive Streams.
The deaf person believes that the scene for Reactive Streams is the enumeration processing of borderless data. Without boundaries, the data/requirements will be produced continuously, and the loop rules (such as the number of loops) cannot be established beforehand. On the other hand, it provides Processing rules for a single processing (such as how many data/requirements are processed each time).
Related information:

talk about the backpressure of reactive streams


Spring 5.0 began to provide responsive Web programming support, the framework for Spring WebFlux, different from the traditional Spring MVC synchronization mode.
Spring WebFlux is based on Reactor, which has a syntax similar to JS Promise, and has some flexible and useful features such as delay processing to return.
Specific usage can be seen:

(5) Spring WebFlux quickly get started – responsive Spring’s Dao

As mentioned in the article, Spring Data provides responsive data access support for MongoDB, Redis, Apache Cassandra, and CouchDB databases, meaning that projects using other databases cannot truly respond asynchronously (the most critical IO). The link is still thread synchronization).

After Java 7 introduced asynchronous I/O libraries, and Servlet 3.1 added support for asynchronous I/O, Servlet containers such as Tomcat also began to support asynchronous I/O, and Spring WebMVC also added support for Reactor libraries. Asynchronous mode is already supported in Spring MVC 3.2.
As for why Spring has launched a WebFlux, it is not known, it should be to create a more pure and comprehensive framework.

my father gave birth to my brother called Spring MVC WebFlux the Spring



: Non-blocking programming, Java advancement is slow, the current level is not comparable with .Net a few years ago, syntax, .Net async/await is more concise than Promise syntax, Spring WebFlux in request response processing There are some bright spots on it.

Postscript: C#8.0 introduced Async Streams, which should be borrowed from Reactive Streams.

chat await foreach C # 8.0 in


A few months ago (September 25th, US local time), Oracle officially announced the official release of Java 11 (18.9 LTS).
Java’s current release strategy is a half-year version, with a long-term support release every three years. Java 11 is the first long-term support release since Java 8.
Visually testing many of the features of Java 8 are referenced to C#, such as asynchronous programming, Lambda, Stream, var, etc. This is a good phenomenon, learning from each other can improve.

The .Net MVC template engine defaults to


, which is specific and passionate and relies on backend code.
The Java platform is commonly used, such as


, which is independent of any framework, can be regarded as a complex version of string.format, used in mvc is string.format (v, m), the output is v template binding m The html after the data; and Spring Boot’s own


, which uses the tag attribute as the syntax, the template page can be directly rendered by the browser, so that the front end and the back end can be developed in parallel, stealing that this is both convenient and running. Efficiency and SEO’s best front and rear separation development tools.

Beginning with Java 8, you can define static methods and default methods in Interface.
In the interface, the default method is added to add new functions to the classes of thousands of Java class libraries, and it is not necessary to redesign these classes (similar to C#’s extension method, but the flexibility is low, coupling High degree).

Java8’s Optional is somewhat similar to .NET’s xxxx?, which is a simplification of whether it is empty or not.

The Java ThreadLocal is similar to the .NET ThreadStaticAttribute, which provides local variables [copy] within a thread that act during the life of the thread.



: Java 7 was introduced, so that we can split the task into sub-tasks and execute them in parallel [and return the results and return].

Static introduction: import static.
Import static methods.

Initialize the object using the anonymous inner class:

ArrayList<Student> stuList = new ArrayList<Student> () {
        For ( int i = 0; i < 100; i++ ) {
            Add( new Student("student" + i, random.nextInt(50) + 50 ));


Java: Double Bracket Initialization / Anonymous Internal Class Initialization

Java 9 started to support Http/2. The characteristics of Http/2 and its improvements compared to the 1.0 and 1.1 versions can be Baidu, and the efficiency is greatly improved.


3.0 introduced


Instead of using the XML files, we can use plain Java classes to annotate the configurations by using the @Configuration annotation. If you annotate a class with @Configuration annotation, it indicates that the class is used for defining the beans using the @Bean annotation This is very much similar to the <bean/> element in the spring XML configurations. Of course, xml configuration and annotation configuration can be mixed.
To reuse the configuration classes defined in it, we can use the


annotation, which acts like importing multiple XML configuration files into a single file.

The postprocessor


in Spring is used to add some logic processing before and after bean instantiation, configuration, and other initialization methods in the Spring container.
There is also an

ObjectPostProcessor in

Spring Security
, which can be used to modify or replace the object instance created by Java configuration. It can be used in scenarios where you can’t set values ​​in advance, such as setting different values ​​according to different conditions.

@Value(“#{}”) and @Value(“${}”)

: The former is used to assign a value to the bean field, and the latter is used to assign the property value defined in the properties file.

Servlet 3.0 starts, @WebServlet, @WebFilter, and @WebListener can be enabled by using @ServletComponentScan, not configured in web.xml.
This is not related to Spring, but the Servlet container feature.

@Autowired is automatically assembled by type.
If there is more than one UserDao type bean in the Spring context, a BeanCreationException will be thrown.
We can use @Qualifier to specify the type name to be assembled to solve this problem.


Other references:

Analysis of Spring Security Principle Based on Annotation


Orignal link: