# springboot-mbyd **Repository Path**: mbyd/springboot-mbyd ## Basic Information - **Project Name**: springboot-mbyd - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2019-05-09 - **Last Updated**: 2023-11-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring boot 使用 #### 介绍 记录第一次搭框架,遇到好多坑,各种百度。。。 - ### 搭建spring boot框架 1 . pom.xml中添加springboot的依赖 ``` org.springframework.boot spring-boot-starter-parent 2.1.4.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin ``` 2 . 添加controller ``` @RestController public class UserController { @GetMapping("/hello")("/hello") public String hello(){ return "hello world"; } } ``` 3 . 添加启动类 ``` @SpringBootApplication public class BootStrap { public static void main(String[] args) { SpringApplication.run(BootStrap.class,args); } } ``` - ### spring boot + spring-data-jpa 1 . 添加依赖 ``` org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java 8.0.16 ``` 2 . 创建实体类 ``` @Entity @Table(name = "t_user") @Data public class User { //必须要指定一个@ID 否则会报错 No identifier specified for entity: domain.com.wy.User @Id private int id; private String name; private int age; } ``` 3 . 创建Repository继承JpaRepository绑定实体类 ``` public interface UserRepository extends JpaRepository { } ``` 4 . 在application.properties中添加数据库配置 ``` spring.datasource.url=jdbc:mysql://localhost:3306/mytestdb spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ``` 5 . 在controller中直接注入就可以使用了 ``` @Autowired private UserRepository userRepository; @GetMapping("/listjpa") public List findAll(){ return userRepository.findAll(); } ``` - ### jpa 添加分页 1 . 继承PagingAndSortingRepository ``` public interface UserRepository extends PagingAndSortingRepository { } ``` 2 . 添加处理信息 ``` @Service public class UserService { @Autowired private UserRepository userRepository; public Page findByPage(int pageNo, int pageSize){ PageRequest request = this.buildPageRequest(pageNo,pageSize); Page sourceCodes= this.userRepository.findAll(request); return sourceCodes; } //构建PageRequest private PageRequest buildPageRequest(int pageNumber, int pagzSize) { return PageRequest.of(pageNumber - 1, pagzSize); } } ``` 3 . controller中添加分页信息 @GetMapping("/findbypagejpa") public org.springframework.data.domain.Page findByPageJpa(@RequestParam(value = "pageNo", defaultValue = "1") int pageNo, @RequestParam(value = "pageSize",defaultValue = "2") int pageSize){ org.springframework.data.domain.Page byPage = userService.findByPage(pageNo, pageSize); return byPage; } - ### spring boot + mybatis 1 . 添加依赖 ``` org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.0 mysql mysql-connector-java 8.0.16 ``` 2 . 创建mapper ``` public interface UserMapper { @Select("select * from t_user") List findAll(); } ``` 3 . 在启动类中添加mapper扫描路径 ``` @SpringBootApplication @MapperScan("boottest.mybatisrepo") public class BootStrap { public static void main(String[] args) { SpringApplication.run(BootStrap.class,args); } } ``` - ### spring boot + mybatis 添加分页插件 1 . 添加依赖 ``` com.github.pagehelper pagehelper-spring-boot-starter 1.2.10 ``` 2 . 在mapper添加分页信息 ``` @Select("select * from t_user") Page findByPage(); ``` 3 . 在调用的时候传入页码信息 ``` @GetMapping("/findbypage") public PageInfo findByPage(@RequestParam(value = "pageNo", defaultValue = "1") int pageNo, @RequestParam(value = "pageSize",defaultValue = "2") int pageSize){ PageHelper.startPage(pageNo, pageSize); Page byPage = mapper.findByPage(); PageInfo pageInfo = new PageInfo<>(byPage); return pageInfo; } ``` - ### 添加 AOP ``` @Aspect @Component @Slf4j public class HttpAspect { //定义切点 @Pointcut("execution(public * boottest.controller..*(..))") public void log(){} @Before("log()") public void before(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //url log.info("url={}", request.getRequestURI()); //method log.info("method={}", request.getMethod()); //ip log.info("ip={}", request.getRemoteAddr()); //method log.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); //param log.info("args={}", joinPoint.getArgs()); } //returning="obj" 将返回结果绑定给参数。returning 和 参数名称必须一致 @AfterReturning(returning = "obj",pointcut = "log()") public void doAfterReturn(Object obj){ log.info("返回结果----{}",obj); } } ``` - ### 使用spring boot 自动配置 1 . XXConfig.java ``` /** * 配置文件的存放位置有特殊要求!!!有以下三种方式: * 1.放到application.properties中 * 2.放到application-xxx.properties中,需要在application.properties中激活xxx。spring.profiles.active=xxx * 3.放到自定义的properties中。需要加上@PropertySource("classpath:application-config.properties")来指定加载路径 */ @Component @Data //@PropertySource("classpath:application-config.properties") @ConfigurationProperties(prefix = "people") public class XXConfig { private String name; private int age; } ``` 2 . application-xxx.properties ``` people.name=xiaomei people.age=18 ``` 3 . application.properties ``` spring.profiles.active=xxx ``` - ### 自定义拦截器 1 . 自定义注解设置拦截路径和忽略的路径,以及触发条件等 ``` @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Interceptor { String[] pathPatterns() default {}; String[] excludePatterns() default {}; int order() default 0; //下面的未实现 Class ignore() default Ignore.class; /** * @return 系统配置 */ String condition() default ""; /** * 测试处理器 * * @return ConditionHanlder */ Class conditionHandler() default DefaultHandler.class; } ``` 2 . 解析注解 ``` /** * 解析注解 * 1.通过实现ApplicationContextAware接口得到applicationContext * 2.从applicationContext中得到含有Interceptor注解的bean * 3.实现WebMvcConfigurer接口,根据注解中的参数添加拦截器。 * 4.根据注解中的参数进行拦截器的排序 * 5.如果有ignore参数,自定义拦截器,判断是否需要拦截如果不需要拦截,则放掉。 */ public class InterceptHanlder implements WebMvcConfigurer, ApplicationContextAware { private ApplicationContext context; @Override public void addInterceptors(InterceptorRegistry registry) { String[] beanNames = context.getBeanNamesForAnnotation(Interceptor.class); List interceptors = Arrays.stream(beanNames).map(bean -> context .getBean(bean, HandlerInterceptorAdapter.class)).sorted((o1, o2) -> { Interceptor c1 = o1.getClass().getAnnotation(Interceptor.class); Interceptor c2 = o2.getClass().getAnnotation(Interceptor.class); return c1.order() - c2.order(); }).collect(Collectors.toList()); interceptors.forEach(interceptor->{ InterceptorRegistration registration = registry.addInterceptor(interceptor); Interceptor annotation = interceptor.getClass().getAnnotation(Interceptor.class); registration.addPathPatterns(annotation.pathPatterns()); registration.excludePathPatterns(annotation.excludePatterns()); }); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } } ``` 3 . 在META-INF中添加spring.factories,实现spring自动配置 ``` # Auto Configure 自动配置放到META-INF/spring.factories中 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ interceptor.com.wy.InterceptHanlder ``` - ### spring boot + redis 1 . 添加依赖 ``` org.springframework.boot spring-boot-starter-data-redis ``` 2 . 在application.properties中添加redis的配置 ``` spring.redis.host=127.0.0.1 spring.redis.port=6379 ``` 3 . 在程序中注入RedisTemplate就可以使用了 ``` @Autowired private RedisTemplate redisTemplate; public void save(String key,String value){ redisTemplate.boundValueOps(key).set(value); } public String get(String key){ return (String) redisTemplate.boundValueOps(key).get(); } ``` 4 . 以上是使用spring的配置,还可以自定义配置 4.1 在application.properties中添加属性 ``` spring.redis.keyprefix=springboot-test ``` 4.2 添加属性配置文件 ``` @Data @ConfigurationProperties("spring.redis") public class RedisProperties { private String host; private int port; private String keyprefix; } ``` 4.3 添加配置文件 ``` @Configuration @Import(RedisProperties.class) public class RedisConfig { @Bean @ConditionalOnMissingBean public RedisTemplate redisTemplate( RedisConnectionFactory redisConnectionFactory, final RedisProperties redisProperties) { RedisTemplate template = new RedisTemplate<>(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); template.setValueSerializer(jackson2JsonRedisSerializer); template.setKeySerializer(new StringPrefixRedisSerializer(Charset.forName("UTF8"), redisProperties.getKeyprefix())); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory, final RedisProperties redisProperties) { StringRedisTemplate template = new StringRedisTemplate(); template.setKeySerializer(new StringPrefixRedisSerializer(Charset.forName("UTF8"), redisProperties.getKeyprefix())); template.setConnectionFactory(redisConnectionFactory); return template; } } ``` 4.4 添加Key序列化文件 ``` public class StringPrefixRedisSerializer extends StringRedisSerializer { private String prefix; public StringPrefixRedisSerializer(Charset charset, String prefix) { super(charset); this.prefix = prefix; } public String deserialize(@Nullable byte[] bytes) { String tempKey = super.deserialize(bytes); if(!StringUtils.isEmpty(tempKey) && tempKey.startsWith(prefix) ){ tempKey = tempKey.substring(prefix.length() + 1); } return tempKey; } public byte[] serialize(@Nullable String key) { return super.serialize(StringUtils.isEmpty(prefix) ? key : prefix + "." + key); } } ``` 5 . 使用Cacheable 5 . 1 添加缓存管理器配置 ``` //缓存管理器 @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解决乱码的问题) RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() // .entryTtl(timeToLive) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } ``` 5 . 2 添加注解@Cacheable value 表示将结果缓存在某个缓存上 key 表示缓存的key ``` @Cacheable(key = "methodName",value = "users") public List findAll(){ System.out.println("请求来了====="); return userMapper.findAll(); } ``` 使用key属性自定义key key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。 自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。 ``` @Cacheable(value="users", key="#id") public User find(Integer id) { returnnull; } @Cacheable(value="users", key="#p0") public User find(Integer id) { returnnull; } @Cacheable(value="users", key="#user.id") public User find(User user) { returnnull; } @Cacheable(value="users", key="#p0.id") public User find(User user) { returnnull; } ``` 除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。 属性名称 | 描述 | 示例 ------------- | -------------------------| ----------------------- | methodName | 当前方法名 | #root.methodName | method | 当前方法 | #root.method.name | target | 当前被调用的对象 | #root.target | targetClass | 当前被调用的对象的class | #root.targetClass | args | 当前方法参数组成的数组 | #root.args[0] | caches | 当前被调用的方法使用的Cache | #root.caches[0].name | condition属性指定发生的条件 有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。 其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。 ``` @Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0") public User find(User user) { System.out.println("find user by user " + user); return user; } ``` 5 . 3 @CacheEvict @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。 @CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的 语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key, 如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。 下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。 - allEntries属性 allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时, Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。 ``` @CacheEvict(value="users", allEntries=true) public void delete(Integer id) { System.out.println("delete user by id: " + id); } ``` - beforeInvocation属性 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。 使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法 之前清除缓存中的指定元素。 ``` @CacheEvict(value="users", beforeInvocation=true) public void delete(Integer id) { System.out.println("delete user by id: " + id); } ``` - ### spring boot + mongo 1 . 添加依赖 ``` org.springframework.boot spring-boot-starter-data-mongodb ``` 2 . 在application.properties中添加配置 ``` spring.data.mongodb.host=127.0.0.1 spring.data.mongodb.port=27017 spring.data.mongodb.database=spring-mongo-test ``` 3 . 在使用的地方注入MongoTemplate就可以使用了。 ``` @Service public class MongoUtil { @Autowired private MongoTemplate mongoTemplate; public void save(User user){ mongoTemplate.save(user); } public User findByUserName(String name){ return mongoTemplate.findOne(Query.query(Criteria.where("name").is(name)), User.class); } } ``` 4 . 使用GridFS 4 . 1 添加配置类 ``` @Configuration public class MongoConfig { @Autowired private MongoDbFactory mongoDbFactory; @Bean public GridFSBucket gridFSBucket(){ MongoDatabase db = mongoDbFactory.getDb(); return GridFSBuckets.create(db); } } ``` 4 . 2 注入 GridFsTemplate 和 GridFSBucket 就可以使用了 ``` @Service public class MongoUtil { @Autowired private MongoTemplate mongoTemplate; @Autowired private GridFsTemplate gridFsTemplate; @Autowired private GridFSBucket gridFSBucket; public void save(User user){ mongoTemplate.save(user); } public User findByUserName(String name){ return mongoTemplate.findOne(Query.query(Criteria.where("name").is(name)), User.class); } public void store(InputStream inputStream,String filename){ gridFsTemplate.store(inputStream,filename); } public byte[] find(String filename){ GridFSFile fsFile = gridFsTemplate.findOne(Query.query(Criteria.where("filename").is(filename)); GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(fsFile.getObjectId()); GridFsResource resource = new GridFsResource(fsFile,downloadStream); try { InputStream inputStream = resource.getInputStream(); return IOUtils.toByteArray(inputStream); } catch (IOException e) { e.printStackTrace(); } return null; } public void delete(){ mongoTemplate.dropCollection("fs.chunks"); mongoTemplate.dropCollection("fs.files"); } } ``` - ### spring boot + schedule 1 . 在启动类添加注解 @EnableScheduling ``` @SpringBootApplication @EnableCaching @EnableScheduling @MapperScan("boottest.mybatisrepo") public class BootStrap { public static void main(String[] args) { SpringApplication.run(BootStrap.class,args); } } ``` 2 . 在要执行的定时任务上添加注解 @Scheduled ``` @Component public class MyTask { @Scheduled(fixedDelay = 1000) public void task1(){ System.out.println(Thread.currentThread().getName()+" task1----"); } } @Scheduled的各个属性含义 1.cron: 以 unix 的 cron 的方式定义 job, 如 "0 * * * * NON-FRI" 2.fixedRate: 每次任务启动时的间隔时间,fixedRateString,意义是一样,只是可以通过外部来定义,如 fixedRateString = "${job1.fixed.rate}" 3.fixedDelay: 上次任务结束后间隔多少时间再启动下一次任务,这样避免前一个任务尚未结束又启动下一个任务,fixedDelayString 类似 fixedRateString 4.intialDelay: 程序启动后至任务首次执行时的间隔时间,针对 fixedRate(fixedRateString), fixedDelay(fixedDelayString) 5.zone: 给 cron 表达式用的时区 以上采用的都是毫秒。 ``` 到此就可以直接使用了,但是使用的是一个线程来执行定时任务的。 如果想要多个线程跑,需要手动提供一个线程池配置类。 3 . 配置线程池,采用多线程执行定时任务 ``` @Configuration public class ScheduleCofig { @Bean(name="task-pool") public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(20); return taskScheduler; } } ``` 可通过指定bean的名字来命名线程池名称 - ### spring boot + elasticsearch 1.添加依赖 ``` org.springframework.boot spring-boot-starter-data-elasticsearch ``` 2.在配置文件中添加配置项 ``` spring.data.elasticsearch.cluster-name=elasticsearch_admin spring.data.elasticsearch.cluster-nodes=172.16.1.123:9300 ``` 3.添加配置config ``` @Configuration public class ElasticSearchConfig { /** * 防止netty的bug * java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4] */ @PostConstruct void init() { System.setProperty("es.set.netty.runtime.available.processors", "false"); } } ``` 4.继承ElasticsearchRepository ``` public interface BaseInfoInterface extends ElasticsearchRepository { List findByUsernameLike(String name); } ``` 5.在service注入repo实现业务逻辑 详见 ElasticSearchService