java编程高阶技巧

SpringBoot&Spring

1、发布监听模式>>>发布消息,所有监听者都可以执行

适用于类似消息发布,监听
1、创建event类继承org.springframework.context.ApplicationEvent
2、创建listener类实现接口org.springframework.context.ApplicationListener传入泛形为上面定义的event,并声明为组件
3、applicationContext.publish(new event),使所订阅者都可以收到事件执行
4、未来扩展方式:只需要新增实现event的listener即可自动订阅

代码示例

1、创建event

public class ImsgEvent extends ApplicationEvent{
    
    
    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public ImsgEvent(Object source) {
    
    
        super(source);
    }

}

2、创建实现event的监听并声明为组件

package com.example.springBootTest.eventTest;

import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class WeChatListener implements ApplicationListener<ImsgEvent> {
    
    

    @Override
    public void onApplicationEvent(@NotNull ImsgEvent event) {
    
    
        System.out.println("微信发送成功");
        System.out.println(event.getSource().toString());
    }
}

package com.example.springBootTest.eventTest;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MsgListener implements ApplicationListener<ImsgEvent> {
    
    


    @Override
    public void onApplicationEvent(ImsgEvent event) {
    
    
        System.out.println("短信发送成功");
    }
}


3、发布事件

package com.example.springBootTest.eventTest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;

@Service
public class orderServce {
    
    

    public orderServce(ApplicationContext ctx ) {
    
    
        System.out.println("订单已生成");
        String msg = "order created";
        ctx.publishEvent(new ImsgEvent(msg));
    }
}

4、显示结果

2020-08-28 10:37:40.441  INFO 4320 --- [           main] 
订单已生成
短信发送成功
微信发送成功
order created
2020-08-28 10:37:40.628  INFO 4320 --- [           main] 

2、策略注入模式>>>特定特征调用不同执行类

适用于类似区分不同用户执行不同取价,用户身份未来可能会新增的情况
1、编写strategy接口
2、创建实现strategy接口的具体执行类比如*strategy{type:},@server 注入到bean中
3、通过构造注入的方式将所有strategy实现类获取,根据type封装到map中
4、通过map.get(Type)获取到具体实现的strategy,执行对应方法获取价格
5、未来扩展方式:只需要实现对应的strategy接口,书名类中type类型即可

代码示例

1、定义策略接口

package com.example.springBootTest.testStrategy;

public interface IpriceStrategy {
    
    

    double calDisCount(double price);

    String getuserTpye();


}

2、实现具体接口,注入到容器中

package com.example.springBootTest.testStrategy;

import org.springframework.stereotype.Service;

@Service
public class NormalStrategyService implements IpriceStrategy{
    
    
    @Override
    public double calDisCount(double price) {
    
    
        return price;
    }

    @Override
    public String getuserTpye() {
    
    
        return "normal";
    }
}

package com.example.springBootTest.testStrategy;

import org.springframework.stereotype.Service;

@Service
public class VippriceStrategyService implements IpriceStrategy {
    
    
    @Override
    public double calDisCount(double price) {
    
    
        return price*0.9;
    }

    @Override
    public String getuserTpye() {
    
    
        return "vip";
    }
}

package com.example.springBootTest.testStrategy;

import org.springframework.stereotype.Service;

@Service
public class VVippriceStrategyService implements IpriceStrategy {
    
    
    @Override
    public double calDisCount(double price) {
    
    
        return price*0.8;
    }

    @Override
    public String getuserTpye() {
    
    
        return "vvip";
    }
}

3&4 获取策略集合,注入后调用

package com.example.springBootTest.testStrategy;

import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CalPriceService {
    
    

   Map<String,IpriceStrategy> map ;


    public CalPriceService(List<IpriceStrategy> list) {
    
    
        System.out.println("calPriceService");
        Map<String,IpriceStrategy> map = new HashMap<String,IpriceStrategy>();
        for (IpriceStrategy ipriceStrategy : list) {
    
    
            map.put(ipriceStrategy.getuserTpye(),ipriceStrategy);
        }
        System.out.println(map.get("vip").calDisCount(100));
        System.out.println( map.get("vvip").calDisCount(100));
        System.out.println(map.get("normal").calDisCount(100));
    }
}

