springboot


title: springboot
date: 2020-11-08 21:07:43
tags:

杂记

  1. springboot项目打jar包之后,找不到文件的问题,解决在收藏中,暂时只知道不能写./static/…
    相对路径,而是要写classpath,类路径,用classPathResource路径就可以

springboot的static和template详解

  1. 两者的区别看这里,简单说就是static用来存放静态html,css和js之类文件
  2. controller return的解析,当没有动spring.view.prefix和suffix的情况时,并且没有加载thymeleaf等模板时,controller中的返回值默认就是static目录下的静态资源。而当引入模板之后,动态就会覆盖静态,返回值默认是template下的文件。如果此时还想访问static静态资源的话,那么就要使用重定向。
  3. 注意事项,想要直接访问static下的资源的时候,不需要在路径下写/static,那样反而会找不到文件。
  4. 视图解析器的前后缀,这个是最牛逼的,视图解析器就是靠前后坠来拼接逻辑视图的完整路径,可以指定任何路径

spring的数据源

  1. 启动spring工程首先要配置数据源,如果未配置数据源就会报错无法启动。
  2. 配置数据源时注意,如果使用的是properties那么密码是数字000000可以的,但是如果是yml,那么000000就会被转换成0,因为在yml中是区分数据类型的,或者将00000两端加上单引号表示这是一个字符串。
  3. 如果使用的是yml的自动提示,那么username和password前面会加上data-,这样的话就会报错,将data去掉就可以正常连接数据库。详细看这里

springSecurity

  1. 使用jdbc验证之后,spring5之后版本规定必须要使用密码编码器,明文密码经过密码编码器编码之后就会产生一个字符串,数据库中的密码应该存储的是这个字符串而不是明文密码。
  2. 使用httpSecurity来对url进行权限控制的时候,角色名要存储为ROLE_ADMIN的类似形式,security才能进行权限的控制。
  3. 先挖个坑,HTTPS的使用方式还未知。

杂记

  1. 调整日志的级别的时候,不能使用代码智能提示,和之前配置数据源一样,要注意,在logging.level.root要新增一个root目录
logging:
  level:
    root: debug

#而不是代码提示的那样
logging:
    level: debug

JMs

  1. jms连接池上我看的书有些错误

        <!--activemq的连接池-->
<!--        <dependency>-->
<!--这种是2.0以下版本的连接池-->
<!--            <groupId>org.apache.activemq</groupId>-->
<!--            <artifactId>activemq-pool</artifactId>-->
<!--        </dependency>-->

<!--这种是2.0以上版本的连接池,应该使用这种-->
        <dependency>
            <groupId>org.messaginghub</groupId>
            <artifactId>pooled-jms</artifactId>
        </dependency>

  1. 发送消息和接受消息的都必须是同一种类型的数据,否则会报错无法映射(unable to cast).

单元测试

  1. 单元测试需要使用@RunWith和@SpingBootTest,前者需要引入junit,后者需要使用starter-test

mybatis-plus

  1. 在controller中注入的时候应该注入service的接口,而不是注入实现类,因为spring基于接口代理,所以会无法代理注入
  2. 另外一种情况可能也会引发这种情况,就是开启了事务管理,可能也会出错
  3. 解决办法有两种,一种是将实现类换为接口,另一种方法是在application.yml中进行配置,spring.aop.proxy-target-class=true
  4. 详情看这里
  5. 详情看这里
  6. 详情看这里
  7. 详情看这里

将数据库中的datetime类型的数据经常作为字符串进行存储,rom框架会只能转换

mp自动填充功能

  1. 使用逻辑删除功能,这样在删除更新查询的情况下可以自动将逻辑删除字段进行更改,但是新增的时候无法自动填充字段
  2. 填充字段方法看这里这里

StringUtils的isempty弃用,建议使用hasLenght或者hasText,另一种方法是使用ObjectUtils.isempty方法

这里

mybatis-plus的数据传送时数据库无法更新,检查字段的拼写和数据库中的是否相同,如果相同仍然不行,检查getsset方法,因为mybaits是通过setget方法传递数据的

