Fork me on GitHub
AlbertWei's Blogs

Record the challenage met in work


  • 首页

  • 归档

自定义注解-入门

发表于 2018-04-27

自定义注解

一.自定义注解时常用的几个注解

1.@Target:

表明该注解可以申明的位置
例:value = {ElementType.FIELD, ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}
表明该注解可以生命到字段属性,类,方法,和参数上面

2.@Retention:表明注解加载的时机

例:RetentionPolicy.RUNTIME,表明在运行的时候可以加载,一般情况下都使用这个,
因为反射实现代码的时候是在程序运行时执行的。

3.@Inherited

表明被自定义的注解修饰的类在这个类的子类中也起作用
例:在FatherClass上声明了一个自定义注解,在SonClass上也继承了该注解


二.反射实现自定义注解的属性注入

@TestAnnotation(age = 12)
private Integer id;

@TestAnnotation(name = "lisi")
private String name;

例:要将12注入id中,lisi注入name中

//获得这个类的所有属性
Field[] declaredFields = testDemoClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
    //获得有自定义注解的所有字段  
    TestAnnotation annotation = declaredField.getAnnotation(TestAnnotation.class);
    if(annotation != null){
        //获得自定义注解中的属性值
        String name = annotation.name();
        int age = annotation.age();
        declaredField.setAccessible(true);
        //获得属性的类型
        Type genericType = declaredField.getGenericType();
        String type = genericType.toString();
        if (type.equals("class java.lang.Integer")){
            //给属性设置值
            declaredField.set(testDemo, age);
        }else if (type.equals("class java.lang.String")){
            declaredField.set(testDemo, name);
        }else {
            return;
        }
    }
}

三.自定义注解在方法上实现方法的形参注入

try {
    Class<TestDemo> testDemoClass = TestDemo.class;
    Method[] declaredMethods = testDemoClass.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
        TestAnnotation annotation = declaredMethod.getAnnotation(TestAnnotation.class);
        if (annotation != null){
            int parameterCount = declaredMethod.getParameterCount();
            if (parameterCount == 2){
                declaredMethod.invoke(testDemoClass.newInstance(),1, "2");
            }else {
                declaredMethod.invoke(testDemoClass.newInstance(), null);
            }
        }
    }
} catch (Exception e) {
e.printStackTrace();
}

PS:以上只是自定义注解中简单的使用,更深入的在自定义注解中定义Class,接下来在项目中深入了再更新

mysql优化

发表于 2018-03-16

Mysql优化


Mysql的数据库引擎:
5.5版本之前默认的存储引擎为MyISAM(性能快,但不支持事务处理,支持表级锁,不支持外键)
5.5版本之后使用InnoDB存储引擎(支持事务处理和行锁)

注:有雨MyISAM性能快,如果大量的使用查询语句可以使用其作为存储引擎,否则使用InnoDB

数据库的行锁:

在执行一条事务的时候数据库无法被另外一个事务给改变。

数据库事务的4种隔离级别:
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。
其他数据库默认的隔离级别为 Read commited
mysql数据库默认的隔离级别为 repeatable Read

数据库的隔离级别

① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

  ② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

  ③ Read committed (读已提交):可避免脏读的发生。

  ④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

脏读

指的是一个事务中读取了另外一个事务修改了但没有提交的数据

不可重复读

指的是一个事务在读取了一条数据时,另外一个事务修改了这个数据,再次读取的时候这条数据就不同了。
理论上来说不可重复读并不算是一个错误的情况,因为本来就应该读取到最后一条修改后的数据

幻读

其实和不可重复读是差不多的情况。同一个事务两次读取之间,会有另外一个事务参与进来修改了数据。
例如:查询10条姓名有冯的,然后修改了一条,现在理论上来说是9条,但是在再次查询之前,另外一个事务提交了一条姓名中包含冯字的。此时再次查询则还是有10条数据,就以为出现了幻觉 就是幻读。

sql语句优化:

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描

例:select id from t where num=10 or Name = ‘admin’
可以这样查询:

select id from t where num = 10
union all
select id from t where Name = ‘admin’

5.in 和 not in 也要慎用,否则会导致全表扫描 对于连续的数值,能用 between 就不要用 in 了

6.模糊查询会导致索引失效,必要时可以采用搜索引擎

7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描

8.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