5、显示结果

calPriceService
90.0
80.0
100.0

3、模板编码>>>jdbc执行,bio执行,需要前后控制(try-finally)的代码(类似AOP,但是不需要写切面类)

1、方法中定义方法,实现使用虚类
2、使用时直接重写虚类代码就可以

4、AOP方法>>>适用于事务,jdbc等,前后添加代码

1、引入AOP依赖
2、编辑AOP切面类,设置切面方法和实现方法
3、主类开启aop

代码示例

1、引入aop依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、编辑AOP切面类,设置切面方法和实现方法,此处使用注解表达

package com.example.springBootTest.testAOP;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class testAOPAspect {
    
    

    @Around("@annotation(aopAnnotion)")
    public Object testAop(ProceedingJoinPoint joinPoint) {
    
    
        System.out.println("我是通过around在你之前执行");
        Object proceed = new Object();
        try {
    
    
            Object[] args = joinPoint.getArgs();
            System.out.println("我知道了你的参数"+args[0].toString());
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
    
    
            throwable.printStackTrace();
        }
        System.out.println("我是通过around在你之后执行");

        return proceed;
    }

}

3、启用AOP

@EnableAspectJAutoProxy//开启AOP

4、主类测试和获取springApplicationContext的工具类

package com.example.springBootTest.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringApplicationContextUtil implements ApplicationContextAware {
    
    
    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
    
    
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        if (SpringApplicationContextUtil.applicationContext == null) {
    
    
            SpringApplicationContextUtil.applicationContext = applicationContext;
        }
    }

    public static Object getBean(String name) {
    
    
        return getApplicationContext().getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
    
    
        return getApplicationContext().getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> clazz) {
    
    
        return getApplicationContext().getBean(name, clazz);
    }


}
package com.example.springBootTest;

import com.example.springBootTest.testAOP.AopTestServer;
import com.example.springBootTest.util.SpringApplicationContextUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy//开启AOP
public class SpringBootTestApplication {
    
    


    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringBootTestApplication.class, args);
        ApplicationContext context = SpringApplicationContextUtil.getApplicationContext();
        AopTestServer bean = context.getBean(AopTestServer.class);
        bean.doServer("我是传入参数");
    }

}

5、测试结果

我被构造了

我是通过around在你之前执行
我知道了你的参数我是传入参数
我被执行了我是传入参数
我是通过around在你之后执行

注意:构造server时,调用和被调用的方法不会被aop切入到

示例:

package com.example.springBootTest.testAOP;

import org.springframework.stereotype.Service;

@Service
public class AopTestServer {
    
    


    public AopTestServer() {
    
    
        System.out.println("我被构造了");
        doServer("我是构造中被调用时传入的参数");
    }

    @aopAnnotion
    public void doServer(String msg) {
    
    
        System.out.println("我被执行了,传入参数是:"+msg);
    }

}

结果:

我被构造了
我被执行了,传入参数是:我是构造中被调用时传入的参数
2020-08-28 19:37:22.819  INFO 5796 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-28 19:37:22.967  INFO 5796 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-08-28 19:37:22.975  INFO 5796 --- [           main] c.e.s.SpringBootTestApplication          : Started SpringBootTestApplication in 1.961 seconds (JVM running for 2.757)
我是通过around在你之前执行
我知道了你的参数我是传入参数
我被执行了,传入参数是:我是传入参数
我是通过around在你之后执行

5、多线程 >>>callable&future 生产者消费者模式

callable和runable的区别
1、callable有返回值
2、callable会抛出异常
3、结合futureTask使用,futureTask中run方法里面会调用callable.call
4、futureTask的get()方法中,会停滞当前线程,直到有返回值结果返回

代码示例

1、 基础service代码,用于返回用户信息

package com.example.springBootTest.threadTest.service;


import org.springframework.stereotype.Service;

import java.util.Random;

@Service
public class threadTestService {
    
    


    public String getUserPic() throws InterruptedException {
    
    
        Thread.sleep(1000);
        return "照片";
    }