mapper接口和xml文件的名字尽量一致,否则可能会由莫名的bug

教程中有错,spring-web-starter中已经没有hebernate-validation依赖了,集成到了validation-starter中了,要单独引入

过滤器,拦截器,切面的区别与联系

  1. 参考博客看这里这里这里

  2. 这里简单介绍一下

过滤器拦截器AOP
实现方式集成Filter实现springmvc提供的拦截器接口通过aspect注解实现
可获取内容原始的http请求与响应获取请求访问的类与方法获取访问的类、方法以及参数值
无法获取内容请求要访问的类与方法,以及参数(例如:拿不到你请求的控制器和请求控制器中的方法的信息)请求参数的值. (例如:可以拿到你请求的控制器和方法,却拿不到请求方法的参数),具体可根据dispatcherServlet跟踪源码http原始的请求与响应的对象
拦截内容URLURL类的元数据(包、类、方法名、参数等)
依赖及实现依赖于servlet容器,基于函数回调依赖于web框架,基于Java的反射机制,属于面向切面编程(AOP)的一种运用依赖spring,基于AOP实现
应用场景在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。请求参数做过滤和修改,同时FilterChain过滤链执行完,并且完成业务流程后,会返回到过滤器,此时也可以对请求的返回数据做处理。登陆校验,设置字符编码,鉴权操作国际化,做主题更换,过滤等AOP常和事务结合:Spring的事务管理:声明式事务管理(切面),日志记录,使用日志,事务,请求参数安全验证
  1. Filter与Interceptor联系与区别
  • 拦截器是基于java的反射机制,使用代理模式,而过滤器是基于函数回调。
  • 拦截器不依赖servlet容器,过滤器依赖于servlet容器。
  • 拦截器只能对action起作用,而过滤器可以对几乎所有的请求起作用(可以保护资源)。
  • 拦截器可以访问action上下文,堆栈里面的对象,而过滤器不可以。
  • 执行顺序:过滤前-拦截前-Action处理-拦截后-过滤后。

3.1 作用域不同

  • 过滤器依赖于servlet容器,只能在 servlet容器,web环境下使用
  • 拦截器依赖于spring容器,可以在spring容器中调用,不管此时Spring处于什么环境

3.2 细粒度的不同

  • 过滤器的控制比较粗,只能在请求进来时进行处理,对请求和响应进行包装
  • 拦截器提供更精细的控制,可以在controller对请求处理之前或之后被调用,也可以在渲染视图呈现给用户之后调用

3.3 中断链执行的难易程度不同

  • 拦截器可以 preHandle方法内返回 false 进行中断
  • 过滤器就比较复杂,需要处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面
  1. 注意拦截器提供三个基本的切入点
  • public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)表示被拦截的URL对应的方法执行前的自定义处理

  • public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)表示此时还未将modelAndView进行渲染,被拦截的URL对应的方法执行后的自定义处理,。

  • public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e)表示此时modelAndView已被渲染,执行拦截器的自定义处理。

springboot+redis

  1. redis的详细介绍看这里

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <!--不依赖redis的异步客户端lettuce-->
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
     </exclusions>
</dependency>

<!--引入redis的客户端驱动,类似于mysql-connector-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

redis的体系结构

  1. 请添加图片描述

  2. 配置工厂的代码在开头的连接中,配置工厂主要就是配置连接池。

redisTemplate

  1. 使用工厂方法每次需要从工厂获取连接,使用后在关闭,于是spring封装了redisTemplate方便操作。
  2. redis在springboot的自动配置中已经创建好了,可以直接使用。但是如果你自己配置了redis,那么自动配置就失效了。这里介绍一下自己配置redis

@Configuration
public class RedisConfig {
    
    private RedisConnectionFactory redisConnectionFactory = null;