9.任何地方都不要使用 select from t ,用具体的字段列表代替“”,不要返回用不到的任何字段

10.使用连接(JOIN)来代替子查询


数据库优化:

1.选取最适用的字段属性MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小

2.使用索引,外键,数据量太大的话可以考虑使用redis

3.以上都做了后,还是慢,就做主从复制或主主复制,读写分离,可以在应用层做,效率高,也可以用三方工具,第三方工具推荐360的atlas,其它的要么效率不高,要么没人维护;

Mysql的事务处理

数据库的事务特性

1.原子性:要么全部成功,要么全部失败

2.一致性:比如存取钱,钱的总数一定是一致的

3.隔离性:在并发条件下,两次事务之间是需要隔离的

4.持久性:当一次事务提交了后,这条数据就被持久化了。

Pattern实现正则用法

发表于 2018-03-15

Pattern实现正则用法

Pattern类和Matcher类

//定义正则匹配的规则 例:匹配三个数字
Pattern pattern = Pattern.compile(“[\d]{3}”);
//定义要匹配的字符串
Matcher matcher = pattern.matcher(“9879a13a456”);


Matcher类api的使用

//完全匹配返回true
boolean matches = matcher.matches();
//局部匹配返回true,并将匹配的字符串缩小到第一次匹配之后
boolean find = matcher.find();
//查询到再次匹配时的起始索引,这里就已经排除了已经find的
//使用了find方法之后,start方法相当于indexOf的作用 获取匹配到的索引,若没有匹配到会抛出异常
int start = matcher.start();
//重置匹配的索引,变为0
matcher.reset();
//得到匹配的值
String group = matcher.group();
//从第一个字符开始局部匹配
boolean lookingAt = matcher.lookingAt();

一些简单的正则匹配规则

https://github.com/fengshiweiit/RegexDemo

ElasticSearch入门

发表于 2018-02-26

ElasticSearch入门


一、ElasticSearch结构和传统数据库对比

Database ———-> index 索引
Table ———-> Type 类型
Row ———-> Document 文档
Column ———-> Field 字段
Schema ———-> Mapping 映射
SQL ———-> Query DSL 查询语法
Select * ———-> GET http: 查询
Update ———-> PUT http: 更新,插入
Delete ———-> Delete 删除

Docker入门

发表于 2018-01-30

Dcoker入门

Docker 基本命令

docker run                         启动一个容器
docker search                      搜索容器
docker pull                        从dockerHub下载镜像
docker images                      列出docker所有的镜像
docker ps                          列出所有正在运行的容器
docker ps -a                       列出所有的容器
docker ps -l                       列出最新创建的容器
docker stop                        停止一个容器
docker kill                        强制停止一个容器
docker restart                     重新启动一个容器
docker attach                      进入一个容器
docker rmi                         删除镜像
docker rm                          删除容器
docker rm $(docker ps -a -q)       删除所有容器
docker push                        将容器发到dockerHub
sudo docker exec -it containerId /bin/bash    进入容器
dokcer logs                        查看容器的日志信息
待补充。。。。。。。

1.搜索并下载一个镜像

docker search java 搜索java镜像
docker pull java:1.8(默认下载最新的镜像,添加:1.8指定版本)

2.启动一个镜像(容器)

docker run XXX (注:遇到了容器状态为exited状态的,其实是此容器执行已经结束了)
例:docker run echo "aaa" 这样输出一个字符串  状态为exited
若想将一个服务挂到docker下,状态为up
例:docker run -d nginx (将nginx在后台启动)   

3.使用DockerFile构建Docker镜像

DockerFile其实就是一个描述了Docker若干条指令的文本文件
构建镜像:docker build -t 仓库名称/镜像名称 Dockerfile相对位置
启动镜像:docker run -d -p xx:xx 仓库名称/镜像名称
遇到的问题:
    1.启动失败:容器的状态为EXIT,看Dockerfile中jar的配置路径,容器要在后台运行 -d

4.使用maven构建docker镜像

<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.4.13</version>
    <configuration>
        <imageName>demo/provider</imageName>
        //指定Dockerfile的文件路径
        <dockerDirectory>${basedir}/src/main/docker</dockerDirectory> 
        <resources>
            <resource>
                <targetPath>/</targetPath>
                <directory>${project.build.directory}</directory>
                <include>${project.build.finalName}</include>
            </resource>
        </resources>
    </configuration>
