第一章 主要特性 DI
依赖注入.
让Spring容器来管理对象的创建和销毁,注入;依赖关系更为明确,耦合程度降低. 以前是bean工厂,现在是各种ApplicationContext容器.
AOP
切面.
声明切点,注入before,after事件,降低代码耦合.
1 2 3 4 5 6 7 8 9 10 11 12 <aop:config > <aop:aspect ref ="minstrel" > <aop:pointcut id ="embark" expression ="execution(* *.embarkOnQuest(..))" /> <aop:before pointcut-ref ="embark" method ="singBeforeQuest" /> <aop:after pointcut-ref ="embark" method ="singAfterQuest" /> </aop:aspect > </aop:config >
模板 1 2 jdbcTemplate+RowMapper, 简化jdbc查询. // 用JPA的话就不用写RowMapper了.
bean的生命周期
实例化
填充属性
setBeanName
setBeanFactory
setApplicationContext
调用BeanPostProcessor的预初始化方法
调用InitializingBean的afterProperties set方法
调用自定义的初始化方法
调用BeanPostProcessor的初始化后方法
—— bean的使用
—— 容器关闭
调用DisposableBean的destroy方法
调用自定义的销毁方法
第二章 DI 讲了一下声明和使用Bean的方法. 与Springboot实战相同,略过.
第三章 条件化配置\高级装配 各种profile切换,条件bean,配置注入等等,与Springboot实战相同,略过.
第四章 AOP 具体实现机制:
Spring 采用jdk动态代理模式来实现Aop机制。Spring AOP采用动态代理过程:1.将切面使用动态代理的方式动态织入到目标对象,形成一个代理对象。2.目标对象如果没有实现代理接口,那么spring会采用CGLib来生成代理对象,该代理对象是目标对象的子类。3.目标对象如果是final类,也没有实现接口,就不能运用AOP
AspectJ是AOP的一个实现,Spring借鉴了AspectJ.(有合作)
术语
Advice : 通知(增强)(Before,After,Around)
描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)
PointCut: 切点(例子中的切点是execution表达式规定的方法)
指定在哪些类的哪些方法上织入横切逻辑.
JoinPoint: 连接点 (粒度是方法)
Advisor(切面):
将Pointcut和Advice两者组装起来。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了
例子:
1 2 3 4 5 6 7 8 9 10 11 12 <aop:config > <aop:aspect ref ="minstrel" > <aop:pointcut id ="embark" expression ="execution(* *.embarkOnQuest(..))" /> <aop:before pointcut-ref ="embark" method ="singBeforeQuest" /> <aop:after pointcut-ref ="embark" method ="singAfterQuest" /> </aop:aspect > </aop:config >
spring支持的AspectJ切点表达式(挺多的记不住,就不列全了):1 2 3 4 5 arg() this target execution ...
用切点表达式来选取某个方法示例:
1 2 3 4 5 execution(* concert.Performance.perform(..))
简单总结一下步骤(用注解):
写一个config,把观众和演员都注册为Bean.加上@EnableAspectJAutoProxy
.
观众类上加@Aspect
,写各种@Before,@After方法;
代码见:https://github.com/xiaoyue26/spring-gradle/tree/master/src/main/java/com/xiaoyue/nov/practice/concert
书里还提供了一个思路,通过AOP扩展原来的类,而不改动原来的源代码.
原理总结
动态代理
JDK动态代理
:只能为接口创建动态代理实例,而不能针对类 。CGLib
: (Code GenerationLibrary)动态代理:可以为任何类创建织入横切逻辑代理对象,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。
原理对比:(尽量用接口,不用final)
JDK动态代理:(只能是接口)
JDK动态代理技术。通过需要代理的目标类的getClass().getInterfaces()方法获取到接口信息(这里实际上是使用了Java反射技术。getClass()和getInterfaces()函数都在Class类中,Class对象描述的是一个正在运行期间的Java对象的类和接口信息),通过读取这些代理接口信息生成一个实现了代理接口的动态代理Class(动态生成代理类的字节码),然后通过反射机制获得动态代理类的构造函数,并利用该构造函数生成该Class的实例对象(InvokeHandler作为构造函数的入参传递进去),在调用具体方法前调用InvokeHandler来处理。
第二部分 WEB 第五章 构建 第六第七章 SpringMVC 内容,比较繁琐
第八章 Spring Web Flow stackoverflow上有个帖子详细解释了为啥不能用这个框架.https://stackoverflow.com/questions/29750720/what-are-the-spring-web-flow-advantages 包括:
xml配置难用;
不直观;
与第三方库(如模板引擎)整合复杂; … // 看了前三个缺点就没耐心看后几个缺点了… 核心还是难用,比普通spring的xml配置复杂得多,几乎没有人能坚持看十分钟攻略.
第九章 安全 Spring Security 原理:
用的filter. (AOP用的interpretor)
antMatchers用的是 ant pattern,和普通的通配符有点不一样,是专门用于url的通配符:
1 2 3 ? 匹配任何单字符 * 匹配0或者任意数量的字符 ** 匹配0或者更多的目录
其实就是:
1 2 * 匹配任何字符,不含 “/” ** 匹配任何字符,含 “/”
匹配上路径以后,配置能够做什么.
1 2 3 4 5 6 7 8 9 10 11 12 13 access("hasRole('READER')" ) 设置SpEL表达式为true 时允许访问; anonymous() 允许匿名访问 authenticated() 允许认证过的用户访问 denyAll() 拒绝所有 fullyAuthenticated() 允许认证过的,但排除remember-me的 hasAnyAuthority(String...) 具备某个权限 hasAnyRole(String ...) 具有某个Role hasAuthority(String) 某有某个权限 hasRole(String) 具有某个Role hasIpAddress(String) 具有某个IP not() 对其他访问方法的结果取反 permitAll() 允许所有 rememberMe() 允许通过Remember-me的.
最后的default条件就是 anyRequest().denyAll()
.
SpEL
: Spring Expression Language
:
access方法里头使用,可以代替其他方法,如: access(“hasRole(‘ROLE_READER’ and hasIpAddress(‘192.168.1.2’))”);
Authentication与Authorization区别 Authentication
: 鉴权,用户是谁.Authorization
: 授权,是否可以.
1 2 3 4 SecurityContextHolder.getContext().getAuthentication() SecurityContextHolder.getContext().getAuthentication().getAuthorities()
其他概念(自顶向下) SecurityContextHolder
: 为使用者提供全局的SecurityContext//ThreadLocal变量,每个线程唯一.SecurityContext
: 为了hold住AuthenticationAuthentication
:// 鉴权 主要负责两方面信息,一个是当前用户的详细信息(Principal、UserDetails),一个是用户鉴权时需要的信息。Principal
: 安全主体Authorization
: 授权,访问控制.GrantedAuthority
: 提供当前用户(UserDetails)所获得的系统范围内的授权。UserDetails
: 提供了用户的详细信息,主要被用来构建Authentication。UserDetailsService
: 这个接口的实现主要是负责通过用户名查找并提供用户的详细信息(UserDetails)。
密码的比较: 由AuthenticationManager
以及AuthenticationProvider
负责.
鉴权流程
用户名,密码 => UsernamePasswordAuthenticationToken
对象; //Authentication
的一个实现.
=> AuthenticationManager
对它进行验证;
=> 提取UserDetails
,GrantedAuthority
=> Authentication
对象
Authentication
=> SecurityContextHolder.getContext().setAuthentication
.
其中第一步在:
1 2 3 UsernamePasswordAuthenticationFilter类中: UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password);
第十章 模板方法设计模式 实现分为: 模板(template,固定部分)和回调(callback,可变部分) Spring提供的模板包括:
1 2 3 4 5 jdbcTemplate SimpleJdbcTemplate -- Spring3后已废弃 HibernateTemplate JdoTemplate JpaTemplate
第十一章 ORM
DSL JPA repository的DSL(黑话):1 2 3 4 5 readSpitterByFirstnameOrLastnameOrderByLastname(); 动词: read,get,find,count. // 前三个等效 Spitter: 主题 // 还可以加Distinct 断言: 还可以有: isAfter,isNull,isLike,IgnoringCase等等.
只要按这个模式命名方法名,就会自动被实现.
2.@Query
自定义查询
1 2 @Query ("select username,password,fullname from Reader" ) List<Reader> findAllGmailReaders () ;
3.混合自定义实现 上述情况都是声明一个ReaderRepository
接口, 继承JPARepository<Reader,String> (对象和对象主键类型), Spring就会自动生成一个Impl,根据定义的方法名自动生成实现.(类似于用iBatis时,工具生成map) 如果有比较复杂的自定义SQL操作,可以创建一个ReaderRepositoryImpl
, 和原来的名字相比增加Impl
后缀,(这个是默认后缀,可以通过注解配置调整), 然后继承同一个接口(比如都继承/实现ReaderSweeper
). Spring就会混合自动生成的实现和自定义的实现.
//详见github.
第十二章 NoSQL
MongoDb 及其template
note4j 及其template
redis 及其template 依赖:
1 2 compile "redis.clients:jedis:2.9.0" compile 'org.springframework.boot:spring-boot-starter-data-redis'
yaml配置:
1 2 3 4 5 spring: redis: host: localhost password: redis123 port: 6379
配置:
1 2 3 4 5 6 @Bean public RedisTemplate<String, Product> redisTemplate (RedisConnectionFactory cf) { RedisTemplate<String, Product> redis = new RedisTemplate<>(); redis.setConnectionFactory(cf); return redis; }
最后一步如果用不着template的话,甚至可以不用配.还蛮简单的.
序列化相关: Spring Data Redis提供的序列化器:
RedisTemplate
会使用JdkSerializationRedisSerializer
;
StringRedisTemplate
默认会使用StringRedis-Serializer
;
可以在创建RedisTemplate
的时候配置其他序列化器:1 2 3 4 5 6 7 8 @Bean public RedisTemplate<String, Product> redisTemplate (RedisConnectionFactory cf) { RedisTemplate<String, Product> redis = new RedisTemplate<>(); redis.setConnectionFactory(cf); redis.setStringSerializer(new StringRedisSerializer()); redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class )) ; return redis; }
第十三章 缓存技术 (切面实现)
1.声明缓存管理器
RedisCacheManager :单个缓存管理器1 2 3 4 5 6 7 8 9 @Configuration @EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager (RedisTemplate redisTemplate) { return new RedisCacheManager(redisTemplate); } }
多个缓存管理器:CompositeCacheManager 会迭代查找添加的各个缓存管理器.
2.使用缓存 4个相关注解: // 可用于类或方法级别.
1 2 3 4 @Cacheable : 先找之前缓存的值,没有则调用. @CachePut : 只是放到缓存里,始终调用. // 一般用于save方法. @CacheEvict : 清除n个缓存 @Caching : 分组
@CachePut
:
1 2 @CachePut (value="spittleCache" , key="#result.id" ) Spittle save (Spittle spittle) ;
条件缓存:
1 2 3 4 5 @Cacheable (value="spittleCache" unless="#result.message.contains('Nocache')" condition="#id >= 10" Spittle findOne (Long id) ;)
移除缓存:
1 2 @CacheEvict ("spittleCache" )void remove (long spittleid) ;
组合缓存操作:
1 2 3 4 5 6 7 8 @Caching ( put = { @CachePut (value = "user" , key = "#user.id" ), @CachePut (value = "user" , key = "#user.username" ), @CachePut (value = "user" , key = "#user.email" ) } ) public User save (User user) {...};
自定义注解:
1 2 3 4 5 6 7 8 9 10 11 12 @Caching ( put = { @CachePut (value = "user" , key = "#user.id" ), @CachePut (value = "user" , key = "#user.username" ), @CachePut (value = "user" , key = "#user.email" ) } ) @Target ({ElementType.METHOD, ElementType.TYPE}) @Retention (RetentionPolicy.RUNTIME) @Inherited public @interface UserSaveCache { }
第十四章 保护方法应用
保护方法调用 // SecuredConfig
使用表达式定义安全规则 //JSR250Config
创建安全表达式计算器 // ExpressionSecurityConfig 详见源码chapter_14. 配置上:1 2 3 4 5 6 7 8 9 @EnableGlobalMethodSecurity (securedEnabled=true )public class SecuredConfig extends GlobalMethodSecurityConfiguration { @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user" ).password("password" ).roles("USER" ); }
相关注解:1 2 3 4 @PreAuthorize 方法调用之前,用表达式限制调用 @PostAuthorize 方法调用之后,检查表达式,抛出异常 @PostFilter 允许方法调用,按表达式过滤结果 @PreFilter 允许方法调用,按表达式过滤输入值
service:
1 @Secured ({"ROLE_SPITTER" , "ROLE_ADMIN" })
第十五章 远程调用 里头的RMI都是java的,不能跨语言.跳过.
第十六章 rest api
内容协商 Content negotiation
:选择一个能用的视图
消息转化器 Message conversion
:转换成客户端使用的形式
协商 一维协商:// 只有视图名
Controller返回 -> 视图名 // 或void,原视图 -> DispatcherServlet -> 视图解析器.
二维协商: // (视图名,内容类型)
1.查看url的文件后缀名. 2.使用Accept首部类型 3.使用默认类型.
协商优点: 可以同时提供给人看的html和给客户端服务用的json. //重用controller 协商缺点: 客户端不能发送json或xml. 返回值是一个Model,也就是一个kv,比普通的多一层嵌套.
信息转换器 流程: Accept头信息(application/json) ->处理方法返回的对象交给MappingJacksonHttpMessageConverter. // 引入 Jackson JSON Processor库.
Spring提供的HTTP信息转化器包括:
1 2 3 4 AtomFeedHttpMessageConverter BufferedImages ByteArrayHttpMessageConverter ...
@ResponseBody
: 发送数据时使用转换器; 配合produces
1 2 3 4 5 6 @RequestMapping (method=RequestMethod.GET, produces="application/json" ) public List<Spittle> spittles ( @RequestParam(value="max" , defaultValue=MAX_LONG_AS_STRING) long max, @RequestParam (value="count" , defaultValue="20" ) int count) { return spittleRepository.findSpittles(max, count); }
@RequestBody
: 接受数据时使用转换器. 配合consumes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RequestMapping (method=RequestMethod.POST, consumes="application/json" ) @ResponseStatus (HttpStatus.CREATED) public ResponseEntity<Spittle> saveSpittle (@RequestBody Spittle spittle, UriComponentsBuilder ucb) { Spittle saved = spittleRepository.save(spittle); HttpHeaders headers = new HttpHeaders(); URI locationUri = ucb.path("/spittles/" ) .path(String.valueOf(saved.getId())) .build() .toUri(); headers.setLocation(locationUri); ResponseEntity<Spittle> responseEntity = new ResponseEntity<Spittle>(saved, headers, HttpStatus.CREATED); return responseEntity; }
@RestController
注解: 为所有方法增加@ResponseBody.方法的输入参数增加@RequestBody.1 2 3 4 5 6 7 8 @PathVariable long id@RequestParam (value="max" , defaultValue=MAX_LONG_AS_STRING) long max@RequestMapping (method=RequestMethod.POST, consumes="application/json" ) @ResponseStatus (HttpStatus.CREATED)
第十七章 异步消息
简介
JMS java消息服务 略过
AMQP 高级消息队列服务 (rabbitMQ)
POJO
简介 RMI是同步的,异步的只要投递完就返回.
发送
broker: 消息代理
destination: 目的地
dest分为两种:
topic 主题,发布/订阅模型
queue 队列,点对点模型
JMS api规范.支持点对点和发布订阅. //旧
AMQP 高级MQ协议. 不但约束了api,还使不同实现之间可以合作. 加入了Exchange,Binding,解耦了队列, 可以灵活实现除了点对点\发布订阅以外的模型.
Exchange
类型:
Direct:
判读消息和binding的key相等. 把消息路由到key相等的binding上.(binding的队列)
Topic:
判读消息key符合binding的通配符. 把消息路由到key匹配的binding上.
Headers:
判断消息的headers与bingding参数匹配.
Fanout:
无条件匹配. 广播到所有队列上.
默认会有一个没有名字的Direct Exchange, 所有队列都会绑定到这个Exchange上,并且routing key和队列名相同.
使用rabbitTemplate时,如果不指定exchange ,就会发送到默认的exchange,也就是上面说的默认Direct Exchange.
// 其他内容写入<< rabbitmq in action笔记 >>
第十八章 websocket 与http同级. 持久连接和前端通信(浏览器).用http的话,服务端无法向客户端发消息.
1. 直接使用websocket 前端: 使用ws://协议,或安全的wss://. 后端: 继承AbstractWebSocketHandler
接口,引入依赖:
1 compile 'org.springframework.boot:spring-boot-starter-websocket'
为了防止浏览器不兼容的情况,使用sockjs:
1 registry.addHandler(marcoHandler(), "/marco").withSockJS();
相应的,前端也引入sockjs库.
2. 使用封装以后的STOMP STOMP: Simple Text Oriented Messaging Protocol 简单文本的消息协议. 主要是加了个代理,消息流如下:
1 2 3 4 5 6 两条消息,目的地分别为: /app/marco,/topic/polo =>请求通道 =>/app: 发送到 AnnotationMethodMessageHandler => 代理通道 /topic: SimpleBrokerMessageHandler /queue: SimpleBrokerMessageHandler =>响应通道
如果要二进制的,还有别的协议MQTT.
第十九章 邮件 用qq邮箱代发即可,配置依赖以后,用springboot能自动获得相关bean,调用即可.不使用ssl的话需要配置:
1 2 3 4 5 6 spring.mail.username: xxx@qq.com spring.mail.password: password spring.mail.port: 25 spring.mail.protocol: smtp spring.mail.host: smtp.exmail.qq.com spring.mail.properties.mail.smtp.auth: true
第二十章 使用JMX 注册MBean到JMX中. 就试试Jconsole.
第二十一章 Springboot 起步依赖具体引入了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 spring-boot-starter-actuator=> spring-boot-starter ,spring-boot-actuator ,spring-core spring-boot-start-amqp=> spring-boot-start ,spring-boot-rabbit ,spring-core ,spring-tx spring-boot-aop=> spring-boot-starter ,spring-boot-aop ,AspectJ Runtime ,AspectJ Weaver ,spring-core spring-boot-starter-batch spring-boot-starter ,HSQLDB ,spring-jdbc ,spring-batch-core ,spring-core ... 太多了,以后有需要再查吧.