    /**
     * 配置连接工厂,使用单例模式。避免重复创建,浪费资源
     * @return
     */
    @Bean(name = "RedisConnectionFactory")
    public RedisConnectionFactory initRedisConnectionFactory() {
        if (this.redisConnectionFactory!=null) {
            return redisConnectionFactory;
        }

//        首先配置连接池
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(30);
        config.setMaxTotal(50);
        config.setMaxWaitMillis(2000);

        JedisConnectionFactory factory = new JedisConnectionFactory(config);
//        获取单机配置
        RedisStandaloneConfiguration standaloneConfiguration = factory.getStandaloneConfiguration();
        
//        配置单机redis信息
        standaloneConfiguration.setHostName("192.168.1.101");
        standaloneConfiguration.setPort(6379);
        standaloneConfiguration.setPassword("123456");
        this.redisConnectionFactory = factory;
        return redisConnectionFactory;
    }


    /**
     * 初始化redisTemplate
     * @return
     */
    @Bean
    public RedisTemplate<Object,Object> initRedisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(initRedisConnectionFactory());
        return redisTemplate;
    }
    
}

  1. 我们设置好了之后开始set值,但是可以看到客户端中存储的并不是字符串而是二进制字符串,这是因为redis存储的是java对象序列化之后的东西,通过这个原理,spring提供了不同的序列化器,可以将java对象序列化成指定的样子。序列化器的体系结构入下图所示
    请添加图片描述

  2. 这里主要介绍stringRedisSerializer和jdkSerializationRedisSerializer。其中jdkSerializationRedisSerializer是其默认序列化器
    请添加图片描述

spring默认使用jdkSerializationRedisSerializer。但是键用二进制存储不利于阅读,因此一般将键的序列化器改为stringRedisSerializer。

/**
     * 初始化redisTemplate
     * @return
     */
    @Bean
    public RedisTemplate<Object,Object> initRedisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//        redisTemplate已经把StringSerializer初始化完成,直接用就可以。
        RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
//        设置key,hashkey和hashvalue的序列化器都是StringSerializer
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        redisTemplate.setConnectionFactory(initRedisConnectionFactory());
        return redisTemplate;
    }

spring对redis数据类型操作的封装

操作接口功能备注
GeoOperation单元格单元格
单元格单元格单元格
  1. 简单的操作接口就不展示了,直接redisTempalte.opsFor。。。直接提示出来。

  2. 有时需要对一个接口进行多次操作,就需要绑定一个接口。

redisTemplate.boundHashOps("hashkey");
// 绑定操作接口类型以及绑定的key

sessionCallback和redisCallback

  1. redis默认是每个操作都是一个单独的连接,想要一个连接执行多条操作可以用SessionBack和redisCallback。
  2. session的操作抽象级别高,平常使用这个比较多,而redis的比较底层,使用较少。

public void useRedisCallback() {
    redisTemplate.execute((RedisConnection rc) -> {
                rc.set("key1".getBytes(), "field".getBytes());
                rc.hSet("hash".getBytes(), "field".getBytes(), "hvalue".getBytes());
                return null;
            }
        );
}


public void userSessionCallback() {
    redisTemplate.execute((RedisOperations ro)->{
        ro.opsForValue().set("key1","value1");
        ro.opsForHash().put("hash","field","hvalue");
        return null;
    });
}

在springboot中配置和使用redis

  1. 只需要引入依赖(在上面),在配置一下就可以使用

spring:
  redis:
    jedis:
      pool:
        min-idle: 5 # 最大空闲数
        max-active: 10 # 最大活跃数
        max-idle: 10 # 最大空闲时间
        max-wait: 2000 # 最大等待时间
    port: 6379
    host: 192.168.1.101
    password: 123456
    timeout: 1000 # 连接超时,ms
  1. springboot会自动生成并装配redistemplate所需的bean,但是使用的还是默认jdk序列化器,我们需要改为string序列化器。
@Configuration
public class RedisConfig1 {

    @Autowired
    private RedisTemplate redisTemplate;
    
    // 在构造方法执行完之后执行。先让spring为我们初始化redistemplate,在修改redistemplate的序列化器
    @PostConstruct
    public void init() {
        initRedisTemplate();
    }
    
    
    private void initRedisTemplate() {
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
    }
}

redistemplate使用实战

