028-86922220

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

SpringBoot微服务改造过程是怎样

SpringBoot微服务改造过程是怎样,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

我们提供的服务有:网站制作、成都网站制作、微信公众号开发、网站优化、网站认证、汇川ssl等。为上千企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的汇川网站制作公司

SSO是公司一个已经存在了若干年的项目,后端采用SpringMVC、MyBatis,数据库使用MySQL,前端展示使用Freemark。今年,我们对该项目进行了一次革命性的改进,改造成SpringBoot架构,并且把前后端分离,前端采用Vue框架。

一、使用SpringBoot架构进行改造

1.1 为什么使用SpringBoot

相比较于传统的Spring,SpringBoot具有以下优点:

1.2 一个常规项目都需要改造哪些部分

1.2.1 配置文件

SSO项目改造前充斥着大量的配置文件,主要包含以下这些部分:

本文着重介绍以下几个部分:

1)静态资源处理

SpringMVC中,如果mvc:interceptors配置的URL规则如下,则不会拦截静态资源。

但是如果配置的是:

方案1: 在web.xml中配置default,用defaultServlet先处理请求如:

   
        default
        *.jpg
    
    
        default
        *.png
    
    
        default
        *.gif
    
    
        default
        *.ico
    
    
        default
        *.gif
    
    
        default
        *.js
    
    
        default
        *.css
    

方案2:使用标签声明静态资源路径

 
 

方案3:使用mvc:default-servlet-handler/标签

SpringBoot解决方案:继承WebMvcConfigurerAdapter实现addResourceHandlers方法。

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**")
    .addResourceLocations("classpath:/resource/")//sso静态资源
    .addResourceLocations("classpath:/META-INF/resources/")//swagger静态资源
    .setCachePeriod(0);//0表示不缓存 
}

sso静态资源文件路径如图:

SpringBoot微服务改造过程是怎样

2)拦截器

SpringMVC配置文件内容:

拦截任何请求并且初始化参数,有些请求是不需要拦截的,有的请求登录后不需要经过权限校验直接放行。

  
	  
		  
             
		     
		  
		  请求地址
		  
		  
		  
		  请求地址
		  
		 
    
 

SpringBoot中添加拦截器只需继承WebMvcConfigurerAdapter,并重写addInterceptors方法即可。

 /*** 拦截器 
 * @param registry
 */ 
 @Override
 public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(permissionInterceptor).
            addPathPatterns("/**");
 	super.addInterceptors(registry);
 }

自定义的拦截器需要初始化一些参数,因此需要在注册拦截器之前注册,这里我们设置为懒加载。免登录拦截的路径,以及登录后不需要判断权限的路径都写在yml文件了,通过系统环境变量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)数据库和mybatis配置

A、数据源配置

数据源注入的三种情况:

【情况一】

【情况二】

【情况三】

pom.xml依赖:

	
	
		
		
		
	
	
		com.alibaba
		druid-spring-boot-starter
		1.1.10
	
	
		org.mybatis.spring.boot
		mybatis-spring-boot-starter
		RELEASE
		

yml配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver            # mysql驱动包
    url: jdbc:mysql://yourURL        # 数据库名称
    username: yourusername
    password: yourpassword
    druid:
      initial-size: 5  # 初始化大小
      min-idle: 5  # 最小
      max-active: 20  # 最大
      max-wait: 60000  # 连接超时时间
      time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000  # 指定一个空闲连接最少空闲多久后可被清除,单位是毫秒
      validationQuery: select 'x'
      test-while-idle: true  # 当连接空闲时,是否执行连接测试
      test-on-borrow: false  # 当从连接池借用连接时,是否测试该连接
      test-on-return: false  # 在连接归还到连接池时是否测试该连接
      filters: config,wall,stat

B、MyBatis配置

通过引入mybatis-spring-boot-starter依赖,可以简单配置mybatis上手使用。

下面简单分析mybatis-starter的源码以及如何配置mybatis。

