近期学习总结

此篇总结下自己所学的知识。

1.使用AOP对controller层的异常进行统一处理

   代码如下:

 

@ControllerAdvice
public class ControllerExceptionHandler {


    @ExceptionHandler({Exception.class})
    public Map handleException(Exception ex) {
 
        Map<String, Object> resultMap = new HashMap();
        resultMap.put("code", 500);
        resultMap.put("message", "发生了内部异常,请查看相关日志.");
        return resultMap;
    }

}

使用@ControllerAdvice和@ExceptionHandler就可以对controller 捕获的异常进行统一处理了,当然是可以指定包名的。

上面的示例代码是没有打印日志的,这个日志肯定是要加的。

 

2.对集合按照某个字段进行排序

  如果我们查询数据库得到了一个集合,需要按照集合的某个字段进行排序,在数据库使用order by不能满足需求。、

  那么我们可以在集合上实现Comparable接口,实现compareTo方法,然后使用Collections.sort 排序就可以了

  示例代码如下:

  

@Data
public class StudentVo implements Comparable<StudentVo> {

    private String name;

    private int age;

    private String address;

    @Override
    public int compareTo(StudentVo studentVo) {
        return this.age-studentVo.age;
    }
}
 

测试代码如下:

  @Test
    public void testStudentVoComparable() {

        List<StudentVo> studentVoList = new ArrayList<>();
        StudentVo studentVo1 = new StudentVo();
        studentVo1.setName("张三");
        studentVo1.setAge(20);
        studentVoList.add(studentVo1);

        StudentVo studentVo2 = new StudentVo();
        studentVo2.setName("李四");
        studentVo2.setAge(25);
        studentVoList.add(studentVo2);

        StudentVo studentVo3 = new StudentVo();
        studentVo3.setName("王二");
        studentVo3.setAge(15);
        studentVoList.add(studentVo3);

        Collections.sort(studentVoList);
        for (StudentVo studentVo : studentVoList) {
            System.out.println("年龄:"+studentVo.getAge());
        }
    }

 

排序结果:

年龄:15
年龄:20
年龄:25

 

3.使用动态代理简单的实现一个AOP

spring AOP 原理是由动态代理实现的,可以说是对方法进行了增强,但是因为使用了反射,所以也需要考虑到反射的开销。

Spring AOP有很多注解,诸如:@Before、@After、@Around等。

这里我们来写代码来模拟下。

思路: 首先我们先写一个接口里面有一个方法,然后写一个实现类去实现这个方法,随便打印一段话。

然后写一个Before 和After的接口,这里为了简便就不写成注解了。

再写一个代理工厂,工厂的构造方法有参数 1.需要代理的类 2.before接口 3.after接口

然后写具体的代理方法,直接使用Proxy.newProxyInstance。这个方法有三个参数 ClassLoader、Class<?>[],invocationHandler.

第一个参数是this.getClass().getClassLoader() 。第二个是 this.target.getClass().getInterfaces()。

第三个直接new InvocationHandler(){重写invoke方法}。

代码如下:

public interface AfterAdvice {

    void after();

}

public interface BeforeAdvice {

    void before() ;

}


public interface People {

    void say();
}

public class ChinesePeople implements People {
    @Override
    public void say() {
        System.out.println("我说中文。");
    }
}


public class ProxyFactory {

    private Object target;
    private BeforeAdvice beforeAdvice;
    private AfterAdvice afterAdvice;
    
    public Object createProxy() {

        ClassLoader loader = this.getClass().getClassLoader();

        Class<?>[] interfaces = this.target.getClass().getInterfaces();
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {

                if (beforeAdvice != null) {
                    beforeAdvice.before();
                }

                Object targetObject = method.invoke(target,args);

                afterAdvice.after();

                return targetObject;

            }
        };

        Object object = Proxy.newProxyInstance(loader,interfaces,handler);

        return object;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public BeforeAdvice getBeforeAdvice() {
        return beforeAdvice;
    }

    public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
        this.beforeAdvice = beforeAdvice;
    }

    public AfterAdvice getAfterAdvice() {
        return afterAdvice;
    }

    public void setAfterAdvice(AfterAdvice afterAdvice) {
        this.afterAdvice = afterAdvice;
    }
}