redis的事务

  1. redis中使用事务,通过watch…multi…exec,watch是监控redis的键,multi是开始事务,注意开始事务后,redis指令不会立即执行,而是放在任务队列中,如果在此时调用redis的返回结果,都是空。只有exec才是开始执行命令,这是redis的返回值就不是null。如果键发生了改变(即使是重新赋值和原来一样的值,也被认为是改变了值)。
    请添加图片描述
/**
     * 测试事务
     */
    public void testTransaction() {
        redisTemplate.opsForValue().set("key1","value1");
        List execute = (List) redisTemplate.execute((RedisOperations ro) -> {
            //           设置要监控的key1 
            ro.watch("key1");
            //            开启事务,在exec前都是只进入队列,不执行
            ro.multi();
            ro.opsForValue().set("key2", "value2");
            ro.opsForValue().increment(1);
            System.out.println("ro.opsForValue().get(\"key2\") = null,因为任务只是进入队列,未执行" + ro.opsForValue().get("key2"));
            ro.opsForValue().set("key3", "value3");
            Object value3 = ro.opsForValue().get("key3");
            return ro.exec();
        });
    }
  1. 注意redis和sql事务的不同,如果命令执行过程中抛出异常,那么哪个命令会失败,但是后面的任务仍然会执行。redis只是将任务放入到队列,并不会检查任务是否会成功。

redis流水线

  1. redis的流水线相当于sql的批量执行,避免redis命令一条条发送,性能不高。因为reids的瓶颈不是io而是网络传输速度。
/**
     * 测试redis流水线
     */
    public void testPipLine() {
        long start = System.currentTimeMillis();
        redisTemplate.executePipelined((RedisOperations ro)->{
            for (int i = 0; i < 10000; i++) {
                ro.opsForValue().set("pipline"+i,"value"+i);
                if (i==10000){
                    System.out.println("命令只是进入队列,所以值为空");
                }
            }
        });

        System.out.println("耗时:"+(System.currentTimeMillis()-start));
    }

redis发布订阅

  1. 发布订阅模式,redis提供一个通道,让消息能够发送到这个通道上,多个系统可以监听这个通道,当有消息到通道上的时候,通道就会通知它的监听者。
  2. 发布订阅模式是经典的设计模式,需要创建消息监听器监听发送的消息。
@Component
public class RedisMessageListener implements MessageListener {


    /**
     * 当有消息时,会调用此方法
     * @param message
     * @param bytes
     */
    @Override
    public void onMessage(Message message, byte[] bytes) {
//        消息体
        String body = new String(message.getBody());
//        通道名称
        String topic = new String(bytes);
        System.out.println(body);
        System.out.println(topic);
    }
}
  1. 随后在创建配置文件,配置任务线程池和监听器容器
@Configuration
public class RedisConfig1 {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private MessageListener messageListener;

//    任务线程池
    private ThreadPoolTaskScheduler taskScheduler;

    /**
     * 初始化线程池,用来运行线程等待处理redis的信息
     * @return
     */
    @Bean
    public ThreadPoolTaskScheduler initTaskSceduler() {
        if (taskScheduler!=null) {
            return taskScheduler;
        }
        taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(20);
        return taskScheduler;
    }

    /**
     * 定义redis的监听容器
     * @return
     */
    @Bean
    public RedisMessageListenerContainer initRedisContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.setTaskExecutor(taskScheduler);
//        指定接受topic1的消息
        ChannelTopic topic1 = new ChannelTopic("topic1");
        container.addMessageListener(messageListener,topic1);
        return container;
    }

}
  1. 随后在redis客户端中输入命令,publish topic1 msg
  2. 在spring中,使用redistemplate发送消息,
redisTemplate.convertAndSend(channel,message);

使用Lua脚本

使用spring缓存注解操作redis

  1. spring缓存注解可以简化redis的操作,在这之前需要配置spring的缓存管理器

  2. 缓存管理器能提供一些重要的信息,比如缓存类型,超时时间等。spring可以支持多种类型的缓存的使用。因此存在多种缓存处理器。提供了缓存处理器的接口CacheMapper。
    请添加图片描述

  3. 主要使用的是redisCacheManager。springboot可以使用配置文件生成缓存管理器

  4. 配置文件