先看mybatis-spring-boot-starter中mybatis-spring-boot-autoconfigure的spring.factories文件

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

可以看到自动注入类是MybatisAutoConfiguration,我们从这个类入手分析可以知道,必须先创建好了数据源后,才会加载mybatis的sqlSessionFactory。

@EnableConfigurationProperties({MybatisProperties.class})注解指定了配置文件中 prefix = "mybatis" 那部分属性有效,这部分属性值将注入到已创建的SqlSessionFactoryBean中,最后生成SqlSessionFactory对象。

@Configuration
//当SqlSessionFactory,SqlSessionFactoryBean存在的情况下加载当前Bean
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
//当指定数据源在容器中只有一个或者有多个但是只指定首选数据源
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
//当数据源注入到Spring容器后才开始加载当前Bean
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
	private final MybatisProperties properties;
	@Bean
	@ConditionalOnMissingBean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); 		 
		factory.setDataSource(dataSource);
		factory.setVfs(SpringBootVFS.class);
	   //设置mybatis配置文件所在路径
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
	      factory.setConfigLocation(this.resourceLoader.getResource
          (this.properties.getConfigLocation())); }
	    }
	  //设置其他MyBatisProperties对象中有的属性略....
       return factory.getObject();
   }
}

MybatisProperties含有的属性:

@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、使用mybatis

application.yml

mybatis:
config-location: classpath:mybatis.xml        # mybatis配置文件所在路径
type-aliases-package: com.creditease.permission.model    # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/**/*.xml

从上面的MybatisProperties可以看出,mybatis可以指定一些configuration,比如自定义拦截器pageHelper。

mybatis.xml




	
		
		
	
@MapperScan("com.creditease.permission.dao")//mapper类所在目录
public class SsoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SsoApplication.class, args);
    }
}
4)事务

Spring事务有两种处理方式:

用TransactionTemplate或者直接使用底层的PlatformTransactionManager将事务代码写在业务代码中。

优点:可以在代码块中处理事务,比较灵活。

缺点:对代码具有侵入性。

采用@Transactional注解或者基于配置文件方式,在方法前后进行拦截。

优点:非侵入性不会污染代码。

缺点:事务只能在方法和类上控制,粒度较小。

A、使用@Transactional注解

非SpringBoot工程,需要在配置文件中加入配置:

 

SpringBoot工程可以用@EnableTransactionManagement注解代替上面的配置内容。

B、采用配置文件方式

之前的sso是基于配置的方式,配置代码如下:


     
     
 
 
     
         
         
         
         
         
         
     
 

改造后的SpringBoot基于Java代码:

@Aspect
@Configuration
public class TransactionAdviceConfig {

 /**
  * 指定切入点
  */
 private static final String AOP_POINTCUT_EXPRESSION = "execution(public * com.creditease.permission.service.impl.*Impl.*(..))";

 @Resource
 DruidDataSource dataSource;

 /**
  * 指定处理事务的PlatformTransactionManager
  * @return
  */
 @Bean
 public DataSourceTransactionManager transactionManager() {

     return new DataSourceTransactionManager(dataSource);

 }

 /**
  * 指定切入点处理逻辑,执行事务
  * @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组装配置,将Advice的代码逻辑注入到Pointcut位置
  * @return
  */
 @Bean
 public Advisor txAdviceAdvisor() {
     
     AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
     pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
     return new DefaultPointcutAdvisor(pointcut, txAdvice());
 }
5)全局异常处理

一般编码时有异常我们都会try-catch捕获异常,有时为了区分不同的异常还会一次catch多个异常,大量的try-catch语句,这样使得代码也不够优雅;一个相同的异常处理写多次代码也比较冗余,所以引入全局的异常处理非常必要。

改造前的异常处理配置文件:



        
            
                /permission/noSecurity
            
        
 

使用SimpleMappingExceptionResolver类处理异常,设置自定义异常类型NopermissionException,以及异常发生后的请求路径/permission/noSecurity。

SpringBoot中采用@RestControllerAdvice或者@ControllerAdvice设置全局异常类。这两者区别类似于@Controller和@RestController注解。