</plugin>
mvn clean package docker:build

5.Docker compose 编排微服务(暂时理解的作用为可以同时启动多个容器)

1.centos7 下安装docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose  
2.设置权限
chmod -x /usr/local/bin/docker-compose

6.遇到的bug总结

1.maven构建docker时主机连接异常:docker的配置文件中没有暴露端口号
添加:ExecStart=/usr/bin/docker daemon --tls=false -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375   ps:docker老版本的配置文件在default/docker下 , 新版本的配置文件在/lib/systemd/system/docker.service下(卡了2天!)
2.maven构建的时候找不到jar的路径,配置pom文件中的resource
<resources>
    <resource>
        <targetPath>/</targetPath>
        <directory>${project.build.directory}</directory>
        <include>${project.build.finalName}.jar</include>
    </resource>
</resources>
3.Dockerfile中暴露的端口号要和jar包中的一致!

RabbitMQ

发表于 2018-01-25

RabbitMQ基础


1.RabbitMQ

rabbitMQ是基于amqp协议的一种消息队列。

2.Amqp和jms(kafka,activeMQ)的区别

1.通信机制的区别
JMS:消息生产者和消息消费者必须知道对方的Queue
AMQP: 消息生产者和消息消费者无须知道对方的Queue,消息生产者将Exchange通过Route key和任意Queue绑定。消息消费者通过Route key从任意Queue中获取Exchange.
2.消息传输机制的区别
JMS:只支持点对点的通信模式和订阅广播的通信模式。
Amqp:有4中交换机分别代表着不同的通信模式
    1.Direct直连交换机:直连交换机是一种带路由功能的交互机,一个队列通过routing_key与一个交换机绑定,当消息被发送的时候,需要指定一个routing_key,这个消息被送达交换机的时候,就会被交换机送到指定的队列里面去。同样一个routing_key也是支持应用到多个队列中的,当一个交换机绑定多个队列时,消息就会被送到对应的队列去处理。
    2.Topic主题交换机:直连交换机的routing_key方案非常简单,如果我们希望一条消息发送给多个队列,那么这个交换机需要绑定上非常多的routing_key,假设每个交换机上都绑定一堆的routing_key连接到各个队列上。那么消息队列的管理就会异常的困难所以RabbitMQ提供了一种主题交换机,发送到主题交换机上的消息需要携带指定规则的routing_key,主题交换机会根据这个规则将数据发送到对应的(多个)队列上。
    3.Fanout扇形交换机:扇形交换机是最基本的交换机类型,它做的事情很简单--广播信息。Fanout交换机会把接收到的消息全部转发到绑定的队列上。因为广播不需要“思考”,所以Fanout交换机是四种交换机中速度最快的
    4.Header交换机:设置header attribute参数类型的交换机。(基本不用)

3.springBoot整合RabbitMQ

1.添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.代码实现

生产者:
rabbitTemplate.convertAndSend(“exchange1”,”key12”, 1234);
消费者:
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = “rabbit2” , durable = “true”) ,
exchange = @Exchange(value = “exchange1” , type = “direct” , durable = “true”) ,key = “key12”))
public void aaa(String message){
System.out.println(message);
}

RabbitMQ遇到的bug总结

1.启动失败,由于已经起了一个名为exchange的交换机,类型为topic,再次启动一个名为exchange的交换机,类型为direct。则启动报错ShutdownSignalException: channel error。
解决方式:暂时是去rabbit客户端关闭了类型为topic名为exchange的交换机(127.0.0.1:15672)


注:queue本质上是和exchange没有任何关系的(一旦创建了队列,是不会消失的!!遇到好多坑。。)

Summarize Jpa

发表于 2018-01-24

Jpa中遇到的知识点总结


1.jpa实现limit的方法(自己定义一个pageable)

final int currentPage = 0;
final int pageSize = 5000;
Sort sort = new Sort(Sort.Direction.DESC, "createTime");
Pageable pageable = new PageRequest(currentPage, pageSize, sort);
Page<Order> page = orderRepository.findAll(spec, pageable);
List<Order> list = page.getContent();

2.jpa实现limit的方法2

//这里必须加by
例:userRepository.findTop10By();

3.jpa的一级缓存