spring: 
    cache:
    cache-names: # 如果由底层的缓存管理器支持创建,以逗号分割的列表来缓存名称
    caffeine:
      spec: # caffeine缓存配置细节
    couchbase:
      expiration: 0ms # couchbase缓存超时时间,默认是永不超时
    ehcache:
      config: # 配置ehcache的缓存初始化文件路径
    infinispan:
      config: # infinispan缓存配置文件
    jcache:
      config: # jcache缓存配置文件
      provider: # jcache缓存提供者配置
    redis:
      cache-null-values: # 是否允许redis缓存空值
      key-prefix: # redis的键的前缀
      time-to-live: 0ms #缓存超时时间戳,配置为0则不设置超时时间
      use-key-prefix: #是否启用redis的键前缀
    type: # 缓存类型,在默认的情况下,spring会自动根据上下文探测
  1. 这里只需要配置redis,只需要关注几个配置就可以
srping: 
    cache: 
        type: REDIS
        cache-names: redisCache

这样就完成了配置缓存管理器,type指的是缓存类型。为Redis,sprinboot会自动生成RedisCacheManager,而cache-name是缓存名称。多个名称用逗号分割,便于注解使用
6. 不要忘了在启动类上加@EnableCaching

缓存注解实例

  1. 首先介绍几个注解
  • @CachePut
    表示将方法结果返回存放到缓存中

  • @Cacheable
    先从缓存中通过定义的键查询,如果可以查询到数据,则返回,否则执行方法,将返回结果保存到缓存中

  • @CacheEvict
    通过定义的键移除缓存,有一个boolean的配置项beforeInvocation,表示在方法之前或者之后移除缓存,默认值是false,也就是方法之后移除缓存。

public class RedisCacheTest {



    @Transactional
    @CachePut(value = "redisCache",key = "'redis_user_'+#result.id")
    public User insert(User user) {
        return null;
    }


    @Transactional
    @Cacheable(value = "redisCache",key = "'redis_user_'+#id")
    public User getUser(Long id) {
        return null;
    }


    @Transactional
    @CachePut(value = "redisCache",condition = "#result!='null'",key = "'redis_user_'+#id")
    public User update(Long id,String username) {
        User user = this.getUser(id);
        if (user==null) {
            return null;
        }
        return null;
    }


    @Transactional
    public List<User> findUsers(String username,String note) {
        return null;
    }

    @Transactional
    @CacheEvict(value = "redisCache",key = "'redis_user_'+#id",beforeInvocation = false)
    public int delete(Long id) {
        return 0;
    }

}
  • 可以看到3个缓存中都配置了value=redisCache,这是因为我们配置了spring.cache.cache-name=redisCache,即对应的缓存名称是redisCache。键配置项是springEl表达式,'redis'+#id,#id表示参数,通过参数名称来匹配,要求方法存在一个参数且名称为id。

  • #result.id,是有时候我们想要使用返回结果的一些数据,比如insertUser,在插入之前是没有id的,mybatis回写之后才会有,于是就用result表示返回结果,id是其中的一个属性。

  • 可以看到update方法可能返回null,如果为null则不需要缓存任何数据,所以加入的condition配置,只有当返回结果不为空时再缓存。

  • 可以看到在update方法中我们先调用了getuser方法,因为不要依赖缓存,缓存存在脏数据的可能,比如数据库中数据已经改变,但是缓存中的数据没有变,就会出现数据不一致,因此需要先从数据库中查询数据。这里有一个误区,认为getUser上面有cache注解,就是从缓存中取的数据,不是的,因为缓存注解是依靠AOP代理实现的,而update方法调用insert方法是类内部方法的自调用,不存在代理对象的调用,也就不会使用缓存了。解决这个问题,可以拆分为两个service,service之间的调用可以看作是不同的类之间的调用,也就是AOP。或者直接从IOC容器中获取代理对象操作。

  • findUser没有使用缓存,这是因为查询的条件多样,使得缓存的命中率很低,不适合使用缓存。

