2018.10.08
1.自定义事件,一般是继承ApplicationEvent抽象类
2.定义事件监听器,一般是实现ApplicationListener接口
3.配置监听器,启动的时候,需要把监听器加入到Spring容器中
4.发布事件,使用ApplicationContext.publishEvent发布事件
配置监听器
1.SpringApplication.addListeners 添加监听器
2.把监听器纳入到spring容器中管理 @Component
3.使用context.listener.classes配置项配置---写入application.properties
4.使用注解@EventListener,在方法上面加入,且该类需要纳入到spring容器中管理
2018.10.09
ApplicationContextInitializer 接口是在spring容器执行refreshed之前的一个回调
使用步骤:
1.写一个类,实现ApplicationContextInitializer接口
2.注册ApplicationContextInitializer
注册方法:
1.SpringApplication.addInitializers
2.在配置文件中加入 context.Initializer.classes=com.hikvision.xxxxxx
3.可以通过META-INF/spring.factories机制(listener也适用)
---------------------------
CommandLineRunner 接口是在容器启动成功之后的最后一步回调
使用步骤:
1.写一个类,实现CommandLineRunner接口
2.把该类纳入到spring容器的管理之中
有很多个CommandLineRunner的时候,先执行@Order(值) 值比较小的,不写值则排最后
注意:可以使用注解@Order或者Ordered接口来控制执行顺序
CommandLineRunner的参数是最原始的参数,没有做任何处理
ApplicationRunner的参数是ApplicationArguments,是对原始参数做了进一步封装
-----------------------------
@SpringBootApplication默认扫描的包的路径是当前包以及子包下面的所有类
可以通过scanBasePackages=""修改这个扫描包的路径
@SpringBootApplication(scanBasePackages = "com.zhanghan.demo",exclude = MyConfig.class)
可以通过exclude = xxx.class 来排除指定的类
或使用excludeName = "com.zhanghan.demo.MyConfig" 根据classname
-----------------------------
app.setBannerMode(Banner.Mode.OFF);
可以取消这个大图标
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
可以在resources目录下新建一个banner.txt,系统会自动取代开机动画
也可以在application.properties中指定 banner.location=my.txt
banner.image.location=my.jpg
-----------------------------
springboot运行流程
1.判断是否是web环境
2.加载所有classpath下的META-INFO/spring.factories ApplicationContextInitializer
3.加载所有classpath下的META-INFO/spring.factories ApplicationListener
4.推断main方法所在的类
5.开始执行run方法
6.设置java.awt.headless系统变量
7.加载所有classpath下的META-INFO/spring.factories SpringApplicationRunListener
8.执行所有SpringApplicationRunListener的started方法
9.实例化ApplicationArguments对象
10.创建environment
11.配置environment,主要是把run方法的参数配置到environment
12.执行所有SpringApplicationRunListener的environmentprepared方法
13.如果不是web环境,但是是web的Environment,则把这个Environment转换成标准的Environment
14.输出banner
15.初始化applicationContext,如果是web环境,则实例化AnnotationConfigEmbeddedWebApplicationContext对象,否则则实例化AnnotationConfigApplicationContext
------------------------------------
2018.10.10
application.properties 中可设置
server.port=8081
server.contextPath=/ 这样就是服务器下的路径
在Controller里,参数接收可以用@RequestParam(value="username",defaultValue="admin",required=false) 表示不是必须
@GetMapping("/display/{fuck}")
@ResponseBody
public String display(@PathVariable("fuck") String fuck){
return fuck;
}
如上图所示,这样就可以获取url中的变量
@RestController=@Controller+@ResponseBody
--------------------------
application.properties 中可设置
spring.mvc.view.prefix=/123 即是相对于webapp的路径
spring.mvc.view.suffix=.jsp (可为空) springboot默认不支持jsp
在springboot中使用jsp,需要另外加入tomcat-embed-jasper的依赖,且不能用@RestController
--------------------------
freemarker模板
也是直接return String
需要先把jsp的依赖去掉,且不能用@RestController
在pom.xml加入依赖
默认在classpath下的templates
也可以在配置文件中 spring.freemarker.templateLoaderPath=classpath:/xxx (可用逗号隔开)
默认扩展名.ftl
---------------------------
更换内置服务器tomcat为jetty
先在pom中的starter-web后加入
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
再加入jetty的依赖
---------------------------
springboot如何处理静态资源
1.src/main/webapp目录下,就可以直接访问
2.默认的静态资源路径是 /META-INFO/resources/
/resources/
/static/
/public/
四个都是
--------------------------
servlet 的Filter
先implements Filter
重写doFilter方法
在类上加@WebFilter("/*")
在启动类上加入@ServletComponentScan
--------------------------
springboot中使用拦截器
1.写一个拦截器,实现HandlerInterceptor接口
2.写一个类,继承WebMvcConfigurerAdapter,然后重写addInterceptor方法,并使用registry.addInterceptorfangfa
===============preHandle===============Controller执行之前调用
===============postHandle===============Controller执行之后,页面渲染之前
===============afterCompletion===============一切结束后,一般用于资源清理操作
---------------------------
装配DataSource
1.加入数据库驱动
2.配置文件
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/syn_management
spring.datasource.username=ddl_mgr
spring.datasource.password=hik12345#
#######spring.datasource.type 可以指定具体使用哪种数据源(一共四种:DataSourceConfiguration.枚举)
----------------------------
使用阿里druid
加入maven依赖
写一个类,@SpringBootConfiguration
@ConfigurationProperties(prefix="jdbc.mysql")
private String url;
private String username;
private String password;
@Bean
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);// 用户名
dataSource.setPassword(password);// 密码
return dataSource;
}
----------------------------------
事务(只生效于public)
在启动类上加入@EnableTransactionManagement
然后在事务类上加入@Transactional(rollbackFor = Exception.class,noRollbackFor=xxxx.class)
即可
---------------------
AOP
开发流程
1.加入相应依赖 spring-boot-starter-aop
2.写一个Aspect,封装横切关注点(日志、监控等),需要配置通知(前置通知,后置通知)和切入点(那些包哪些类哪些方法等等)
3.这个aspect需要纳入spring容器管理,加入@Aspect
---------------------------------
springboot微服务
@Transactional注解应用于测试类的方法上的时候,只帮你测通而不会插入真实数据(回滚)。
---------------------------------
@JsonInclude(JsonInclude.Include.NON_NULL)
在model类里面加入,这样为null的字段就不返回了
当然,还可以不加注解,用全局配置:
jackson:
default-property-inclusion: non_null
//TODO 不安全的做法,改进
这种注释后,可以直接在intellij的控制台找到留下的地方
----------------------------------
使用RestTemplate 进行http请求
String url = "https://www.baidu.com";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url,String.class);
//这里代表是返回一个json字符串
先引入freemarker
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
在controller里返回一个ModelAndView
return new ModelAndView("pay/create")
系统会自动去找templates下的路径 .ftl结尾文件
public ModelAndView create(@RequestParam("orderId")String orderId,
@RequestParam("returnUrl") String returnUrl,
Map<String,Object> map){
//1.查询订单
OrderDTO orderDTO = orderService.findById(orderId);
if(null==orderDTO){
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//发起支付
PayResponse payResponse = payService.create(orderDTO);
map.put("orderId","111111");
return new ModelAndView("pay/create",map);
}
其中map中的属性会注入到模板文件里,用${orderId} 去取得即可
如果是个对象,用${orderId.appId}
-----------------------------------
全局异常处理
写一个类,用@ControllerAdvice修饰
写一个方法,用@ExceptionHandler(value=xxxx.class)修饰
拦截后就可以return new ModelAndView("redirect:.......");
------------------------------------
websocket再看一遍
------------------------------------
springboot优雅使用redis缓存
@RestController
@RequestMapping("/api/")
public class aaController {
@RequestMapping("aaa")
@Cacheable( cacheNames="product",key="1234")
public String fuck() {
System.out.println("111111111");
return "2";
}
@RequestMapping("fuckyou")
@CachePut( cacheNames="product",key="1234")
public String fuckyou() {//注意了,这里cacheput的话,必须返回值一样,返回后会加入缓存
System.out.println("111111111");
return "3";
}
@RequestMapping("fuckyou")
@CacheEvict( cacheNames="product",key="1234")
public void qinghuancun() {
System.out.println("111111111");//这里与返回值无关,执行完就会清缓存
}
}
---------------------
lambda表达式学习
Optional<Integer> optional = stream.filter(i->i<10).findAny(); 这里返回了一个optional 此时离结果已经很近了
sout(optional.orElse(-1)); 这里代表将结果返回,如果没有结果,那么返回-1,这样会避免空指针异常
经常性的stream和option用完转化为普通变量时,又想进行操作,则可以使用Option.of(xxxx) Stream.of(xxxx) 则可以继续操作了。
list.stream().collect(Collectors.groupingBy(Dish::getType));会返回一个map,key就是type,value就是符合的list中的元素
对应重载的方法:list.stream().collect(Collectors.groupingBy(Dish::getType),Collectors.counting())
这样key是type,value就是各项数量。非常强大
对应重载的方法:list.stream().collect(Collectors.groupingBy(Dish::getType),Collectors.averageingInt(Dish::getCalories))
集合转各种map:
list.stream().collect(Collectors.toConcurrentMap(Dish::getName,Dish::getCarlories)); key就是name,value就是carlories
集合转各种类型集合
list.stream().collect(Collectors.toCollection(LinkedList::new))
------------------------\
intStream.parallel().filter().sequential().map().parallel().forEach();
这种并不是先转成并行再转串行再转并行,而是一个flag,以最后的flag的值为准
-------------------------
completableFuture.whenComplete((v,t)->{})
这是事件执行完后执行的回调函数,v是返回值,t是异常。
一般而言completableFuture不是new出来的,而是接收一个supplier(也就是返回你需要东西的supplier)
一段经典的高并发多线程查询:
public static void main(String[] args){
//取得非守护线程池
ExecutorService executor = Executors.newFixedThreadPool(2,r->{
Thread t = new Thread(r);
t.setDaemon(false);
return t;
});
List<Integer> productionIDs = Arrays.asList(1,2,3,4,5);//假设前端传来五个productId
productionIDs.stream().map(i->CompletableFuture.supplyAsync(()->queryProduction(i),executor))//此处模拟按照id一次一次查询数据库
.map(future->future.thenApply(a->a*100)) //模拟对取出来的数据进行操作
.map(CompletableFuture::join).collect(Collectors.toList());//这里用join方法等待future跑完,然后汇集
}
---------------------
thenApply(a->axxxxxx) 传入Function, handle((v,t)->vxxxxxx)比thenApply 多了一个异常处理
whenComplete Consumer
thenRun 可以在complete之后执行的Consumer,注意,这里前面的值不会传进去
汪文君需要把最后那俩api再看看