springBoot服务分析

springboot服务相关

1springboot常见相关配置

1 spring.profiles.active=PROD   //设置环境

2 server.port=8009   //设置内置tomcat服务启动窗口

3 server.context-path=/notify   //设置服务路径

4 management.security.enabled=false //springboot服务接口访问暴露是因为secrutiy,但是已废弃

5 management.endpoints.web.exposure.include=*  

6 security.basic.enabled=false

读取配置问题

遇到这个问题第一个可能性是配置文件出错,比如yml文件冒号后面少了一个空格,或者属性拼写错误。

第二种可能性是 resources目录没有Mark as resources root,导致读取不到对应目录下的配置文件(idea),这个问题的解决办法是根据target目录下查看是否有对应的配置文件

在ConfigFileApplicationListener类中,可以看到 DEFAULT_SEARCH_LOCATIONS 默认加载位置有:"classpath:/,classpath:/config/,file:./,file:./config/",DEFAULT_NAMES 默认配置文件名:"application"

第三种可能性是因为idea配置问题,当出现这种问题的时候,一般yml文件是粉色的

需要在project structure->对应的module中添加spring即可,之后变成

第四种,resource下的配置文件是绿色的,但是在target/class目录下却没有对应的application.yml配置文件,这种情况下springboot也是无法读取application.yml配置文件的,

这种情况造成的原因是因为在该module下<packaging>标签使用了pom打包,导致在resources下存在application.yml但是在target不存在该配置文件。

5.This View Is Not Available Until Indices Are Built

这种情况下是因为清除了缓存,需要等待编译才能显示相关配置

通常情况下我们将配置配置在application开头的主配置文件中,这样随着项目的增大配置项的增多会使文件变得非常臃肿,其实SpringBoot早已考虑到了该问题,SpringBoot提供了**@PropertySource和@ImportResource**两个注解用于加载外部配置文件使用。

  • @PropertySource通常用于属性加载配置文件,注意@PropertySource注解不支持加载yaml文件,支持properties文件。
  • @ImportResource通常用于加载Spring的xml配置文件

2.springboot相关配置区域

springboot api服务启动改变端口有下面几种方式:

1)通过Java程序启动参数传入:可以通过在Program arguments中添加--server.port=9000传入;(idea在edit configuration->environment->program agruments)

 2)通过配置文件传入:可以通过在配置文件application.properties中添加server.port=9000传入。

 3)通过JVM启动命令传入:可以通过-Dserver.port=9000传入(idea在edit configuration->environment->vm options)。

3.引入多个properties文件

SpringBoot的application.yml中内容较多,想按照内容的类别进行划分,比如数据库的配置文件,缓存的配置文件等等,这时候在appliacrion.yml中引用这些的配置主要有两种方式

1多个配置文件:在appliacrion.yml同级目录下新建:appliacrion-aaa.yml、appliacrion-bbb.yml两个文件;
在appliacrion.yml配置
spring:
  profiles:
      include:
          aaa,bbb
就能加进来了。。

2.使用@PropertySource(values={"classpath:/com/xxxx/xxxxx.properties"})【注意@ConfigurationProperties(prefix = "xxx")注解指定自定义配置文件中哪个层级属性需绑定,而PropertySource是指定配置文件路径,这两个配置文件可以配合使用】

4.springboot 不配置数据库

一般情况下springboot 服务都会有数据库相关的操作,因为配置文件缺少数据库连接相关配置启动的过程会报错,原因在于springboot在启动过程中会默认加载org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类,DataSourceAutoConfiguration类使用了@Configuration注解向spring注入了dataSource bean。因为工程中没有关于dataSource相关的配置信息,当spring创建dataSource bean因缺少相关的信息就会报错。

解决方法:

在Application类上增加:
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})

5.springboot接口访问安全访问关闭

springboot在启动后会因为安全设置问题,而无法访问,返回信息如下

{
    "timestamp": 1595312892656,
    "status": 401,
    "error": "Unauthorized",
    "message": "Full authentication is required to access this resource",
    "path": "/demo/greeting"
}

原因:有引入spring-boot-starter-security的jar包,显然这个是没有获取当登录信息,即是没有用户登录,没有登录就肯定没有对应的权限了

解决办法:在properties文件中设置security.basic.enabled=false

6 springboot项目实现热部署

文档:https://www.cnblogs.com/chenkeyu/p/10296588.html

7.spring api requestBody编码问题

spring在api接口层面,在调用过程中,机器发来的请求没有设置content-type,于是默认就是content-type:application/x-www-form-urlencoded,这样会导致请求的body会经过urlEncode编码,导致一些符号字符例如%\"和'等字符会经过编码成为%3A%22传给接口参数,所以需要指定Content-Type为application/json这样就可以避免字符串被url encode了。

