我是靠谱客的博主 拼搏冰棍,最近开发中收集的这篇文章主要介绍springboot自动配置以及集成redis使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

对于springboot的使用起来非常的轻便简单,但是如果能够理清原理会对你的学习帮助很多,对于面试也肯定不会只让我们去讲如何使用它,面试官更喜欢听的还是你对底层原理的了解程度,因为底层很多的思路都是贯通的,话不多说,直接开干。

首先我们来实现一个小小需求,按条件去加载一个类进入ioc容器,比如当我们导入了jedis的jar包的时候,加载实体类User,如果不导入的话,加载失败,可以想一想?之所以摆出这个问题,是因为官方底层也是这么加载的。

  1. 创建一个实体类User
public class User {
}

2.创建配置类UserConfig

@Configuration
public class UserConfig {

   
    @Bean
    @MyConditionOnClass("redis.clients.jedis.Jedis")
    public User user(){
        return  new User();
    }
}

3.上面用到了自定义注解,所以我们创建一个注解@MyConditionOnClass

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface MyConditionOnClass {

    String[] value();
}

4.以上又使用到了ClassCondition类,所以我们创建出来,并实现对应的类。

public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> map = metadata.getAnnotationAttributes(MyConditionOnClass.class.getName());
        System.out.println(map);
        boolean flag = true;
        try {
            String[] strings =(String[]) map.get("value");
            for(String className : strings){
                Class.forName(className);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
}

5.最后测试,条件是是否导入jedis的jar包,来看ioc是否有User对象

 <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
</dependency>
@SpringBootApplication
public class AutoConfigureApplication {
    public static void main(String[] args) {
       //返回ioc容器,可以操作得到对应的bean对象
        ConfigurableApplicationContext context = SpringApplication.run(AutoConfigureApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);

    }
}

6.测试结果是,
当我们导入了以上的依赖jar包控制台打印了com.lzc.domain.User@17d238b1,成功获取到对象。
当我们拿掉依赖的时候,报错加载不到user对象,需求实现成功。

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'user' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:808)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1279)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
	at com.lzc.AutoConfigureApplication.main(AutoConfigureApplication.java:12)

总结:当我们在配置类上,把User对象加入ioc的时候,上面加了一个自定义注解,@MyConditionOnClass,而这个注解上面又有一个本地注解,@Conditional(ClassCondition.class),它引用了ClassCondition类,而这个类又实现了Condition,并覆写了方法matches,最主要的是我们要理清这里面的逻辑,它的意思是判断@MyConditionOnClass注解里面是否有属性,如果有就通过Class.forName()加载这个属性,最后做出判断,这就是本质。

@ConditionalOnBean:当容器里有指定的bean的条件下。 @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。 @ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。

接下来我们揭开springboot自动配置的面纱
首先我们点开我们的常用注解@SpringbootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

其中我们关注的是@EnableAutoConfiguration,点开

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

可以知道它是通过Import实现的,接下里我们看AutoConfigurationImportSelector,同样覆写SelectImports(),并调用了以下方法,这是关键。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,

这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
自动配置原理过程总结
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

接下来我们看一个具体的RedisAutoConfiguration,对以上有个深入的了解。
在这里插入图片描述
这就是上面说的经典组合,RedisAutoConfiguration和RedisProperties
,几乎都是这样实现的,当我们引入第三方jar包的时候,比如当我们引入mybatis继承springboot,底层也是MybatisAutoConfiguration,MybatisProperties
在这里插入图片描述
当然在META-INF下面spring.factories文件,和上面一样,只不过上面是默认大量的配置类,引入这个myabtis的时候,就会扫描对应的spring.factories文件,然后加入容器中,我们就可以使用。
接下来我们看看这两个类MybatisAutoConfiguration,MybatisProperties

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
```java
     @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

从上面可以看到用到了我们最开始将的其中一个@ConditionalOnMissingBean,现在大家应该知道了,都是按条件加载,其实很多的实现都是这样,还有一点上面使用了MybatisProperties,我们看看

@ConfigurationProperties(
    prefix = "mybatis"
)
public class MybatisProperties {
    public static final String MYBATIS_PREFIX = "mybatis";
    private String configLocation;
    private String[] mapperLocations;
    private String typeAliasesPackage;
    private String typeHandlersPackage;
    private boolean checkConfigLocation = false;
    private ExecutorType executorType;
    private Properties configurationProperties;

以上定义了一些属性,还有前缀mybatis,这样我们也能知道当我们编写配置文件application.properties或application.yml的时候,对应的前缀加上mybatis,就是从这个而来的。关于这个就介绍到这里,其他的大家可以去分别看看。

这里既然讲到了application.properties配置文件,我就简单的说一下他们怎么加载的。
spring加载配置文件是通过listener监视器实现的

application.properties和application.yml文件可以放在一下四个位置

外置,在相对于应用程序运行目录的/congfig子目录里。
外置,在应用程序运行的目录里
内置,在config包内
内置,在Classpath根目录
同样,这个列表按照优先级排序,也就是说,src/main/resources/config 下application.properties覆盖src/main/resources 下application.properties中相同的属性
此外,如果你在相同优先级位置同时有application.properties和application.yml,那么application.yml里面的属性就会覆盖application.properties里的属性。

集成Redis,实现缓存

  1. 导入依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.2.6.RELEASE</version>
 </dependency>

2.写配置类

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 选择redis作为默认缓存工具
     * @param redisConnectionFactory
     * @return
     */
    /*@Bean
    //springboot 1.xx
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        return rcm;
    }*/
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration).build();
    }

    /**
     * retemplate相关配置
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }

    /**
     * 对hash类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * 对redis字符串类型数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    /**
     * 对链表类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * 对无序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    /**
     * 对有序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }

3.使用

@Mapper
@CacheConfig(cacheNames = "users")
public interface UserMapper {
        

        @Insert("insert into user(name,age) values(#{name},#{age})")
        int addUser(@Param("name")String name,@Param("age")String age);

        @Select("select * from user where id =#{id}")
        @Cacheable(key ="#p0")
        User findById(@Param("id") String id);

        @CachePut(key = "#p0")
        @Update("update user set name=#{name} where id=#{id}")
        void updataById(@Param("id")String id,@Param("name")String name);

        //如果指定为 true,则方法调用后将立即清空所有缓存
        @CacheEvict(key ="#p0",allEntries=true)
        @Delete("delete from user where id=#{id}")
        void deleteById(@Param("id")String id);


        @Select("select * from user")
        @Cacheable(key = "#p0")
        List<User> findAll(String id);
    }

注解解释:
@EnableCaching 开启注解
@CacheConfig(cacheNames = “users”) 注解生成前缀
@Cacheable将查询结果缓存到redis中,(key="#p0")指定传入的第一个参数作为redis的key。
@CachePut,指定key,将更新的结果同步到redis中
@CacheEvict,指定key,删除缓存数据,allEntries=true,方法调用后将立即清除缓存
使用redis是主要因为它是基于内存的,相比mysql是基于硬盘,它的速度飞快,第一次查询的时候从mysql数据库中查询,然后存到redis中去,第二次查询的时候直接从缓存中拿,很好的提升性能,关于redis非关系型数据库在项目经常会使用,也是相当的重要,所以我们不要局限于mysql,多去接触其他更好的技术,今天就到此为止,有讲的不对的,还请指出,下期再见。

最后

以上就是拼搏冰棍为你收集整理的springboot自动配置以及集成redis使用的全部内容,希望文章能够帮你解决springboot自动配置以及集成redis使用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部