In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
How is the transformation process of SpringBoot micro services? aiming at this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a simpler and easier way.
SSO is a project that has existed in the company for several years. The back end uses SpringMVC and MyBatis, the database uses MySQL, and the front display uses Freemark. This year, we made a revolutionary improvement to the project, transforming it into a SpringBoot architecture, separating the front and rear ends, and using the Vue framework at the front end.
1. Using SpringBoot architecture for transformation 1.1 Why use SpringBoot
Compared with traditional Spring,SpringBoot, it has the following advantages:
Deployment is simple, SpringBoot has a built-in Tomcat container, which can compile the program directly into a jar and run it through java-jar.
Coding is simple, SpringBoot only needs to add a starter-web dependency to the pom file to help developers quickly start a web container, which is very convenient.
The configuration is simple, and SpringBoot can replace the very complex xml mode of Spring through simple annotations. If I want to hand over a normal class to Spring management, I just need to add @ Configuration and @ Bean annotations.
Monitoring is simple, we can introduce spring-boot-start-actuator dependency and directly use REST to obtain the runtime performance parameters of the process, so as to achieve the purpose of monitoring.
1.2 which parts of a regular project need to be modified? 1.2.1 configuration file
Before the transformation of the SSO project, there are a large number of configuration files, mainly including the following parts:
Static resource dependence
Data source
Mybatis configuration
Redis configuration
Business
Interceptor intercepts content
Listener, filter
Component scan path configuration
This article focuses on the following parts:
1) static resource processing
In SpringMVC, static resources are not intercepted if the URL rules configured by mvc:interceptors are as follows.
However, if the configuration is:
Solution 1: configure default in web.xml, and use defaultServlet to process requests such as:
Default * .jpg default * .png default * .gif default * .ico default * .gif default * .js default * .css
Scenario 2: declare static resource paths using tags
Scenario 3: use mvc:default-servlet-handler/ tags
SpringBoot solution: inherits the WebMvcConfigurerAdapter implementation of the addResourceHandlers method.
Public void addResourceHandlers (ResourceHandlerRegistry registry) {registry.addResourceHandler ("/ * *") .addResourceLocations ("classpath:/resource/") / / sso static resources. AddResourceLocations ("classpath:/META-INF/resources/") / / swagger static resources .setCachePeriod (0); / / 0 means no cache}
The path of sso static resource file is shown in the figure:
2) interceptor
The contents of the SpringMVC profile:
Intercept any request and initialize the parameters. Some requests do not need to be intercepted, and some requests are released without permission verification after login.
Request address
Adding an interceptor to SpringBoot simply inherits the WebMvcConfigurerAdapter and overrides the addInterceptors method.
/ * * interceptor * @ param registry * / @ Override public void addInterceptors (InterceptorRegistry registry) {registry.addInterceptor (permissionInterceptor). AddPathPatterns ("/ *"); super.addInterceptors (registry);}
The custom interceptor needs to initialize some parameters, so it needs to be registered before registering the interceptor, here we set it to lazy loading. The paths that are not blocked by login and the paths that do not need to judge permissions after login are written in the yml file, and the values are obtained through the system environment variable Environment.
@ Autowired@Lazy private PermissionInterceptor permissionInterceptor; @ Autowired private Environment environment;/****/@Bean public PermissionInterceptor permissionInterceptor () {PermissionInterceptor permissionInterceptor = new PermissionInterceptor (); List excludeUrls = Arrays.asList (environment.getProperty ("intercept.exclude.path"). Split (",")); List commonUrls = Arrays.asList (environment.getProperty ("intercept.login.exclude.path"). Split (",")); permissionInterceptor.setCommonUrls (commonUrls); permissionInterceptor.setExcludeUrls (excludeUrls); return permissionInterceptor;} 3) Database and mybatis configuration
A, data source configuration
There are three cases of data source injection:
[case 1]
Condition: do not quote druid-spring-boot-starter only rely on druid.jar, do not specify spring.datasource.type.
Result: the injected data source is the data source of tomcat.
Parsing: the dependent mybatis-spring-boot-starter project relies on the data source of tomcat. The DataSourceAutoConfiguration automatic injection class of spring-boot-autoconfigure-starter will determine whether one of the four default data sources (Hikari,Tomcat,Dbcp,Dbcp2) exists in the path without specifying the data source, and if so, inject it.
[case 2]
Condition: do not introduce druid-spring-boot-starter only rely on druid.jar, specify spring.datasource.type as DruidDataSource.
Result: the DruidDataSource data source is injected, but the druid configuration in the configuration file does not take effect.
Parsing: after the dependent data source is specified, the starter automatically injected by spring will inject the specified data source, and yml specifies the druid data source. The DataSourceProperties of the @ ConfigurationProperties annotation does not handle the performance parameter properties of the druid part, only the properties of the data source part.
[case 3]
Condition: the lead druid-spring-boot-starter does not depend on druid.jar and specifies that spring.datasource.type is DruidDataSource.
Result: if the DruidDataSource data source is injected, the druid configuration in the configuration file will also take effect.
Parsing: the druid-spring-boot-starter automatic configuration class creates a data source before DataSourceAutoConfiguration, and the DataSourceProperties injected by @ ConfigurationProperties contains the attributes of druid in the configuration file.
Pom.xml dependencies:
Com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter RELEASE
Yml configuration:
Spring: datasource: type: com.alibaba.druid.pool.DruidDataSource # current data source operation type driver-class-name: com.mysql.jdbc.Driver # mysql driver package url: jdbc:mysql://yourURL # Database name username: yourusername password: yourpassword druid: initial-size: 5 # initialization size min-idle: 5 # minimum max-active: 20 # maximum max-wait: 60000 # connection timeout time-between-eviction-runs-millis: 60000 # configure how often the test is performed Detect idle connections that need to be closed in milliseconds min-evictable-idle-time-millis: 300000 # specify at least how long an idle connection can be cleared, in milliseconds validationQuery: select 'x' test-while-idle: true # whether to perform a connection test when the connection is idle test-on-borrow: false # when borrowing a connection from the connection pool Whether to test the connection test-on-return: false # test the connection filters: config,wall,stat when the connection is returned to the connection pool
B, MyBatis configuration
By introducing mybatis-spring-boot-starter dependencies, you can easily configure mybatis for hands-on use.
Here is a brief analysis of the source code of mybatis-starter and how to configure mybatis.
First look at the spring.factories file of mybatis-spring-boot-autoconfigure in mybatis-spring-boot-starter
# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
You can see that the automatic injection class is MybatisAutoConfiguration. Starting with the analysis of this class, we can see that the data source must be created before the sqlSessionFactory of mybatis is loaded.
The @ EnableConfigurationProperties ({MybatisProperties.class}) annotation specifies that the prefix = "mybatis" part of the configuration file is valid, and that the property values are injected into the created SqlSessionFactoryBean, resulting in a SqlSessionFactory object.
@ Configuration// loads the current Bean@ConditionalOnClass ({SqlSessionFactory.class, SqlSessionFactoryBean.class}) when SqlSessionFactory, SqlSessionFactoryBean exists / / when the specified data source has only one or more data sources in the container but only specifies the preferred data source @ ConditionalOnSingleCandidate (DataSource.class) @ EnableConfigurationProperties ({MybatisProperties.class}) / / load the current Bean@AutoConfigureAfter ({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean {private final MybatisProperties properties only when the data source is injected into the Spring container @ Bean @ ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory (DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean (); factory.setDataSource (dataSource); factory.setVfs (SpringBootVFS.class) / / set the path if (StringUtils.hasText (this.properties.getConfigLocation () {factory.setConfigLocation (this.resourceLoader.getResource (this.properties.getConfigLocation () where the mybatis configuration file is located;}} / / set some properties in other MyBatisProperties objects slightly.... Return factory.getObject ();}}
The attributes that MybatisProperties contains:
@ ConfigurationProperties (prefix = "mybatis") public class MybatisProperties {public static final String MYBATIS_PREFIX = "mybatis"; private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver (); private String configLocation; private String [] mapperLocations; private String typeAliasesPackage; private Class typeAliasesSuperType; private String typeHandlersPackage; private boolean checkConfigLocation = false; private ExecutorType executorType; private Properties configurationProperties; @ NestedConfigurationProperty private Configuration configuration;}
C, use mybatis
Configuration file:
Application.yml
Mybatis:config-location: classpath:mybatis.xml # mybatis configuration file path type-aliases-package: com.creditease.permission.model # package of all Entity alias classes mapper-locations: classpath:mybatis/**/*.xml
As you can see from the MybatisProperties above, mybatis can specify some configuration, such as a custom interceptor pageHelper.
Mybatis.xml
Add @ MapperScan annotation to the startup class
@ MapperScan ("com.creditease.permission.dao") / / directory where mapper class resides public class SsoApplication {public static void main (String [] args) {SpringApplication.run (SsoApplication.class, args);}} 4) transaction
There are two ways to handle Spring transactions:
Programming
Write the transaction code in the business code using TransactionTemplate or directly using the underlying PlatformTransactionManager.
Advantages: you can handle transactions in the code block, which is more flexible.
Cons: intrusive to the code.
Declarative type
Use the @ Transactional annotation or profile-based approach to intercept before and after the method.
Advantages: non-invasive will not pollute the code.
Disadvantages: transactions can only be controlled on methods and classes, with small granularity.
A. Use @ Transactional annotations
For a non-SpringBoot project, you need to add configuration to the configuration file:
The SpringBoot project can replace the above configuration content with the @ EnableTransactionManagement annotation.
B. Using configuration files.
The previous sso is based on configuration, and the configuration code is as follows:
The modified SpringBoot is based on Java code:
@ Aspect@Configurationpublic class TransactionAdviceConfig {/ * specify the pointcut * / private static final String AOP_POINTCUT_EXPRESSION = "execution (public * com.creditease.permission.service.impl.*Impl.* (..))"; @ Resource DruidDataSource dataSource; / * specify the PlatformTransactionManager * @ return * / @ Bean public DataSourceTransactionManager transactionManager () {return new DataSourceTransactionManager (dataSource) that handles the transaction } / * specify pointcut processing logic, execute transaction * @ return * / @ Bean public TransactionInterceptor txAdvice () {DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute (); txAttrRequired.setPropagationBehavior (TransactionDefinition.PROPAGATION_REQUIRED); DefaultTransactionAttribute txAttrRequiredReadonly = new DefaultTransactionAttribute (); txAttrRequiredReadonly.setPropagationBehavior (TransactionDefinition.PROPAGATION_REQUIRED); txAttrRequiredReadonly.setReadOnly (true); NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource (); source.addTransactionalMethod ("query*", txAttrRequiredReadonly) Source.addTransactionalMethod ("find*", txAttrRequiredReadonly); source.addTransactionalMethod ("save*", txAttrRequired); source.addTransactionalMethod ("delete*", txAttrRequired); source.addTransactionalMethod ("add*", txAttrRequired); source.addTransactionalMethod ("modify*", txAttrRequired); return new TransactionInterceptor (transactionManager (), source) } / * Advisor assembly configuration, injecting Advice code logic into the Pointcut location * @ return * / @ Bean public Advisor txAdviceAdvisor () {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.set_Expression (AOP_POINTCUT_EXPRESSION); return new DefaultPointcutAdvisor (pointcut, txAdvice ();} 5) Global exception handling
In general, when there are exceptions in coding, we will try-catch catch exceptions, and sometimes in order to distinguish different exceptions, we will catch more than one exception at a time, and a large number of try-catch statements, which makes the code not elegant enough; the same exception handling is also redundant to write code multiple times, so it is necessary to introduce global exception handling.
Exception handling configuration file before transformation:
/ permission/noSecurity
Use the SimpleMappingExceptionResolver class to handle exceptions, set the custom exception type NopermissionException, and the request path / permission/noSecurity after the exception occurs.
The global exception class is set with @ RestControllerAdvice or @ ControllerAdvice in SpringBoot. The difference is similar to the @ Controller and @ RestController annotations.
Three kinds of global exception handling are defined in SSO: normal Exception handling, custom NopermissionException exception and parameter check exception.
The global exception handling code is as follows:
@ Configuration@Slf4j@RestControllerAdvicepublic class GlobalExceptionConfig {/ / does not have permission to process @ ExceptionHandler (value = {NopermissionException.class}) public void noPermissionExceptionHandler (HttpServletRequest request, Exception ex, HttpServletResponse response, @ Value ("${sso.server.prefix}") String domain) throws IOException {printLog (request,ex); response.sendRedirect ("jump to unauthorized page address") } / / Parameter verification processing @ ExceptionHandler (value = {BindException.class}) public ResultBody BindExceptionHandler (BindException bindException) {List errors = bindException.getBindingResult () .getAllErrors (); / / this ResultBody is a returned result object, which needs to return json, which contains the status code and prompt information return ResultBody.buildFailureResult (errors.get (0). GetDefaultMessage ()). } / / all uncaught exception handling logic @ ExceptionHandler (value = {Exception.class}) public ResultBody exceptionHandler (HttpServletRequest request,Exception ex) {printLog (request,ex); return ResultBody.buildExceptionResult ();} / / print out the request parameters and exceptions, combined with @ slf4j annotation public void printLog (HttpServletRequest request,Exception ex) {String parameters = JsonHelper.toString (request.getParameterMap ()) Log.error ("url >: {}, params >: {}, printLog >: {}", request.getRequestURL (), parameters,ex);}}
@ RestControllerAdvice is combined with @ Validation to verify Bean. If the verification fails, a BindException exception will be thrown. Through annotations, we can write less if-else code, judge whether the requested interface parameters are empty or not, and improve the aesthetics of the code. For example:
/ / General practice if (StringUtils.isEmpty (ssoSystem.getSysCode ()) / / SSO practice / / add @ Valid annotation @ RequestMapping (value = "/ add") to the Controller request method Method = RequestMethod.POST) public ResultBody add (@ Valid @ RequestBody SsoSystem ssoSystem) {} / / add @ NotNull annotation @ NotNull to the attribute of the SsoSystem Bean to be processed (message = "system number cannot be empty") private String sysCode
When the input parameter of sysCode is empty, the global exception handling class of BindException is thrown, and the parameters in json format are returned by capture processing:
{"resultCode": 2, "resultMsg": "system number cannot be empty", "resultData": null} 1.3 Note 1.3.1 problems caused by too high built-in tomcat version
SpringBoot1.5 uses the embedded tomcat8.5 version by default, while the original SSO of SpringMVC is deployed on tomcat7. The most obvious impact of the upgrade of tomcat on this transformation is cookie. The cookie verification protocol adopted after tomcat8 is Rfc6265CookieProcessor. The protocol requires that the naming of domain must follow the following rules:
Must be 1-9, amurz, Amurz,. ,-these characters are composed of.
It must start with a number or a letter (it used to start with. Creditease.corp will error tomcat cookie domain validation exception, which will be changed to creditease.corp).
Must end with a number or a letter.
Second, the front and rear ends are separated 2.1 to solve cross-domain problems
Because it is two different applications, there must be two different ports. Different ports will have cross-domain problems. SSO uses nginx to distinguish requests from front and back end, and reverse proxy requests correspond to different services.
Sso.creditease.com corresponds to the back-end application service.
Sso.creditease.com/web corresponds to the front-end static resource application service.
2.2 facilitate co-tuning efficiency and introduce swagger
Swagger is a plug-in displayed by the back-end interface. By modifying the interceptor code, the mock login object is not logged in, and the direct access interface is used for front-end and back-end debugging. You can see specific interface request paths and parameters, whether parameters are required, return values, interface statistics, and so on on the swagger plug-in.
Interface Statistics
Request parameters and path
Return value
2.3 Hop Interface Modification
The jump was previously done through SpringMvc's modeAndview method, but now there are two ways to handle it:
Changed to the form of restful interface, the front-end control jumps and then directly obtains the data.
Jump to the page directly through response.sendRedirect.
Note: the old code jump is in the form of adding redirect in front of the page path of return through SpringMvc, such as return "redirect:index", which will add jessionID after the URL of return by default.
2.4 problems that may be caused by static resource address change
Special attention should be paid to the relevant check paths in the code. For example, in the process of this transformation, path modification will affect the following aspects.
When verifying menu permissions, the previous person, role, and path have been bound, and modifying the menu access path will result in no permissions.
The refer source is determined by the login API that scans the code. Changing the path will cause the request to fail.
The previous sso-dome project refers to static resources, and the modified path will report 404.
This is the answer to the question about the transformation process of SpringBoot micro-service. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.