public class ProxyDemo {



    public void demo() {

        ProxyFactory proxyFactory = new ProxyFactory();

        proxyFactory.setTarget(new ChinesePeople());

        BeforeAdvice beforeAdvice = new BeforeAdvice() {
            @Override
            public void before() {
                System.out.println("before ...");
            }

        };

        AfterAdvice afterAdvice = new AfterAdvice() {
            @Override
            public void after() {
                System.out.println("after ...");
            }
        };
        proxyFactory.setBeforeAdvice(beforeAdvice);
        proxyFactory.setAfterAdvice(afterAdvice);

        People people = (People) proxyFactory.createProxy();

        people.say();

    }
}

 

4. 使用MQ 和反射来异步处理数据

    需求是这样的, controller层会调用service层,serivce层会调用dao层来插入或者更新数据,像这种情况,如果频繁的调用这种请求,会对数据库是一种不小的压力,所以希望使用MQ来达到异步处理数据、削峰的目的。

  思路是这样的。controller层不去调用service层,而调用MQ的生产者,将数据以JSON的形式存储MQ里面,数据包括类名,方法名,入参,入参类名。如果有多个入参的时候,可以组装成一个map 或者弄一个VO去存储。

消费者就获取数据,然后通过反射来调用方法,来插入或者更新数据。

核心代码如下:

@Data
public class MQEventVo<T extends BaseVo> {

    //类名
    private String className;

    //方法名
    private String methodName;

    private Class<T> methodClass;

    //vo入参
    private T vo;

    public MQEventVo(String className,
                     String methodName,
                     T vo,
                     Class<T> methodClass) {
        this.className = className;
        this.methodName = methodName;
        this.vo = vo;
        this.methodClass = methodClass;
    }


}


    /**
     * 点对点消费用户队列 并利用反射执行service方法
     *
     * @param message 要处理的消息
     */
    @JmsListener(destination = QUEUE_USER, containerFactory = QUEUE_LISTENER_CONTAINER_FACTORY)
    public <T extends BaseVo>void handlerQueue(String message) {
        log.info("MQCustomer start to handler queue message:" + message);
        try {
            JSONObject jsonObject = JSONObject.parseObject(message);
            String className = jsonObject.getString("className");
            String methodName = jsonObject.getString("methodName");
            String methodClassStr = jsonObject.getString("methodClass");
            Class methodClass = Class.forName(methodClassStr);
            JSONObject voObject = (JSONObject) jsonObject.get("vo");
            T insertData = (T)JSON.toJavaObject(voObject,methodClass);
            Method mh = ReflectionUtils.findMethod(SpringContextUtil.getBean(className).getClass(),
                    methodName, methodClass);
            ReflectionUtils.invokeMethod(mh, SpringContextUtil.getBean(className), insertData);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }



 

ActiveMQ的配置如下:


package cdm.mq.demo.config;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Topic;


@Configuration
public class ActiveMQConfig {

    public static final String TOPIC_USER = "user.topic";

    public static final String QUEUE_USER = "useR.queue";

    public static final String QUEUE_LISTENER_CONTAINER_FACTORY = "queueListenerContainerFactory";

    public static final String TOPIC_LISTENER_CONTAINER_FACTORY = "topicListenerContainerFactory";

    //消息队列的1对多模式
    @Bean
    public Topic topic() {
        return new ActiveMQTopic(TOPIC_USER);
    }

    //消息队列的点对点模式
    @Bean
    public Queue queue() {
        return new ActiveMQQueue(QUEUE_USER);
    }

    @Value("${spring.activemq.broker-url}")
    private String host;

    @Bean
    public ConnectionFactory getActiveMqConnection() {
        return new ActiveMQConnectionFactory(host);
    }

    @Bean(name = QUEUE_LISTENER_CONTAINER_FACTORY)
    public JmsListenerContainerFactory queueListenerContailerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(false);
        return factory;
    }

    @Bean(name = TOPIC_LISTENER_CONTAINER_FACTORY)
    public JmsListenerContainerFactory topicListenerContainerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(true);
        return factory;
    }

}

 

application.yml文件如下


