http://git.oschina.net/zhuqiang/smart-framework 跟着书上学习的 框架git地址
http://git.oschina.net/zhuqiang/mrweb 依赖上面框架的demo练习
啰嗦一句:本笔记只是自己在学习过程中的一些分析和理解,看的人不一定能看懂.如果有兴趣还是去买这本书看.笔记就当是另外一种解说好了
在本章节中会学习到如下的技术:
- 如何理解并使用代理技术
- 如何使用Spring提供的AOP技术(忽略,太多知识)
- 如何使用动态代理技术实现AOP框架
- 如何理解并使用ThreadLocal技术
- 如何理解数据库事务管理机制
- 如何使用AOp框架实现事务控制
静态代理
顾名思义,我的理解就是,在编译前写死的. 就如同下面这个例子, 写了一个类.然后实现目标类的所有方法,然后再转调目标类的方法.
interface IHello{
public void say(String msg);
}
class Hello implements IHello{
public void say(String msg) {
System.out.println(msg);
}
}
// 静态代理
class HelloProxy implements IHello{
private IHello iHello;
public HelloProxy(IHello iHello) {
this.iHello = iHello;
}
@Override
public void say(String msg) {
System.out.println("增加了前置处理");
iHello.say(msg);
System.out.println("增加了后置处理");
}
}
class Demo{
public static void main(String[] args) {
HelloProxy proxy = new HelloProxy(new Hello());
proxy.say("静态代理");
}
}
JDK动态代理
在运行期间根据代理类的字节码生成代理类的实例.
JDK动态代理:
- 使用Proxy类来获取代理类
- proxy需要 一个 InvocationHandler 类型的代理实现处理类(就是运行切面逻辑的地方)
- 只能代理 有接口的对象实例.强转的话,也只能转换为接口.
/**
* Created by zhuqiang on 2015/10/24 0024.
* jdk 动态代理
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增加了前置处理");
Object result = method.invoke(target, args); //转调目标的实际处理类
System.out.println("增加了后置处理");
return result;
}
public static void main(String[] args) {
Hello hello = new Hello();
DynamicProxy proxy = new DynamicProxy(hello);
IHello o = (IHello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(), // 类加载器
hello.getClass().getInterfaces(), //该类所实现的接口
proxy); // 代理实现类
o.say("动态代理 简单使用");
}
}
//
interface IHello{
public void say(String msg);
}
class Hello implements IHello{
public void say(String msg) {
System.out.println(msg);
}
}
CGlib动态代理
/**
* Created by zhuqiang on 2015/10/24 0024.
*/
public class CGlibProxy implements MethodInterceptor {
private static CGlibProxy cp = new CGlibProxy();
public static CGlibProxy getInstance(){
return cp;
}
// 获取代理类
public <T> T getProxy(Class<T> cls){
return (T)Enhancer.create(cls,this);
}
/**
*
* @param o 目标类
* @param method 目标方法
* @param objects 目标方法参数
* @param methodProxy 方法代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("后置");
return result;
}
}
class HelloCg{
public void say(String msg) {
System.out.println(msg);
}
}
class CGlibTest{
public static void main(String[] args) {
HelloCg proxy = CGlibProxy.getInstance().getProxy(HelloCg.class);
proxy.say("cglib 动态代理,支持没有接口的类哦");
}
}
什么是AOP
Aop(Aspect Oriented Programming面向方面编程) : 是对OOP编程的一种补充,也叫面向切面,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 由Aspect切面 和 Prointcut切点组成.
AOP框架有 aspectJ,前生是AspectWekz.
- Aspect切面: 就是横切业务逻辑的代码
- Prointcut切点: 用一个条件来匹配在哪些地方使用切面.
Spring AOP
这章节就忽略吧.知识点太多了. 要弄明白,得花一些时间,可时间不多.最重要的你要有个大概的知识体系,到时候用到了,再去深挖. 放上一张书上的 思维导图.
加载AOP框架
对 切面@Aspect 注解加载的分析;
1. @Aspect 表示一个类是一个切面,
2. 注解接收一个注解类型的class,表示该切面的切点是 被目标注解类修饰的类中的所有方法(连接点)
AopHelper 的最终目标是: 创建 目标类class 与 增强它的 切面代理类 进行关联
注意:这个操作会让代理类 覆盖掉 之前创建的 普通实例.因为HelperLoader中的加载顺序是:
ClassHelper.class,
BeanHelper.class,
AopHelper.class,
IocHelper.class,
ControllerHelper.class
通过上面的加载顺序可以看到,aop加载的时候,会把之前BeanHelper中把我们需要管理的bean实例化的对象,对应的class实例对象用当前的代理进行替换掉. 那么这样一来,在外部获取到的 目标类其实就已经是一个代理类了
实现的思路:
Map<Class<?>,Set<Class<?>>> createProxyMap()
: 将切面类 和 需要被增强的类class建立关联; key=切面类 value=所有需要被该切面所增强的类class
思路是:- 通过从ClassHelper中扫描到的class集合中 挑选出AspectProxy的子类(也就是 我们编写的切面类)
- 找到之后还不行,还需要从中过滤,拿到被Aspect注解所标注的类(被该注解所标注的类的含义在上面有讲)
- 拿到 被Aspect标注的类之后,我们就可以拿到 拿到 该注解中的切点,也就是说,我们能拿到所有需要被该切面所增强的类class
- 有类切面类(第2步) , 有了 所有切面类所对应的需要被增强的所有的类,那么就建立关联.返回结果
Map<Class<?>,List<Proxy>> createTargetMap
: 根据 createProxyMap 返回的值得,继续建立关联,key = 被增强的类class,value=切面列表实例 (因为一个类,可以被多个切面增强)
思路是:- 创建切面类的实例
- 将 该切面实例所对应增强的class建立关联,因为一个类,可以被多个切面所增强
使用cglib创建代理类,然后把bean容器中的普通示例类给替换掉.
思路是:- 使用Enhancer创建方法级别的拦截增强,
ProxyManger.createProxy(final Class<?> targetClass,final List<Proxy> proxyList)
把目标类和对应增强它的切面列表在intercept中 让我们的代理链模型去执行增强方法,在适合的切入点委托目标方法:return new ProxyChain(targetClass,targetObj,method,methodProxy,paramsObjects,proxyList).doProxyChain();
注意: 一定要注意这里,最开始一直不知道 这个代理链有什么用处,然后在实现代理的时候 方法拦截器中intercept忘记了 cglib生成代理的 步骤api.直接返回了我们的代理链.后来就直接抛出了 不能转换的异常.
debug调试 这个代理链的运作流程.发现.其实就是一个 职责链模式的设计,当我们代理的方法被拦截的之后,就会执行 用cglib生成代理的时候里面的代码,new ProxyChain(..).doProxyChain(). 这里是一个代理链条的入口. 这个里面 会判断,如果 有切面,则执行切面的 doProxy 方法.而 切面的doProxy方法里面 会在合适的时机(也就是我们定义的前后增强等位置)调用切面实现类的增强方法.然后继续调用doProxyChain(),这个时候就相当于 代理连的doProxyChain()方法被切面代理连给递归调用了.这样一来, 就会继续判断是否还有下一个切面增强类,如此的递归调用,最后委托目标方法 返回结果.诶.到现在才发现,最开始,就是这个代理链,和 增强类怎么被调用的设计是最难的了. 好吧,啰嗦了这么多话,下面放出一张流程图.来直观的感受下,这个代理链的增强类是怎么运作的.
- 使用Enhancer创建方法级别的拦截增强,
/**
* Created by zhuqiang on 2015/10/25 0025.
* 拦截所有的controller方法
* 我们想使用这个切面类 来拦截所有controller的方法.
*/
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy{
private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
private long begin; //开始时间
@Override
public void before(Class<?> cls, Method method, Object[] params) throws Throwable {
begin = System.currentTimeMillis();
LOGGER.debug(String.format("begin:class:%s,method:%s",cls.getName(),method.getName()));
}
@Override
public void after(Class<?> cls, Method method, Object[] params) throws Throwable {
long end = System.currentTimeMillis();
LOGGER.debug(String.format("end:class:%s,method:%s,耗时:%s", cls.getName(), method.getName(),end - begin));
}
}
// 加载aop框架的aophelper
/**
* Created by zhuqiang on 2015/10/26 0026.
*/
public final class AopHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);
static {
try {
Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
for(Map.Entry<Class<?>, List<Proxy>> targetEnt:targetMap.entrySet()){
Class<?> targetClass = targetEnt.getKey();
List<Proxy> proxyList = targetEnt.getValue();
Object proxy = ProxyManger.createProxy(targetClass, proxyList);
BeanHelper.setBean(targetClass,proxy);
}
}catch (Exception e){
LOGGER.error("aop fail",e);
}
}
/**
* 获取 aspect 设置的 注解类,类型的所有class 集合
* @param aspect
* @return
* @throws Exception
*/
private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception{
Set<Class<?>> targetClassSet = new HashSet<>();
Class<? extends Annotation> annotation = aspect.value();
// Aspect 是对一类注解类 进行拦截,这里是 不为空并且不是 aspect注解,就可以获取该注解
if(annotation != null && !annotation.equals(Aspect.class)){
targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
}
return targetClassSet;
}
/**
* 获取需要被代理的 所有 class 对象
* @return key= 切面 ,value = 该切面所增强的所有 class 对象
* @throws Exception
*/
private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception{
Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<>();
// 获取 所有的切面代理类 的 子类
Set<Class<?>> aspectProxySet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for (Class<?> cls : aspectProxySet) {
if(cls.isAnnotationPresent(Aspect.class)){// 判断该class 是否有 aspect注解(是否是一个切面标注)
Aspect aspect = cls.getAnnotation(Aspect.class); // 获取注解类
Set<Class<?>> targetClassSet = createTargetClassSet(aspect); // 由于 aspect 的值是接收一个 注解类,所以这里是获取到 使用该注解类的所有class
proxyMap.put(cls,targetClassSet);
}
}
return proxyMap;
}
/**
* 把 class 和 增强它的切面实例进行关联
* @param proxyMap 切面对应需要增强的class集合.
* @return
* @throws Exception
*/
private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
Map<Class<?>,List<Proxy>> targetMap = new HashMap<>();
for(Map.Entry<Class<?>,Set<Class<?>>> proxyEnt : proxyMap.entrySet()){
Class<?> proxyClass = proxyEnt.getKey(); // 获取切面class
Set<Class<?>> targetClassSet = proxyEnt.getValue(); //切面所需要增强的 class 对象(也就是我们需要把该切面在哪些实例上增强的对象class)
for (Class<?> targetClass : targetClassSet) {
Proxy proxy = (Proxy)proxyClass.newInstance(); // 创建该切面的实例
if(targetMap.containsKey(targetClass)){
targetMap.get(targetClass).add(proxy);
}else{
List<Proxy> proxyList = new ArrayList<>();
proxyList.add(proxy);
targetMap.put(targetClass,proxyList);
}
}
}
return targetMap;
}
}
/**
* Created by zhuqiang on 2015/10/25 0025.
* 代理链: 可以将多个代理通过一条链子串起来,一个一个地去执行,执行顺序取决于添加到链上的先后顺序(这不会是职责链模式吧)
*/
public class ProxyChain {
private final Class<?> targetClass; //目标类
private final Object targetObject; //目标对象
private final Method targetMethod; //目标方法
private final MethodProxy methodProxy; //方法代理
private final Object[] methodParams; //方法参数
private List<Proxy> proxyList = new ArrayList<>(); //代理列表
private int proxyIndex = 0; // 代理索引
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
public Object[] getMethodParams() {
return methodParams;
}
public Object doProxyChain() throws Throwable{
Object methodResult;
if(proxyIndex < proxyList.size()){ //根据传进来的 代理列表,
methodResult = proxyList.get(proxyIndex++).doProxy(this);
}else{
methodResult = methodProxy.invokeSuper(targetObject,methodParams);
}
return methodResult;
}
}
/**
* Created by zhuqiang on 2015/10/25 0025.
* 代理接口
*/
public interface Proxy {
/** 执行链式代理 */
Object doProxy(ProxyChain proxyChain) throws Throwable;
}
/**
* Created by zhuqiang on 2015/10/25 0025.
* 代理链: 可以将多个代理通过一条链子串起来,一个一个地去执行,执行顺序取决于添加到链上的先后顺序(这不会是职责链模式吧)
*/
public class ProxyChain {
private final Class<?> targetClass; //目标类
private final Object targetObject; //目标对象
private final Method targetMethod; //目标方法
private final MethodProxy methodProxy; //方法代理
private final Object[] methodParams; //方法参数
private List<Proxy> proxyList = new ArrayList<>(); //代理列表
private int proxyIndex = 0; // 代理索引
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
public Object[] getMethodParams() {
return methodParams;
}
public Object doProxyChain() throws Throwable{
Object methodResult;
if(proxyIndex < proxyList.size()){ //根据传进来的 代理列表,
methodResult = proxyList.get(proxyIndex++).doProxy(this);
}else{
methodResult = methodProxy.invokeSuper(targetObject,methodParams);
}
return methodResult;
}
}
/**
* Created by zhuqiang on 2015/10/25 0025.
* 代理管理器,用来创建代理对象
*/
public class ProxyManger {
/**
* 创建代理
* @param targetClass 目标类
* @param proxyList 代理列表
* @param <T>
* @return
*/
public static <T>T createProxy(final Class<?> targetClass,final List<Proxy> proxyList){
return (T)Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObj, Method method, Object[] paramsObjects, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass,targetObj,method,methodProxy,paramsObjects,proxyList).doProxyChain();
}
});
}
}
最后说一句: 上面的思路 也分析了.加载aop比较简单.学过一次后有思路了 基本上向这样简单的aop框架都能弄出来. 我觉得难的点在于, 这个代理链的执行这一个逻辑模版框架的设计, 和 动态代理的创建. 现在还是觉得设计模式有必要学习.
待续..待学习