动态代理
理解:动态代理就是使用原先类中时,使用代理向原先类的方法的(前面,后面,异常产生时,前面和后面)添加代理功能或处理功能 比如:在代码类的前面和后面添加计算原先方法执行时间的功能,也可以说是对某个方法的增强。
动态代理执行步骤:
示例代码
Collection coll = (Collection)Proxy.newProxyInstance(Collection.class
.getClassLoader(), new Class[]{Collection.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
Object o = method.invoke(list, args);
return o;
}
});
coll.add("aa");
首先会创建一个代理对象Proxy.newProxyInstance 根据类实现的接口
下一步会指定InvocationHandler 因为代理类每执行一次方法,便会到记忆的InvocationHandler 中执行invoke方法,并且把(代理类的对象,方法名,参数传进行) 而invoke方法的几个参数正是通过执行方法的时候传进来的
代理方法执行一次,便在invokecation里面执行一次invoke方法 invokecation被创建后不会被再次创建
当coll.add("aa");执行时找到记忆的invokecation 并调用Invoke方法,把参数传进去,由代理对象执行这个方法,并返回执行的结果
动态代理和装饰模式区别:
装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;
代理模式:给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用;
装饰模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,并不提供对象本身的增强功能
二者的实现机制确实是一样的,可以看到他们的实例代码重复是很多的。但就语义上说,这两者的功能是相反的,模式的一个重要作用是简化其他程序员对你程序的理解,
你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大家就知道是在限制
这两个设计模式看起来很像。对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。
然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
代理方法执行示意图
对象如果要代理,首先会找代理对象,并执行invocationHander方法,invocationHander对目标进行更改后 再执行对象本身的方法
简单代理方法示例:
final ArrayList list = new ArrayList(); //要代理的对象
Collection coll = (Collection)Proxy.newProxyInstance(Collection.class
.getClassLoader(), new Class[]{Collection.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object o = method.invoke(list, args);
long stopTime = System.currentTimeMillis();
System.out.println("方法" + method.getName() + "一共运行时间是"
+ (stopTime - startTime));
return o;
}
});
coll.add("aa");
coll.add("cc");
coll.add("dd");
System.out.println(coll.size());
将方法提取出来,改成通用的取得代理类的方法
1、创建建议接口和实现类:
public interface Adivse {
public void startTime(Method method, Object[] args, Object object);
public void stopTime(Method method, Object[] args, Object object);
}
public class MyAdivse implements Adivse {
long startTime = 0;
@Override
public void startTime(Method method, Object[] args, Object object) {
int len = 0;
if (args != null) {
len = args.length;
}
else {
len = 0;
}
startTime = System.currentTimeMillis();
System.out.println("method " + method.getName() + " 一共有" + len + " 个参数"
+ "现在操作的对象是" + object.getClass().getName());
}
@Override
public void stopTime(Method method, Object[] args, Object object) {
long stopTime = System.currentTimeMillis();
long runtime = stopTime - startTime;
System.out.println("方法" + method.getName() + "一共运行时间是" + runtime);
}
}
2、建立取得代理类
public static Object getProxy(final Object object, final Adivse adivse) {
Object proxyObject = Proxy.newProxyInstance(object.getClass()
.getClassLoader(), object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
adivse.startTime(method, args, object);
Object methodOb = method.invoke(object, args);
adivse.stopTime(method, args, object);
return methodOb;
}
});
return proxyObject;
}
3、调用方法
public static void main(String[] args) {
final ArrayList list = new ArrayList(); // 要代理的对象
Collection coll = (Collection)getProxy(list, new MyAdivse());
coll.add("aa");
coll.add("cc");
coll.add("dd");
System.out.println(coll.size()); }
4、输出结果
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是1
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method size 一共有0 个参数现在操作的对象是java.util.ArrayList
方法size一共运行时间是0
3
模拟spring框架实现AOP代理
血的教训:
执行第三步:
//bean = ibf.getProxy(target, adivse); 第一个传的参数必须是目标对象,并不是代理对象。例如有的bean只是实现了接口接口的代理对象
//传递的参数必须是参数不能是字符串
1、创建 建议接口和实现类
public interface Adivse {
public void startTime(Method method, Object[] args, Object object);
public void stopTime(Method method, Object[] args, Object object);
}
public class MyAdivse implements Adivse {
long startTime = 0;
@Override
public void startTime(Method method, Object[] args, Object object) {
int len = 0;
if (args != null) {
len = args.length;
}
else {
len = 0;
}
startTime = System.currentTimeMillis();
System.out.println("method " + method.getName() + " 一共有" + len + " 个参数"
+ "现在操作的对象是" + object.getClass().getName());
}
@Override
public void stopTime(Method method, Object[] args, Object object) {
long stopTime = System.currentTimeMillis();
long runtime = stopTime - startTime;
System.out.println("方法" + method.getName() + "一共运行时间是" + runtime);
}
}
2、创建代理取得类的方法 有代理接口和代理类
public interface IProxyBeanFactory {
public Object getProxy(Object object, Adivse adivse);
}
public class ProxyBeanFactory implements IProxyBeanFactory {
@Override
public Object getProxy(final Object object, final Adivse adivse) {
Object proxyObject = Proxy.newProxyInstance(object.getClass()
.getClassLoader(), object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
adivse.startTime(method, args, object);
Object methodOb = method.invoke(object, args);
adivse.stopTime(method, args, object);
return methodOb;
}
});
return proxyObject;
}
}
3、创建取得类对象的方法 可以选择是代理的方式还是直接创建的方式(取决于config.properties中选择的的代理,还是直接是对象) 构造参数需要传进去一个输入流来取得config.properties
public class BeanFactory {
Properties pt = new Properties();
public BeanFactory(InputStream is) throws Exception {
super();
pt.load(is);
}
public Object getBean(String name) throws Exception {
Class classes = Class.forName(pt.getProperty(name));
Object bean = classes.newInstance();
if (bean instanceof IProxyBeanFactory) {
IProxyBeanFactory ibf = (IProxyBeanFactory) bean;
Object target = Class.forName(pt.getProperty(name + ".target"))
.newInstance();
String adivseName = pt.getProperty(name + ".divse");
Adivse adivse = (Adivse) Class.forName(adivseName).newInstance();
//第一个传的参数必须是目标对象,并不是bean。bean只是实现了接口接口的代理对象
//还有传递的必须是参数不能是字符串
bean = ibf.getProxy(target, adivse);
return bean;
} else {
return bean;
}
}
}
4、方法的调用
public static void main(String[] args) throws Exception {
InputStream is = Demo3.class.getResourceAsStream("config.properties");
BeanFactory bf = new BeanFactory(is);
Collection coll = (Collection) bf.getBean("x");
coll.add("aa");
coll.add("cc");
coll.add("dd");
System.out.println(coll.size());
}
5、执行结果
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是1
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method size 一共有0 个参数现在操作的对象是java.util.ArrayList
方法size一共运行时间是0
3