静态代理
- 定义真实对象和代理对象的公共接口
- 定义真实对象,实现真实业务行为
- 定义代理对象,包含真实对象,以及真实业务行为的前置后置增强
- 面向接口编程
但是如果增加代理的功能,代理类需要添加新接口、需要包含新的真实业务类,违反开闭原则
设计模式的几个原则
1、单一职责原则:一个类或者一个接口只负责唯一职责,尽量设计出功能单一的接口;
2、依赖倒转原则:高层模块不应该依赖底层模块的具体实现,解耦高层和底层。既是面向接口编程,当实现发生变化时,只需要提供新的实现类,不需要修改原本高层代码;
3、开放封闭原则:;程序对外扩展开放,对修改关闭,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原本代码来满足新需求;
4、迪米特原则:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合度;
5、里氏代换原则:所有引用父类的地方必须能透明的使用其子类的对象;
6、接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小接口上;
动态代理
核心类Proxy和InvocationHandler
Proxy来自于java.lang.reflect反射包,提供了静态方法newProxyInstance,创建动态代理的类和实例,同时也是所有代理类的父类
public static Object new ProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
// 内容省略
}
InvocationHandler是一个接口(只有invoke方法)
public interface InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
Proxy在Jvm内部帮我生成一个实例,InvocationHandler通过反射进行业务增强
Demo代码
public class RyanProxy implements InvocationHandler {
// 被代理的对象
private Object factory;
// 省略get/set方法
...
// 通过Proxy获取动态代理的对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
this);
}
// 通过动态代理对方法进行增强
@Override
public Object invoke(Object proxy, Method method, Object[]args) throws Throwable {
doSomethingBefore();
Object ret = method.invoke(factory, args);
doSomethingAfter();
}
// 前置增强
private void doSomethingBefore() {};
// 后置增强
private void doSomethingAfter() {};
}
假设上述为代理公司,假设有业务工厂CarFactory实现业务接口SellCar,有业务公司WatchFactory实现业务需求接口SellWatch
// 有代理公司和多个业务公司
RyanProxy ryan = new RyanProxy();
CarFactory carFactory = new CarFactory();
WatchFactory watchFactory = new WatchFactory();
// 代理业务公司的业务, 获取业务部门进行业务操作
comp.setFactory(carFactory);
CarFactory carDept = (CarFactory)ryan.getProxyInstance();
carDept.sellCar("Audi");
comp.setFactory(watchFactory);
WatchFactory watchDept = (WatchFactory)ryan.getProxyInstance();
watchDept.sellWatch("Rolex");
这样再新增业务的时候,不需要修改原有代码,提供新的业务实现类即可;
一个业务流程出错也不会影响到其他业务流程;
动态代理源码
Debug走到CarFactory carDept = (CarFactory)comp.getProxyInstance();的时候发现carDept的类名为$Proxy0,watchDept的类名为$Proxy1,这两个类是哪里来的,为什么会有?
类的完整的生命周期
- IDE中编写Java源文件(.java)
- javac命令把Java源文件编译成Java字节码(.class),字节码还可以从网络(例如热加载)、jvm生成
- jvm classloader加载字节码,在jvm内部生成Class对象(Class在jvm的方法区)
- 进一步实例化对象
- 无引用卸载
上述getProxyInstance即为jvm生成的.class文件,那么具体是如何生成的?
字节码class在内存中如何生成
进入getProxyClass方法,因为方法区限定64M,所以限定了类实现接口数量小于65535,防止OOM内存溢出
进入get方法
最后进入apply方法,红框里是校验,继续往下看
这里便是要生成的class文件的名字和具体内容,proxyClassNamePrefix = “$Proxy”
defineClass0的方法如下,native本地方法,用c c++甚至是汇编语言编写的操作系统级别的类库
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
Spring事务注解实现的原理
- Spring在调用到service时候发现实际service是一个动态代理类,$Proxy数字的格式,有一个属性为JdkDynamicAopProxy的类,该类为Spring aop的核心类,实现了InvocationHandler接口
- invoke方法只是定义了通用校验,不和具体业务绑定。Spring启动时扫描那些类的那些方法上添加了aop相关的注解,并且丢到缓存里去。
- Key为方法,Value为方法上有多少拦截器的注解。如果添加了@Transactional注解,具体拦截器为TransactionInterceptor
- 其中具体的业务封装在了TransactionAspecSupport的方法里