原理是spring自作主张对内容进行urlencode,在ServletServerHttpRequest类的getBodyFromServletRequestParameters方法中

     /**
	 * Use {@link javax.servlet.ServletRequest#getParameterMap()} to reconstruct the
	 * body of a form 'POST' providing a predictable outcome as opposed to reading
	 * from the body, which can fail if any other code has used the ServletRequest
	 * to access a parameter, thus causing the input stream to be "consumed".
	 */
	private static InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
		Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);
 
		Map<String, String[]> form = request.getParameterMap();
		for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
			String name = nameIterator.next();
			List<String> values = Arrays.asList(form.get(name));
			for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
				String value = valueIterator.next();
				writer.write(URLEncoder.encode(name, FORM_CHARSET));
				if (value != null) {
					writer.write('=');
					writer.write(URLEncoder.encode(value, FORM_CHARSET));
					if (valueIterator.hasNext()) {
						writer.write('&');
					}
				}
			}
			if (nameIterator.hasNext()) {
				writer.append('&');
			}
		}
		writer.flush();
 
		return new ByteArrayInputStream(bos.toByteArray());
	}

Spring AOP相关

参考:https://blog.csdn.net/javazejian/article/details/56267036 关于 Spring AOP (AspectJ) 你该知晓的一切

          https://www.jianshu.com/p/5b9a0d77f95f   spring aop 及实现方式

AOP 是英文Aspect Oriented Programming 即被称为面向切面编程,它是OOP(Object Oriented Programming面向对象编程)的补充和完善,AOP的实现技术有很多种,其中与Java无缝对接的时AspectJ,AOP的出现解决了外围业务代码与核心业务代码的分离问题,AspectJ是一个Java实现的AOP框架,它能够在编译期中对Java进行AOP编译,而Spring AOP是采用在运行时增加的代理技术,aop的概念如下:

 

 

 

切点(PointCut)是需要在哪些目标方法中进行执行通知的定义,可以是直接定义,也可以是@PointCut

通知(Advice):在定义的切点上实现的增强处理,通知分为5种,前置通知,后置通知,环绕通知,后置返回通知以及异常通知。

连接点(JointPoint):即目标函数

切面(Aspect):一般包含了若干个配套的切点和通知,切面一般是类,例如日志记录切面类,权限校验切面类等

织入(Weaving):将切面应用到目标函数的过程,织入方式分为两种,一种是静态织入,另一种是动态织入。Aspect J采用的是静态织入的技术,ApectJ主要采用的是编译期织入,在编译期间使用AspectJ的ajc编译器(类似javac,是采用Java编写的编译器,能够识别aspect语法,而Java编译器并不识别aspect语法)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。而Spring AOP采用的时动态织入的技术(JDK动态代理或者CGLIB)

AspectJ静态织入方式
AspectJ静态织入方式

 

注意:编写AspectJ类,注意关键字为aspect(MyAspectJDemo.aj,其中aj为AspectJ的后缀),含义与class相同,即定义一个AspectJ的类,这种定义是典型的A,也可以是通过@Aspect注解定义切面类,现在很多都是切点与通知放在一起的定义方式

/**
 * Created by zejian on 2017/2/15.
 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
 * 切面类
 */
public aspect MyAspectJDemo {
    /**
     * 定义切点,日志记录切点
     */
    pointcut recordLog():call(* HelloWord.sayHello(..));

    /**
     * 定义切点,权限验证(实际开发中日志和权限一般会放在不同的切面中,这里仅为方便演示)
     */
    pointcut authCheck():call(* HelloWord.sayHello(..));

    /**
     * 定义前置通知!
     */
    before():authCheck(){
        System.out.println("sayHello方法执行前验证权限");
    }

    /**
     * 定义后置通知
     */
    after():recordLog(){
        System.out.println("sayHello方法执行后记录日志");
    }
}

 

Spring AOP与AspectJ

Spring AOP 与ApectJ 的目的一致,都是为了统一处理横切业务,但与AspectJ不同的是,Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现),Spring AOP 更注重的是与Spring IOC容器的结合,并结合该优势来解决横切业务的问题,因此在AOP的功能完善方面,相对来说AspectJ具有更大的优势,同时,Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器),因此Spring很机智回避了这点,转向采用动态代理技术的实现原理来构建Spring AOP的内部机制(动态织入),这是与AspectJ(静态织入)最根本的区别。在AspectJ 1.5后,引入@Aspect形式的注解风格的开发,Spring也非常快地跟进了这种方式,因此Spring 2.0后便使用了与AspectJ一样的注解。请注意,Spring 只是使用了与 AspectJ 5 一样的注解,但仍然没有使用 AspectJ 的编译器,底层依是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器。

@Aspect
public class MyAspect {

