#一 init
1.创建maven工程,引入依赖
2.编写servlet类,以及jsp文件
3.运行访问:http://localhost:8080/simple-framework/hello
#二至五是知识串讲,是贯穿spring核心的底层知识
#二 泛型是编译器的约束,编译后会擦除泛型信息
1.证明:编译泛型类查看.class文件会发现是没有泛型的
2.泛型不支持继承:
3.解决:
com.demo.demo.generic.GenericDemo.main
1.使用通配符,不指定具体类型,使用时进行强转,这样会使得泛型检查失去意义,和泛型设计初衷是相悖的
2.给泛型加上边界 <? extends E>
3.给泛型加下边界 泛型实参的子类必须是E ->从避免运行时 参数类型转换异常的角度讲: 这种方式是相较上面两种最安全的
#三 工厂模式
1.简单工厂:
提供统一的工厂类实现工厂抽象接口,根据客户端指定类型,判断创建所需的类型
缺点:每次新增需求,都需要在工厂实现类新增代码,违背了开闭原则
com.demo.pattern.simple.MouseFactory
2.工厂模式
将工厂抽象出来:com.demo.pattern.method.MouseFactory
当需要新的类类型,只需新增工厂实现类,不影响之前代码
缺点:添加新产品时时,还需添加徐其数量相对的工厂实现了类,'拖家带口',增加了系统的复杂度,代码臃肿
只支持同一类产品的创建
3.抽象工厂
com.demo.pattern.abstractf.ComputerFactory
提供一个创建一系列相关或相互依赖对象的接口
抽象工厂模式侧重的是同一产品族->同时定义多个抽象
工厂方法更加侧重同一产品等级
解决了工会模式只支持生产一种产品的弊端
新增一个产品族时,只需要增加一个新的具体工厂,不需要修改代码
缺点
更多的类型加入抽象工厂时,所有的实现都需要修改 --违背开闭原则
4. spring的解决方案:
结合了工厂模式和反射机制的spring-ioc容器值得借鉴
#四 反射机制 --java.lang.reflect
1.允许程序在运行时进行自我检查,并对内部成员进行操作
2.作用:
在运行时判断任意一个对象所属的类
在运行时获取类的对象
在运行时访问java对象的属性,方法,构造方法等
3.Class类:
用来表示运行时类型信息的对应类
每个类都有唯一一个与之对应的Class对象
Class类为类类型,而Class对象为类类型对象
4.Class类的特点:
也是类的一种,class则是关键字(用来声明类)
Class类只有一个私有的构造函数,只有JVM能够创建Class类的实例
JVM中只有唯一一个和类相对应的Class对象来描述其类型信息(包名+类名+同一个类加载器创建出来的)
@see java.lang.Class
5.获取Class对象的三种方式
Objec->getClass() --实例方法
任何数据类型(包括基本数据类型)都有一个'静态'的class属性 --静态方法
通过Class类的静态方法: forName(String className) --常用
6.Class对象就像一面镜子,透过这面镜子可以看到类的结构
7.反射的获取源?
用xml来保存类相关的信息以供反射读取调用
注解方式保存类相关的信息供反射调用
#五 注解
1.提供一种为程序元素设置元数据的方法
元数据是添加到程序元素如方法,字段,类,和包上的额外信息
注解是一种分散式的元数据设置方式,XML是集中式的设置方式
注解不能直接干扰程序代码的运行
2.@see java.lang.annotation.Annotation
3.所有注解都继承自 Annotation
反编译注解的.class文件
cd src/main/java/demo/annotation javac TestAnnotation.java
javap -verbose annotation.TestAnnotation
-> public interface demo.annotation.TestAnnotation extends java.lang.annotation.Annotation
4.注解的功能:
作为特定的标记,告诉编译器一些信息
编译时动态处理,如动态生成代码(如:lombok)
运行时动态处理,作为额外信息的载体,如:获取注解信息
5.注解的分类:
5.1 标准注解: Overrride Desprecated SuppressWarnings
5.2 元注解(用于定义注解的注解): @Target @Retention @Inherited @Documented
@Target 注解的作用目标
@see java.lang.annotation.ElementType
@Retention 注解的声明周期
SOURCE 源代码保留,编译后会去除
CLASS 源代码+.class文件
RUNTIME 运行时保留 如:@Autowired --反射时也可从注解中获取额外信息
@Documented 注解是否应当被包含在JavaDoc文档中
@Inherited 是否允许子类继承该注解
5.3 自定义注解
注解支持的数据类型
所有基本数据类型
String类型
Class类型
Enum类型
Annotation类型
以上类型的数组
5.4 Class Constructr Method Field 这些跟反射息息相关的类都实现了 AnnotatedElement 接口
AnnotatedElement提供了多种方法来获取不同定义方式下的注解
Annotation[] getAnnotations();
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 指定类型注解是否存在于此元素上
5.5 解析自定义注解:----附源码跟踪技巧
demo.annotation.AnnotationParser
main函数添加vm参数:-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true -XX:+TraceClassLoading --保存代理对象,并打印程序所加载用到的类
可以看到生成了一个动态代理对象:$Proxy1.class --com/sun/proxy/$Proxy1.class
源码剖析:com.sun.proxy.$Proxy1#gender return (String)super.h.invoke(this, m6, (Object[])null);
调用了父类Proxy的invoke方法(区分于反射的同名方法)
查看java.lang.reflect.InvocationHandler实现了类:sun.reflect.annotation.AnnotationInvocationHandler
侧面说明了spring的强大--合理的命名就能定位到实现类
5.6 注解的工作原理
通过键值对的形式为注解属性赋值
编译器检查注解的使用范围,将注解信息写入元素属性表
运行时JVM将RUNTIME的所有注解属性取出并并最终保存到map里 --这里指的是针对单个Class文件
创建AnnotationInvocationHandler实例并传入前面的map
JVM使用JDK动态代理为注解生成代理类,并初始化处理器
调用invoke方法,通过传入(被加了目标注解的)方法名返回注解对应的属性值
6.上述技术点对自研框架的意义:
使用注解标记需要工厂管理的实例,并依据注解属性做精细控制
spring:@Controller
7.控制翻转IOC-Inversion of Control
依托一个类似工厂的IoC容器
将对象的创建,依赖关系的管理,以及生命周期交由IoC容器管理
降低系统在实现上的复杂性和耦合度,易于扩展,满足开闭原则
IOC是Spring Core最核心部分
需要先了解依赖注入(Dependency Inversion) --Set注入 接口注入 注解注入 构造器注入
以行李箱设计为例
轮子<--底盘<--箱体<--行李箱 上层建筑依赖下层建筑->可维护性低 轮子改了(如:修改构造函数),所有建筑构造函数全部都得改
行李箱<--箱体<--底盘<--轮子 (注入)上层控制下层
IOC优势:
避免在各处使用new来创建对象,并且可以做到统一维护
创建实例时不需要了解其中细节
反射+工厂模式的结合,满足开闭原则
#六 框架
1.1 解析配置,如xml,注解等
1.2 定位与注册对象
1.3 注入对象
1.4 提供通用工具类(非必要)
1. 需要实现的点:
创建注解->提取标记对象->实现容器->依赖注入
2. extractPacakgeClass里面需要完成的事情
获取到类的加载器
通过类加载器加载到加载的资源信息
依据不同的资源类型,采用不同的方式获取资源的集合
2.1 获取项目类加载器的目的?
获取项目发布的实际路径 如:com.demo
2.2 为什么不让用户传入绝对路径?
不够友好: 不同机器之间的路径可能不同
如果打的是war包或者jar包,根本找不到路径
2.3 类加载器ClassLoader
*.java文件->Java编译器->字节码(.class文件)->类加载器Classloader->字节码校验器->解释器->操作系统平台
根据一个指定类的名称,找到或者生成其对应的字节码
加载Java应用所需的资源
@see org.simpleframework.util.ClassUtil.getClassLoader
java.lang.Thread.getContextClassLoader ->java.lang.ClassLoader.getResource ->返回 java.net.URL
URL:协议+ 域名(主机名)+端口号+ 路+ 请求参数+引用位置
http://www.news.cn.8080/Public/GetValidateCode?time=123#index
获取包下类的集合
org.simpleframework.util.ClassUtil.extractPackageClass
2.4 单例模式 --确保一个类只有一个实例,并对外提供统一的访问方式 --阅读源码: ObjectInputStream的readObject
饿汉模式:
懒汉模式
加入双重检查锁机制的懒汉模式能确保线程安全
装备了枚举的饿汉模式能低于反射与序列化的进攻,****满足容器需求****
3. 实现容器
保存Class对象及其实例的载体
容器的加载
容器的操作方式
3.1 实现容器的加载
配置的管理与获取
获取指定范围内的Class对象 -> org.simpleframework.core.BeanContainer.BEAN_ANNOTATION
一句配置提取Class对象,连同实例一并存入容器 -> org.simpleframework.core.BeanContainer.loadBeans
-->测试:org.simpleframework.core.BeanContainerTest.loadBeansTest 对BeanContainer对象打端点进行跟踪更佳...
3.2 实现容器的操作方式(容器的增删改查)
增加删除操作 --org.simpleframework.core.BeanContainer.addBean removeBean
根据Class获取对应实例 --org.simpleframework.core.BeanContainer.getBean
获取所有的Class实例 --org.simpleframework.core.BeanContainer.getClasses
通过注解获取被注解标记的Class org.simpleframework.core.BeanContainer.getClassesByAnnotation
通过超类获取对应的子类Class org.simpleframework.core.BeanContainer.getClassesBySuper --验证isAssignableFrom()方法 ->demo.api.assignable.AssignableDemo
获取容器载体保存Class的数量
3.3 Spring框架作用域
singleton
prototype 每次getBean都产生新的实例
request 每次http请求都产生新实例
session session级别的
globalsession 全局http session
4.实现容器依赖注入
目前容器里面的Bean实例仍可能是不完备的
实例某些必须的成员变量还没被创建出来 ->引出依赖注入的概念
实现思路
定义相关的注解标签
实现创建被注解标记的成员变量实例,并将其注入到成员变量
依赖注入的使用
核心方法实现:org.simpleframework.inject.DependencyInjector.doIoc
测试:org.simpleframework.inject.DependencyInjectorTest.doIocTest
3.1 系统需求
添加日志信息:为每个方法添加统计时间
添加系统权限校验:针对某些方法进行限制
OOP下必须得为每个方法都添加通用逻辑工作,增加维护成本
不同的问题交给不同的部分去解决,每个部分专注做自己的事情
Aspect之于APO就相当于Class之于OOP,Bean之于Spring
3.2 AOP的组成:
切面Aspect:将横切关注点逻辑进行模块化封装的实体对象
通知Advice:好比是Class里面的方法,还定义了织入逻辑的时机
连接点JoinPoint:允许使用Advice的地方
SprignAOP默认只支持方法级别AOP
切入点Pointcut:定义一系列规则对Joinpoint进行筛选
3.3 AOP的种类
Advice的种类
BeforeAdvice:在JoinPoint前被执行的Advice
AfterAdvice:好比try catch finally的finally
AfterReturningAdvice:在Joinpoint执行流程正常返回结果后执行
AfterThrowingAdvice:抛异常执行
AroundAdvice:Joinpoint前后都执行,最常用的Advice
######SpringAOP的实现原理
代理模式
demo.pattern.proxy.impl.AlipayToB
demo.pattern.proxy.impl.AlipayToC
问题:对不同接口的实现,增强功能一致,也需要编写多个代理类(代理类需要实现不同的接口)
需求改进
溯源ClassLoader
切入点:
根据一定规则去改动或者生成新的字节流,将切面逻辑织入其中
动态代理--
根据接口或者目标类,计算出代理类的字节码并加载到JVM中去
1.Jdk动态代理
程序运行时动态生成类的字节码,并加载到JVM中
要求被代理类必须【实现特定接口】
并不要求代理对象去实现接口,所以可以可以复用代理对象的逻辑
--java.lang.reflect.InvocationHandler.invoke
--java.lang.reflect.Proxy
2.CGLIB动态代理
代码生成库:Code Generation Library
不要求被代理类实现接口
内部主要封装了ASM Java字节码操控框架
动态生成子类以覆盖非final的方法,绑定钩子回调自定义拦截器
3.区别
JDK动态代理:基于反射,要求业务类必须实现接口
优势:
不需要额外jar包提来,更可靠
平滑支持JDK版本的升级
CGLIIB:基于ASM机制实现,生成业务类的子类作为代理类
优势:
被代理对象无需实现接口,能实现代理类的无侵入
4.SpringAop的底层机制
CGLIB和JDK动态代理共存
默认:Bean实现接口则用JDK,否则使用CGLIB
######自研框架的AOP1.0 使用CGLIB 思路: 1.解决标记的问题(注解),定义横切逻辑的骨架 1.定义横切逻辑相关的注解:org.simpleframework.aop.annotation.Aspect 2.定义供外部使用的横切逻辑骨架 2.定义Aspect横切逻辑以及被代理方法的执行顺序 创建MethodInterceptor的实现类 定义必要的成员变量--被代理类以及Aspect列表 按照Order对Aspect进行排序 实现对横切逻辑以及被代理对象方法的定序执行 3.将横切逻辑织入到被代理的对象以生成动态代理对象
AspectJ框架
织入时机:
编译时织入:利用ajc,将切面逻辑织入到类生成的.class文件
编译后织入:利用ajc,修改javac编译出来的class文件
类加载期织入:利用java agent,在类加载的时候织入切面逻辑
SpringAOP2.0
仅用到AspectJ的切面语法,未使用ajc编译工具
避免用户学习成本
默认不用,但可以引入
织入机制沿用自己的CGLIB和JDK动态代理机制
@see :src/main/resources/img/AspectJ框架对比springAOP.png
自研aop大体思路:
定义注解:Aspect,并在需要加强的类上加上该注解
拿到这些类--org.simpleframework.core.BeanContainer.getClassesByAnnotation
筛选(需要排除注解本身的类)--org.simpleframework.aop.AspectWeaver.wrapIfNecessary
生成这些类的代理类--org.simpleframework.aop.ProxyCreator.createProxy
将增强后的Bean重新放回容器(覆盖)
######自研框架的MVC
DispatcherServlet
解析请求路径和请求方法
依赖容器,建立并维护Controller方法与请求的映射
用合适的Controller方法去处理特定的请求
责任链模式执行请求之
1.StaticResourceRequestProcessor的开发:
http://localhost:8080/simple-framework/static/aspectOrder.png
2.JspRequestProcessor
http://localhost:8080/simple-framework/templates/addheadline.jsp
3.ControllerRequestProcessor
功能:
针对特定请求,选择匹配的Controller方法进行处理
解析请求里的参数及其对应的值,并赋值给Controller方法的参数
选择合适的Render,为后续请求结果的渲染做准备
实现:
默认--DefaultResultRender
http://localhost:8080/simple-framework/headline/remove
InternalErrorResultRender
http://localhost:8080/simple-framework/main/test
ResourceNotFoundResultRender
http://localhost:8080/simple-framework/not/find
JsonResultRender
http://localhost:8080/simple-framework/headline/query
ViewResultRender
http://localhost:8080/simple-framework/templates/addheadline.jsp