缓存脏数据说明

  1. 两个连接对同一个数据进行操作,如果第二个连接将数据的key修改了,那么此时原来的key对应的值就是脏数据了。一般对于读操作,允许不是实时数据,比如排行榜刷新有一定延迟。但是脏数据不能一直存在,应该设置缓存的过期时间。对于实时性要求高的数据,需要将超时时间设置的更短。对于写操作,一般不轻信缓存的数据,优先考虑从数据库中读取数据。在更新数据,避免将缓存的脏数据写入数据库。

自定义缓存处理器

  1. 采取上面的配置,没有设置redis的超时时间。并且redis默认使用#{cacheName}:#{key}的形式作为键保存数据。有时我们想设置自定义的key或者,自定义超时时间,就需要自定义超时时间。
  2. 有两种方式,一种是通过增加配置文件项,另一种是完全自定义代码。
# 配置文件
spring: 
    cache:  
        redis:  
            use-key-prefix: false # 禁用前缀
            cache-null-value: true # 允许保存空值
            key-prefix:  # 自定义前缀
            time-to-live: 600000 # 超时时间,ms

自定义缓存管理器

@Configuration
public class RedisCacheManagerConfiguration {


    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean(name = "redisCacheManager")
    public RedisCacheManager initRedisCacheManage() {
//          redis加锁的写入器
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory);
//          启动redis缓存的默认配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//          设置jdk默认序列化器
        config = config.serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer())
        );
//          禁用前缀
        config = config.disableKeyPrefix();
//        设置10min超时
        config = config.entryTtl(Duration.ofMinutes(10));
//        创建redis缓存管理器
        RedisCacheManager redisCacheManager = new RedisCacheManager(writer, config);
        return redisCacheManager;
    }
}

springboot中的注解

spring的注解

依赖值(属性)注入有关注解

@value和@configurationproperties
  1. 详细教程在这里
  2. 这里用表格简单描述一下两者的区别
    | | @ConfigurationProperties | @value |
    | :-----| ----: | :----: |
    | 功能 | 批量注入配置文件中的属性 | 一个个指定 |
    | 松散绑定(松散语法) | 支持 | 不支持 |
    | SpEL | 不支持 | 支持 |
    | JSR303数据校验 | 支持 | 不支持 |
    | 复杂类型封装 | 支持 | 不支持 |
  3. 所谓松散语法也就是属性命名规则(Relaxed binding)
  • person.firstName:使用标准方式
  • person.first-name:大写用-
  • person.first_name:大写用_
  • PERSON_FIRST_NAME: 系统属性推荐使用这种写法
    也就是说,如果使用@Value注入,是不能像yml中springboot其他配置一样用-连接,而是只能用驼峰,而@ConfigurationProperties可以自动转换。
@configurationproperties的功能详解
  1. 详情看这里
  2. @enableConfigurationProperties只有在@Configuration中才能自动注入
  3. 注解中有数组形式的参数要用{},@AutoConfigurationAfter({ListPushInfo.class,hashtest.class})
@Value
  1. 类中的属性不要用@Value的注解进行拼接。因为@Value自动注入使用的是代理,这个时候类肯定已经实例化了,属性也实例化了,就会出现null指针
@Value
private String urlPrefix;

private String url = urlPrefix+"/test";
// 这样的话,url中的urlPrefix是null的。
  1. final不能和@Value一起使用。原因和上面一样。final的实例化时@Value还没有赋值。
指定配置文件加载的顺序
  1. 详情在这里这里
  2. 主要有四个注解
  • @AutoConfigureAfter
  • @AutoConfigureBefore
  • @AutoConfigureOrder
  • @Order
  1. 前两个是直接写在那个具体配置类的先后执行,后两个是配置优先级,控制粒度没有前两个好。
    @AutoConfigureAfter(LIstINfo.class)
  2. 如果在配置类中有依赖其他bean,spring非常智能,会优先加载依赖的bean
postConstruct
  1. 想要在类初始化的时候执行某些操作,需要用@PostConstruct注解,不要再构造方法中写自己的方法逻辑。因为这时候类中的某些依赖项还没初始化。除非在构造方法中先初始化。

热部署

  1. spring的devtools只是将页面和classpath进行了热部署(其实是快速重启)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值