    /**
     * 前置通知
     */
    @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 后置通知
     * returnVal,切点方法执行后的返回值
     */
    @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
    public void AfterReturning(Object returnVal){
        System.out.println("后置通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
        System.out.println("出现异常:msg="+e.getMessage());
    }

    /**
     * 无论什么情况下都会执行的方法
     */
    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void after(){
        System.out.println("最终通知....");
    }

}

除了上述方式外,还可采用与ApectJ中使用pointcut关键字类似的方式定义切入点表达式如下,使用@Pointcut注解:

/**
 * 使用Pointcut定义切点
 */
@Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
private void myPointcut(){}

/**
 * 应用切入点函数
 */
@After(value="myPointcut()")
public void afterDemo(){
    System.out.println("最终通知....");
}

切入点指示符

为了方法通知应用到相应过滤的目标方法上,SpringAOP提供了匹配表达式,这些表达式也叫切入点指示符。

通配符

常见的通配符有*  +  ..

.. : 匹配方法定义中的任意数量的参数,此外还匹配类定义中的任意数量包

//任意返回值,任意名称,任意参数的公共方法
execution(public * *(..))
//匹配com.zejian.dao包及其子包中所有类中的所有方法
within(com.zejian.dao..*)

+ :匹配给定类的任意子类

//匹配实现了DaoUser接口的所有子类的方法
within(com.zejian.dao.DaoUser+)

* :匹配任意数量的字符

//匹配com.zejian.service包及其子包中所有类的所有方法
within(com.zejian.service..*)
//匹配以set开头,参数为int类型,任意返回值的方法
execution(* set*(int))

 

类型签名表达式

为了方便类型(如接口、类名、包名)过滤方法,Spring AOP 提供了within关键字。其语法格式如下:within(<type name>)

type name 则使用包名或者类名替换即可

//匹配com.zejian.dao包及其子包中所有类中的所有方法
@Pointcut("within(com.zejian.dao..*)")

//匹配UserDaoImpl类中所有方法
@Pointcut("within(com.zejian.dao.UserDaoImpl)")

//匹配UserDaoImpl类及其子类中所有方法
@Pointcut("within(com.zejian.dao.UserDaoImpl+)")

//匹配所有实现UserDao接口的类的所有方法
@Pointcut("within(com.zejian.dao.UserDao+)")

方法签名表达式

如果想根据方法签名进行过滤,关键字execution可以帮到我们,语法表达式如下

//scope :方法作用域,如public,private,protect
//returnt-type:方法返回值类型
//fully-qualified-class-name:方法所在类的完全限定名称
//parameters 方法参数
execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters))

对于给定的作用域、返回值类型、完全限定类名以及参数匹配的方法将会应用切点函数指定的通知

//匹配UserDaoImpl类中的所有方法
@Pointcut("execution(* com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中的所有公共方法并且返回值为int类型
@Pointcut("execution(public int com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中第一个参数为int类型的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(int , ..))")
原文链接:https://blog.csdn.net/javazejian/article/details/56267036

其他指示符

bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

//匹配名称中带有后缀Service的Bean。
@Pointcut("bean(*Service)")
private void myPointcut1(){}

this :用于匹配当前AOP代理对象类型的执行方法;请注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配

//匹配了任意实现了UserDao接口的代理对象的方法进行过滤
@Pointcut("this(com.zejian.spring.springAop.dao.UserDao)")
private void myPointcut2(){}

target :用于匹配当前目标对象类型的执行方法;

//匹配了任意实现了UserDao接口的目标对象的方法进行过滤
@Pointcut("target(com.zejian.spring.springAop.dao.UserDao)")
private void myPointcut3(){}

@within:用于匹配所以持有指定注解类型内的方法;请注意与within是有区别的, within是用于匹配指定类型内的方法执行;
 

//匹配使用了MarkerAnnotation注解的类(注意是类)
@Pointcut("@within(com.zejian.spring.annotation.MarkerAnnotation)")
private void myPointcut4(){}

@annotation(com.zejian.spring.MarkerMethodAnnotation) : 根据所应用的注解进行方法过滤

//匹配使用了MarkerAnnotation注解的方法(注意是方法)
@Pointcut("@annotation(com.zejian.spring.annotation.MarkerAnnotation)")
private void myPointcut5(){}

这里最后说明一点,切点指示符可以使用运算符语法进行表达式的混编,如and、or、not(或者&&、||、!)

//匹配了任意实现了UserDao接口的目标对象的方法并且该接口不在com.zejian.dao包及其子包下
@Pointcut("target(com.zejian.spring.springAop.dao.UserDao) !within(com.zejian.dao..*)")
private void myPointcut6(){}
//匹配了任意实现了UserDao接口的目标对象的方法并且该方法名称为addUser
@Pointcut("target(com.zejian.spring.springAop.dao.UserDao)&&execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
private void myPointcut7(){}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值