代理(AOP切面的雏形):
题记:顾名思义就是将某件事,某个东西的使用权进行为让授权转移。代理相当于中介(不同于中介者模式),在原本操作的类之间添加了一个桥梁。但代理不能去修改原有目标。比如:一个人要买 房,让中介帮忙给看个房,但中介不能将原有看房这件事变更为看车。他可以对看房提出各种要求和咨询。
回到程序的角度,调用者将调用某些共性类的处理交由代理类处理,代理类根据调用者的要求即满足什么情况可以调用,什么情况不能调用,(卖房者委托中介100万以上卖,100万以下不卖)对处理做前后的包装,但是代理类不能去改变被调用类的内部处理(可口可乐中国区可修改可口可乐包装,但不能在没授权情况下去更改可口可乐饮料)。
1.静态代理
2.动态代理
1.静态代理:对指定接口类做代理,通过对执行方法前后做处理,来实现代理的作用。因对指定接口类,因此可做某些特定类的特殊处理。
1 /** 2 * 静态代理,也被称为decate模式 3 * 4 * @author DennyZhao 5 * @date 2017年6月13日 6 * @version 1.0 7 */ 8 public class AnimalProxyFactory implements AnimalFactory { 9 /** 10 * 序列号 11 */ 12 private static final long serialVersionUID = -3421761900236678842L; 13 14 15 private AnimalFactory animal; 16 17 public AnimalProxyFactory(AnimalFactory animal){ 18 this.animal = animal; 19 } 20 @Override 21 public int getLegs() { 22 System.out.println("before....action"); 23 int legs = animal.getLegs(); 24 System.out.println("after....action"); 25 return legs; 26 } 27 28 }
测试Main:
// 静态代理
Rabit rabit = new Rabit();
AnimalProxyFactory proxyFactory = new AnimalProxyFactory(rabit);
System.out.println(proxyFactory.getLegs());
运行结果:
>>>before....action
>>>after....action
>>>4
2.动态代理:动态代理通过Proxy类创建代理,通过InvokationHandler去调用方法。
1 package pattern.creation.factory; 2 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.IOException; 6 import java.lang.reflect.Proxy; 7 import java.util.Properties; 8 9 /** 10 * 动态代理模式 11 * 12 * @author DennyZhao 13 * @date 2017年6月13日 14 * @version 1.0 15 */ 16 public class ProxyFactory { 17 private static final String CLASS_NAME_PATH = "pattern/classname.properties"; 18 @SuppressWarnings("unchecked") 19 public static <T> T getInstance(String name) throws IllegalArgumentException, InstantiationException, IllegalAccessException{ 20 String className = getClassName(name); 21 T result = null; 22 try { 23 Class clazz = Class.forName(className); 24 result = (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new MyInvocationHandler(clazz.newInstance())); 25 } catch (ClassNotFoundException e) { 26 e.printStackTrace(); 27 } 28 return result; 29 } 30 31 /** 32 * 获取类名称 33 * @return 34 */ 35 private static String getClassName(String name) { 36 Properties pro = new Properties(); 37 try { 38 String url = ProxyFactory.class.getClassLoader().getResource("").getPath() + CLASS_NAME_PATH; 39 pro.load(new FileInputStream(url)); 40 } catch (FileNotFoundException e) { 41 e.printStackTrace(); 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } 45 return (String)pro.get(name); 46 } 47 }
注意* : 代理面向的是接口,因此委托的类都需要实现指定的接口。
result = (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new MyInvocationHandler(clazz.newInstance()));
clazz.getInterfaces():如果一个clazz实现多个接口,此处要传入接口Class。
MyInvocationHandler 实现 InvocationHandler接口,里面要实现一个invoke方法,在这个invoke方法中填写处理。
// 可通过放射机制,来处理特殊类的特殊请求。
测试Main:
// 动态代理,AOP切面的雏形做法
AnimalFactory sheep = ProxyFactory.getInstance("sheep");
sheep.getLegs();
// 动态代理,AOP切面的做法
PlantFactory plum = ProxyFactory.getInstance("plum");
plum.getColor();
// 动态代理,AOP切面的做法
AnimalFactory rabit0 = ProxyFactory.getInstance("rabit");
rabit0.getLegs();
结果:
before-------
sheep has 4 legs....
after-------
before-------
plum green purple....
after-------
before-------
兔子是特殊的
after-------
深入理解静态代理和动态代理:
静态代理:需要代理类和实体类拥有同一个接口,这样才能保证口径一致,即代理商不能随意改变原实体类的行为。在代理类中将实体类注入进去,实现代理类和实体类的关联。这样在调用代理类时,会找到对应的实体类触发其行为,在调用前后又可以写入其它信息比如轨迹日志。
// 静态代理 IAnimal animal = new Sheep(); IAnimal proxy = new ProxyAnimal(animal); System.out.println(proxy.getName());
jdk动态代理:动态代理的本质和静态代理是一致的。只不过动态代理是因为事前根本不知道要去代理谁,且原静态代理类,如果要实现代理需要写相同的实体类的相关接口,和好多代理类,扩展维护很麻烦。因此就采用一种动态代理。 通过定义一个类 实现 InvocationHandler接口的方法,即统一的代理行为,之后的所有代理信息都会进入到这个方法中,包括目标方法和目标参数。然后再由一个Proxy静态类,去在运行时创建一个类class $Proxy0,这个类会由ProxyGenerate根据传入的所有的接口,通过反射获取他们的方法,实现他们的方法,同时把Invocationhandler注入到这个类中,生成类似我们静态代理的临时类。坦白的说只是把我们自己写代理的这个过程交给机器来编写实现。
InvocationHandler invocation = new AnimalInvocation(new Sheep()); // 动态代理 Object animals = Proxy.newProxyInstance(invocation.getClass().getClassLoader(), new Class[]{IAnimal.class, IPlant.class}, invocation); IPlant plant = (IPlant) animals; IAnimal animal = (IAnimal) animals;
在这个 invocation中我们可以传入我们的实体对象(构造带参的构造器),也可以不传递通过rpc或者远端http请求获取Feign。
public class AnimalInvocation implements InvocationHandler { private Object obj; /** * 创建代理对象 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(proxy.getClass()); // rcp调用远端接口处理数据,返回 method, obj ,args return "其实我什么都不是"; //return method.invoke(obj, args); }
spring有个AOP的切面使用的也是反向代理,只不过他的实现机理和我们JDK的内部的有些不一样。spring用的是cJlib,通过创建一个子类继承当前的service类。在调用中通过super来调用父类的方法实现代理。祥见 spring-AOP切面
因service中可以不定义接口,yi'ni'c