//jpa的查询方法生效后 默认会缓存这个查询结果
for (Integer organizationId : listMap.keySet()) {
        OrganizationManager organizationManager = organizationManagerRepository.findById(organizationId);
        if (organizationManager == null){
            log.error("组织不存在");
            throw new MessageException("组织不存在");
        }
        //这里会循环查询到resources
        List<Resource> resources = userOrganizationManagerRoleRepository.findResourceByUserIdAndOrganizationId(userId, organizationId);
        //清一级缓存
        entityManager.clear();
        List<Resource> list = buildTree(resources);
        //对resource进行了操作,如果不清一级缓存会导致查询的结果还是上一次循环的结果
        map1.put(organizationId + organizationManager.getType(), list);
}

SpringCloud

发表于 2018-01-23

SpringCloud入门


服务注册中心Eureka


引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
配置文件(此处用的是properties)
server:
  port: 1888
eureka:
  client:
    //设置不使用客户度,只用eureka提供服务注册中心
    register-with-eureka: false
    fetch-registry: false
    serviceUrl:
    defaultZone: http://localhost:1888/eureka/

服务提供者provider


引入依赖
同上,配置eureka的maven依赖
配置文件
server:
    port: 1990
//配置注册中心的url,将此服务注册到eureka
eureka:
    client:
        serviceUrl:
            defaultZone: http://127.0.0.1:1888/eureka/
//配置服务的名称
spring:
    application:
        name: provider

服务消费者consumer


引入依赖
同上,配置eureka的maven依赖
配置文件
server:
    port: 1990
//配置注册中心的url,将此服务注册到eureka
eureka:
    client:
        serviceUrl:
            defaultZone: http://127.0.0.1:1888/eureka/
//配置服务的名称
spring:
    application:
        name: consumer

ribbon实现负载均衡


添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
设置restTemplate的负载均衡
@Bean
@LoadBalanced // 注解开启均衡负载能力
public RestTemplate restTemplate(){
    return new RestTemplate();
}
配置文件(配置连接和处理超时)
ribbon:
    //连接超时
    ConnectTimeout: 10000
    //处理超时
    ReadTimeout: 30000

feign默认实现了负载均衡和断路器功能(这里我理解为整合了ribbon,hystrix和restTemplate)


添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
feign调用微服务
//配置需要调用的微服务的名称,配置断路器熔断后的默认方法
@FeignClient(value = "provider", fallback = HystrixDemo.class)
public interface FeignClientDemo {
    //配置调用的服务的方法
    @RequestMapping(method = RequestMethod.GET,value = "/plus")
    void plus();
}

//配置熔断的方法
public class HystrixDemo implements FeignClientDemo{
    @Override
    public void plus() {
        System.out.println(999);
        return;
    }
}
配置文件
feign:
    hystrix:
        enabled: true

配置swagger


添加依赖(注:这里如果是2.2以下的版本好像是会出空指针异常的)
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.7.0</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.7.0</version>
</dependency>
swagger配置文件
@Bean
public Docket buildDocket(){
    ParameterBuilder tokenPar = new ParameterBuilder();
    //配置除了参数以外的参数,这里是配置header
    tokenPar.name("X-CURRENT-USER-ID")
            .description("用户id").
            modelRef(new ModelRef("string"))
            .parameterType("header").required(false).build();
    List<Parameter> aParameters = new ArrayList<Parameter>();
    aParameters.add(tokenPar.build());
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            //配置扫描包
            .apis(RequestHandlerSelectors.basePackage("com.provider.provider.controller"))
            .paths(PathSelectors.any())
            .build()
            //添加设置的参数
            .globalOperationParameters(aParameters)
}

Ps:bootstrap.yml在application.yml之前加载
具体配置demo:https://github.com/fengshiweiit/springCloudStudy

POI导出bug总结

发表于 2018-01-23

项目中遇到的bug总结


1.基于反射的poi导出