spring:
  datasource:
    url: jdbc:mysql://localhost:3306/master0?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

  activemq:
    broker-url: tcp://localhost:61616
    user: admin
    password: admin


mybatis:
  mapper-locations: classpath*:mapper/*.xml


 

6. 使用策略模式来减少if else 详见如下:

核心就是实现ApplicationListener 

https://blog.csdn.net/zzti_erlie/article/details/102988486

 

7.使用CountDownLatch来处理线程之间的顺序执行问题

 需求是这样的,我现在有N个线程(N大于1),每个线程都有一个number,依次从小到大,现在要这些线程按照number的顺序

去依次输出自己的number。就可以用到计数器了。

在网上找到的示例感觉都不准确,所以这里以一道LeetCode来演示CountDownLatch是如何工作的。

package yc.leetcode;

import java.util.concurrent.CountDownLatch;

public class Problem1114 {

    public static void main(String[] args) {
        Foo  foo = new Problem1114.Foo();
        Thread t1 = new Thread(()->{
            try {
                foo.first(()->{
                    System.out.println("one");
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程1");

        Thread t2 = new Thread(()-> {
            try {
                foo.second(() -> {
                    System.out.println("two");
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程2");

        Thread t3 = new Thread(()-> {
            try {
                foo.third(() -> {
                    System.out.println("three");
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程3");

        t2.start();
        t3.start();
        t1.start();

    }


    private static class Foo {

        private CountDownLatch countDownLatch1;
        private CountDownLatch countDownLatch2;

        public Foo() {
            countDownLatch1 = new CountDownLatch(1);
            countDownLatch2 = new CountDownLatch(1);
        }

        public void first(Runnable printFirst) throws InterruptedException {

            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            countDownLatch1.countDown();
        }

        public void second(Runnable printSecond) throws InterruptedException {
            countDownLatch1.await();
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            countDownLatch2.countDown();
        }

        public void third(Runnable printThird) throws InterruptedException {
            countDownLatch2.await();
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }

}

 

8.ThreadLocal 为什么建议为static

ThreadLocal即为本地线程变量,它只存储当前线程的变量,对于当前线程来说,所以的对象都是共享的,设置为静态的,

可让当前线程直接就共享此静态变量。即当第一次加载的时候,就分配了一块存储空间,所以此类的对象都可以共享

操作此静态变量。

9.volatile

 volatile 使用在多线程中,多修饰变量,此变量直接保存在内存中,保证了此对象的可见性,对于一写多读,

是可以解决可见性问题的。但是对于多写多读是无法解决线程安全问题的。

如果是count++操作,可以使用原子类。比如AtomicLong或者AtomicInteger.

10. finally 中不要加return 因为加了return之后 就不会走 catch中的return了。

执行顺序是 先走try 然后走catch,走里面的return,只是不会返回数据 ,然后走finally,如果finally中有return 直接返回,程序结束。

11. 统计程序运算时间

  1.System.currentTimeMillis()

  2.System.nanoTime()

  3.

      Instant start = Instant.now();

      Instant end = Instant.now();

     long timeElapsed = Duration.between(start, finish).toMillis();

 4.使用工具类

   StopWatch watch = new StopWatch();

   watch.start();

   watch.stop();

 System.out.println("Time Elapsed: " + watch.getTime() + "ms");

 

12 线程池原理

ThreadPoolExecutor 首先是线程池的参数,核心线程数、最大线程数、空闲线程保持存活时间、

空闲线程等待时间单元、线程阻塞队列、拒绝策略、创建线程工厂。

一个任务进来后,会判断当前线程数是否大于等于核心线程数,没有则创建一个核心线程,如果有,则任务会进入到等待队列,如果被拒绝,并且当前线程数小于最大线程数,那么会创建新的线程,如果大于最大线程数,则进行执行拒绝策略。

当等待队列是无解队列时,这个时候的最大线程数是无效的。比如LinkedBlockingQueue.

workQueue任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。

ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
 

13.反射原理

1.将java文件保存到本地磁盘

2.编译生成class文件

3.JVM将class文件放入到JVM内存中去

4.使用反射,就可以通过class文件来获取到这个类的所有属性,方法

反射的三种方式,Person.class  Class.forName ,Person person = new Person() ;person.getClass();

 

14.

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值