AOP
什么是切面
把皮肤切开,塞一本经书进去,然后缝合。
维护原有结构,在不破坏结构的条件下,插入自定义的东西,这就是切面。
能做写什么
上面塞经书的,是一个和尚,因为大乘真经
被隔绝的很严重,他用这种办法带出了经书。
还是那个熟悉的面孔,一切还是熟悉的模样,只是,夹带了一些私货,也就是我们的目的。
乔装打扮,伪造学历…
本质上仍然是那个谁,不过的确能够达成了一些额外的需求。
这是原本所不具备的,也正是我们所需要的。
有什么不同
AOP
,就是保留原有的滋味,同时,增加额外的信息或操作。
静态代理
首先明确一点:保留固有特征情况下,增加额外的需求。
单纯的从这个目的出发,这样就可以实现
Service
public class Service {
public void service(String name){
System.out.println("service with name : " + name);
}
}
ServiceProxy
public class ServiceProxy {
private Service service;
public ServiceProxy(Service service){
this.service = service;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
public void service(String name){
before();
service.service(name);
after();
}
}
装饰器模式,代理模式或者什么其他的东西,你都可以称呼。
不理会名头,简单来说,调用前后做了些动作,如是而已,目的的确达成了。
不过有意思的是,装饰器模式或其他类似的东西,有自成一派的需求:
- 它可能需要,或者不需要,或者不同需要
- 重要的是它本身,同时附带其他东西
对于这些需求,采用这种模式,装饰器
更贴近一些。
朴实一点来说,如果只是一个东西做扩展,想玩出花样,装饰者设计模式无可厚非。
一整箱的衣服,只为一位女士各种需求,的确需要装饰。
不过,这种方式,有一些弊病:
- 装饰总是很多,原体唯一一个(量身定做)
想象一下在军队中,单纯的只需要一身铠甲,不论合身与否,只在乎坚韧。
但是又成千上万的军人。
在这种场景下,量身定做,是否不太现实。
用程序的角度称述:每一个原型对象,总需要另一个代理对象来装饰。
全部装饰,业务逻辑之外的对象代码,那可就都得翻番了。
动态代理
JDK
IService
public interface Service {
public void service(String name);
}
Service
public class ServiceImpl implements Service{
public void service(String name){
System.out.println("service with name : " + name);
}
}
MyProxy
public class MyProxy<T> implements InvocationHandler {
private T target;
public MyProxy(T target){
this.target = target;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("service".equals(method.getName())){
Object result = null;
before();
result = method.invoke(target, args);
after();
return result;
}
return method.invoke(target,args);
}
public T getProxy(){
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, interfaces, this);
}
}
main
public class Main {
public static void main(String[] args) {
new MyProxy<Service>(new ServiceImpl()).getProxy().service("service");
}
}
这样一来,是不是更亲民了,由原来的多对一
,现在,能够满足一对多
的场景了吧。
的确,似乎变得更好了,但是,没有想的那么好。
不足
main
public class Main {
public static void main(String[] args) {
System.out.println(new MyProxy<Service>(new ServiceImpl()).getProxy().getClass().getName());
}
}
com.sun.proxy.$Proxy0
。结果是这个,它的实体并不是Service
。
正是因为java
中不允许多继承,然后就导致了它不得不利用作为顶层父类才能够进行对象的接收。
不能够进行无接口的对象的代理。
优点
- 满足了装饰的目的
- 实现了多对一的场景需求
诟病
- 必须存在接口,并不适用全体(来了个好裁缝,只为一个团服务,这个就不好了吧)
- 维护方面,还有待改进(大冷天的,换个衣服你也不能让我脱啊,会冻死的,还不如冷着)
CGLIB
proxy
public class Proxy<T> implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Proxy(T proto){
enhancer.setSuperclass(proto.getClass());
enhancer.setCallback(this);
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(obj, args);
after();
return result;
}
public T getProxy(){
return (T)enhancer.create();
}
}
main
public class Main {
public static void main(String[] args) {
new Proxy<Service>(new Service()).getProxy().service("hello");
}
}
值得一说的是,cglib
不仅可以代理接口,还可以对无接口类直接代理。
和jdk
代理有如下区别
jdk
只能代理接口,cglib
全能代理。jdk
基于反射,类生成高效;cglib
基于字节码修改,执行高效。
遗漏
代理还需要替换原有对象,后期维护设计修改原逻辑。
这个问题,cglib
并没有解决。莫不如说,这个就不是代理的工作范围。
不过spring
提供了容器进行对象的管理和注入,从而解决了这个问题。
在Autowired
或者getBean
的时候,是从容器中进行的对象获取。
刨除了手工创建对象的过程,是间接的引用的方式。
所以,在后期维护过程中,只需增添,而无需修改就能够完成想要的效果。
就像既定的代码增加日志,无需再打开原有的代码修改逻辑,也不用修改对象创建代码。
小结
- 所谓代理(装饰)
对原型进行包装,在可辨识的程度内,在需要的地方注入操作(装饰)。
- 所谓切面
在一个既定的可注入空代理的结构中,能够指定某个流程的具体操作办法。
也就是说,前面的都是代理,而AOP
是在代理的成型框架中,对流程方法的指定。
Wrapper
public interface Wrapper {
public void before();
public void after();
}
proxy
public class Proxy<T> implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
private Wrapper wrapper = null;
public void setWrapper(Wrapper wrapper){
this.wrapper = wrapper;
}
public Proxy(T proto){
enhancer.setSuperclass(proto.getClass());
enhancer.setCallback(this);
}
public void before(){
if(this.wrapper != null){
this.wrapper.before();
}
}
public void after(){
if(this.wrapper != null){
this.wrapper.before();
}
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(obj, args);
after();
return result;
}
public T getProxy(){
return (T)enhancer.create();
}
}
大概就是这个意思。
AOP
和代理并不冲突,也不是什么相似的东西。
AOP
是在以实现生命周期代理为前提的情况下,对指定的生命周期进行定制方法注入。
所以,两者并不互斥,尤其是想要扩充方法,变得更加多变的情况下,这个时候装饰
更加有用。
- 装饰:针对类,可以收束到方法,针对原型进行扩充,操作插入和方法扩展。
- 代理:针对流程,插入制定的前后置处理办法。
AOP
: 在既定的生命代理框架下,对前后置操作处理办法进行注入。
方法扩展
和前后置处理
,从这两点就能够很好的区分了。