    public String getUserName() throws InterruptedException {
    
    
        Thread.sleep(1500);
        return "张大大";
    }
}

2、测试类_测试不使用线程

    @Autowired
    threadTestService threadTestService;

    @Test
    void contextLoads() {
    
    
        long timeInMillis = System.currentTimeMillis();
        try {
    
    
            String userName = threadTestService.getUserName();
            System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userName is "+userName);
            String userPic = threadTestService.getUserPic();
            System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userPic is "+userPic);

            System.out.println("返回值是"+userName+userPic);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

返回值

i got userName in 1501ms,userName is 张大大
i got userName in 2502ms,userPic is 照片
返回值是张大大照片

说明前后串联执行,总共耗时为加法

3、使用runnable

    @Test
    void contextLoadsTread() {
    
    
        long timeInMillis = System.currentTimeMillis();
        AtomicReference<String> userName =new AtomicReference<>();
        AtomicReference<String> userPic = new AtomicReference<>();
        new Thread(()-> {
    
    
                try {
    
    
                     userName.set(threadTestService.getUserName());
                    System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userName is "+userName);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }).start();

            new Thread(()->{
    
    
                try {
    
    
                    userPic.set(threadTestService.getUserPic());
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userPic is "+userPic);
            }).start();

//        try {
    
    
//            Thread.sleep(5000);
//        } catch ( InterruptedException e ) {
    
    
//            e.printStackTrace();
//        }
        System.out.println("返回值是"+userName.get()+ userPic.get());


    }

返回信息

返回值是nullnull

此处因为线程还未返回,主测试线程已经测试结束,所以直接忽视了线程打印结果,现在这里主线程等待5s后再观察控制台

等待5s后返回信息

i got userName in 1001ms,userPic is 照片
i got userName in 1501ms,userName is 张大大
返回值是张大大照片

结果与预期相同,但是整体执行时间为主线程等待的5s后,理论上可以不需要等待5s,但是此处难以把控具体时间

4、使用callable和futureTask

 @Test
    void threadCallableTest(){
    
    
        long timeInMillis = System.currentTimeMillis();
        Callable userNameCall = new Callable() {
    
    
            @Override
            public Object call() throws Exception {
    
    
                String userName = threadTestService.getUserName();
                System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userName is "+userName);
                return userName;
            }
        };
        Callable  userPicCall = new Callable() {
    
    
            @Override
            public String call() throws Exception {
    
    
                String userPic = threadTestService.getUserPic();
                System.out.println("i got userName in " + String.valueOf(System.currentTimeMillis()-timeInMillis) + "ms,userPic is "+userPic);
                return userPic;
            }
        };

        FutureTask userNameTask = new FutureTask(userNameCall);
        new Thread(userNameTask).start();

        FutureTask userPicTask = new FutureTask(userPicCall);
        new Thread(userPicTask).start();

        try {
    
    
            System.out.println("返回值是"+userNameTask.get()+ userPicTask.get());
        } catch ( InterruptedException e ) {
    
    
            e.printStackTrace();
        } catch ( ExecutionException e ) {
    
    
            e.printStackTrace();
        }
    }

返回结果

在这里插入图片描述
用时1.729ms,也就是说基本在数据计算完成后,即返回结果,且不会因主线程执行导致返回信息错误

callable和futureTask阻塞原理

源代码中,callable是个只有call()方法的接口,就不看了
主要看furureTask类,其中的get()方法:

    public V get() throws InterruptedException, ExecutionException {
    
    
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

其中get方法会判断当前执行状态,如果没完成,会进入java.util.concurrent.FutureTask#awaitDone方法停滞,并等待返回状态
具体停滞方式为:

/**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
    
    
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
    
    
            if (Thread.interrupted()) {
    
    
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
    
    
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
    
    
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
    
    
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

在for(;;)中持续循环,等待状态完成后返回完成状态或异常状态
之后在上面代码看到的report中根据完成状态返回全局变量记录的返回结果

猜你喜欢

转载自blog.csdn.net/gsh6022/article/details/108270256