SSO中定义了三种全局的异常处理:普通的Exception处理;自定的NopermissionException异常和参数校验异常。

全局异常处理代码如下:

@Configuration
@Slf4j
@RestControllerAdvice
public class GlobalExceptionConfig {

	//无权限处理
    @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("跳转到无权限页面地址");
    }
    
   	 //参数校验处理
	@ExceptionHandler(value = {BindException.class})
    public ResultBody BindExceptionHandler(BindException  bindException){
        List errors = bindException.getBindingResult().getAllErrors();
        //这个ResultBody是一个返回结果对象,这里需要返回json,里面包含了状态码和提示信息
        return  ResultBody.buildFailureResult(errors.get(0).getDefaultMessage());
    }

	//所有未捕获的异常处理逻辑
    @ExceptionHandler(value = {Exception.class})
    public ResultBody exceptionHandler(HttpServletRequest request,Exception ex){
        printLog(request,ex);
        return  ResultBody.buildExceptionResult();
    }

	//将请求参数和异常打印出来,结合@slf4j注解
    public void printLog(HttpServletRequest request,Exception ex){
        String parameters = JsonHelper.toString(request.getParameterMap());
        log.error("url>>>:{},params>>>:{} ,printLog>>>:{}",request.getRequestURL(),parameters,ex);
    }

}

@RestControllerAdvice结合@Validation,可以对Bean进行校验,校验不通过会抛出BindException异常。通过注解可以少写if-else代码,判断请求的接口参数是否为空,提高代码的美观性。例如:

	//常规做法
	if(StringUtils.isEmpty(ssoSystem.getSysCode())
	
	//SSO做法
	//在Controller请求方法上添加@Valid注解
	@RequestMapping(value = "/add", method = RequestMethod.POST)
    public ResultBody add(@Valid @RequestBody SsoSystem ssoSystem) {
    
    }

	//在需要处理的SsoSystem Bean的属性上加@NotNull注解
	@NotNull(message = "系统编号不能为空")
	private String sysCode;

当sysCode传入参数为空时,就会抛出BindException被全局的异常处理类,捕获处理返回json格式的参数:

{
    "resultCode":2,
    "resultMsg":"系统编号不能为空",
    "resultData":null
}

1.3 注意事项

1.3.1 内置tomcat版本太高引发的问题

SpringBoot1.5默认使用内嵌tomcat8.5版本,而原来SpringMVC的SSO部署在tomcat7上。tomcat的升级对这次改造影响最明显的就是cookie。 tomcat8后采用的cookie校验协议是Rfc6265CookieProcessor。该协议要求domain的命名必须遵循以下规则:

二、前后端分离

2.1 解决跨域问题

由于是两个不同的应用,必然会有两个不同的端口。不同的端口就会有跨域问题,SSO采用的方式是通过nginx区分来自前后端的请求,反向代理请求对应到不同的服务去。

2.2 方便联调效率,引入swagger

swagger是后端接口展示的插件,通过修改拦截器代码,mock登录对象免登录,直接访问接口进行前后端的调试。在swagger插件上可以看到具体接口请求路径和参数、参数是否必须、返回值、接口统计信息等。

SpringBoot微服务改造过程是怎样

SpringBoot微服务改造过程是怎样

2.3 跳转接口修改

之前是通过SpringMvc的modeAndview方式跳转的,现在做了两种处理:

注意:老代码跳转采用的是通过SpringMvc在return的页面路径前加redirect的形式,如:return "redirect:index",这样默认会在return的URL后加jessionID。

2.4 静态资源地址变更可能引发的问题

特别需要注意代码中的相关校验路径的地方。比如在这次改造过程中路径修改会影响以下几个方面。

关于SpringBoot微服务改造过程是怎样问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。


名称栏目:SpringBoot微服务改造过程是怎样
本文来源:http://www.tsicrk.com/article/jpdjed.html

其他资讯

让你的专属顾问为你服务

2.2589s