获得order中的所有属性
Field[] fields = orderClass.getDeclaredFields();
List<String> fieldsName = Arrays.stream(fields).map(Field::getName).collect(toList());
创建行和列,将传来的类型和当前属性进行对比,反射得到属性
String firstLetter = titleType[n].substring(0, 1).toUpperCase();
String methodName = "get" + firstLetter + titleType[n].substring(1);
Method method = orderClass.getDeclaredMethod(methodName);
如果是date格式的需要特殊转换
CellStyle cellStyle = sxssfWorkbook.createCellStyle();
DataFormat format = sxssfWorkbook.createDataFormat();
cellStyle.setDataFormat(format.getFormat("yyyy-mm-dd hh时MM分ss秒"));
cell.setCellStyle(cellStyle);
sheet.autoSizeColumn(n, true);
若传来的为对象里的子对象,根据a得到子对象,根据b得到子对象的属性(归队格式为a.b,后面根据.进行切割)
Method method = orderClass.getDeclaredMethod(methodName);
Order currentOrder = list.get(i);
Field field = orderClass.getDeclaredField(attr1);
Class<?> type = field.getType();
Type genericType = field.getGenericType();
此处为List集合,取集合的泛型,根据属性b反射得到属性
Class genericClazz = (Class)pt.getActualTypeArguments()[0];
Method declaredMethod = genericClazz.getDeclaredMethod(methodName2);
Object invoke1 = declaredMethod.invoke(invoke.get(0));
导出
File file = new File(excelName);
OutputStream outputStream = new FileOutputStream(file);
sxssfWorkbook.write(outputStream);
oss上传,由于上传的方法在另外一个微服务中,所以要跨服务调用
FileSystemResource fileSystemResource = new FileSystemResource(excelName);
String url = CList.ServiceUrl.DOCUMENT_SERVICE_URL + "document/uploadAndSave";
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("multipart/form-data");
headers.setContentType(type);
headers.add("X-CURRENT-USER-ID", String.valueOf(currentUserId));
MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
form.add("file", fileSystemResource);
form.add("scheduleId",condition.get("scheduleId"));
HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(form, headers);
restTemplate.postForEntity(url, formEntity, Result.class);

遇到的问题

1.生产端接收的类为multipart,跨服务调用的时候不能使用multipart的实现类进行传递,否则会出
找不到multipart类型的异常(使用FileSystemResource)。
2.项目中springboot启动类中设置了文件转换类型messageConvert为fastJson,导致传递文件的时候文件丢失。需要加上一个文件类型的转换器
@Bean
public RestTemplate restTemplate(
    HttpMessageConverters fastJsonHttpMessageConverters) {
    HttpMessageConverter converter = new FormHttpMessageConverter();
    List<HttpMessageConverter<?>> converterList = new ArrayList<>();
    converterList.add(converter);
    converterList.addAll(fastJsonHttpMessageConverters.getConverters());
    RestTemplate restTemplate = new RestTemplate(converterList);

    return restTemplate;
}
3.反射只能得到本类的属性。继承的baseEntity的属性得不到,这里的做法是直接把父类的
属性重写了一遍(巨low,反射有方法可以获得父类的属性)。

线程池原理初探

发表于 2017-12-24

线程池原理初探

线程池的优势

再高并发的情况下如果不使用线程池的话会不断的创建线程,销毁线程,这样对内存的消耗会很大。使用线程池的话,再一个任务使用完线程之后不会立即销毁线程。

线程池原理

一.首先了解一下线程池初始化时的几个重要参数:
    1.corePoolSize:核心线程数
    2.maximumPoolSize:最大线程数
    3.keepAliveTime: 表示线程多长时间没有执行任务的话会销毁
    4.unit:keepAliveTime的单位
    5.workQueue:等待队列(缓存队列),用来储存等待的队列
    6.threadFactory: 线程工厂,用来创建线程
    7.handler:表示拒绝处理任务时的策略,默认时抛异常(所以再创建线程池的时候一般都要try catch)
二.线程池工作原理
    线程池初始化的时候会初始以上参数,当有任务进来的时候就会给其创建线程,当线程数大于核心线程数但小于最大线程数的时候,会将线程存入缓存队列,当缓存队列中的线程堆积满的时候,会继续创建新的线程直到线程数达到最大线程数。当线程数达到最大线程数时,再有任务进来则不会创建新的线程,而是调用handler中的拒绝处理任务的策略(默认策略为抛出异常)。
PS:如果想要控制并发量的情况最好使用Excutors.newFixedThreadPool,如果队并发量不确定的情况下可以使用Executors.newCachedThreadPool
12
AlbertWei

AlbertWei

Record the challenage met in work

12 日志
友情链接
  • xiongyufeng的博客
© 2018 AlbertWei
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4