一提到jdk中的java.lang.reflect.Proxy,用过spring,hibernate等框架的人应该都有所了解,对!就是动态代理。AOP - 面向切面编程 - 就是基于动态代理实现的。
为什么要提代理模式。因为AOP的广泛实现都是通过动态代理,而动态代理又不得不说代理模式。
代理模式,顾名思义,就是对一个类的访问,变为访问这个类的代理人。经由代理再访问这个类。(代理与被代理的类实现了相同的接口,因此客户感觉不到通过代理访问这个类和直接访问这个类的区别)
为什么需要代理呢,因为一个良好的设计不应该轻易的修改。这正是开闭原则的体现:一个良好的设计应该对修改关闭,对扩展开放。而代理正是为了扩展类而存在的。他可以控制对现有类(就是需要被代理的类)服务的访问,通俗的解释就是 可以拦截对于现有类方法的调用并做些处理。
而动态代理,是指在运行期动态的为指定的类生成其代理类。(需要相关的运行时编译技术,)
AOP如Spring的AOP实现就是以这种方式实现的。他使用动态生成的代理类拦截了现有类的“切点”。并进行控制,使得这些切面的逻辑完全与该类脱离,实现了关注点分离。
下面附上我用Javassist实现的简单动态代理。(Javassist是一个运行时编译库,他能动态的生成或修改类的字节码,类似的有ASM和CGLIB,大多数框架就是基于后者实现的)
在项目中加入所需要的jar包
我使用的maven管理jar包
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
要代理的类:
A
package com.cglib.demo02;
public class A {
public void save(){
System.out.println("保存商品");
}
public void del(){
System.out.println("删除商品");
}
}
第一种方式:
JavassistProxyFactory.java
package com.javassist.demo;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class JavassistProxyFactory<T> {
private T target;
public void setTarget(T target) {
this.target = target;
}
@SuppressWarnings( "deprecation")
public T getProxy() throws InstantiationException, IllegalAccessException {
// 代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 设置需要创建子类的父类
proxyFactory.setSuperclass(target.getClass());
/*
* 定义一个拦截器。在调用目标方法时,Javassist会回调MethodHandler接口方法拦截,
* 来实现你自己的代理逻辑,
* 类似于JDK中的InvocationHandler接口。
*/
proxyFactory.setHandler(new MethodHandler() {
/*
* self为由Javassist动态生成的代理类实例,
* thismethod为 当前要调用的方法
* proceed 为生成的代理类对方法的代理引用。
* Object[]为参数值列表,
* 返回:从代理实例的方法调用返回的值。
*
* 其中,proceed.invoke(self, args);
*
* 调用代理类实例上的代理方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
*/
public Object invoke(Object self, Method thismethod, Method proceed, Object[] args) throws Throwable {
System.out.println("--------------------------------");
System.out.println(self.getClass());
//class com.javassist.demo.A_$$_javassist_0
System.out.println("要调用的方法名:"+thismethod.getName());
System.out.println(proceed.getName());
System.out.println("开启事务-------");
Object result = proceed.invoke(self, args);
//下面的代码效果与上面的相同
//不过需要传入一个目标对象
//Object result = thismethod.invoke(target,args);
System.out.println("提交事务-------");
return result;
}
});
// 通过字节码技术动态创建子类实例
return (T) proxyFactory.createClass().newInstance();
}
}
第二种方式:
JavassistProxyFactory02.java
这种方式不需要事先创建要代理的对象
package com.javassist.demo;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/*
* 这种方式不需要事先创建
* 要代理的对象
*
* */
public class JavassistProxyFactory02 {
/*
* 要代理的对象的class
* */
@SuppressWarnings("deprecation")
public Object getProxy(Class clazz) throws InstantiationException, IllegalAccessException {
// 代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 设置需要创建子类的父类
proxyFactory.setSuperclass(clazz);
/*
* 定义一个拦截器。在调用目标方法时,Javassist会回调MethodHandler接口方法拦截,
* 来实现你自己的代理逻辑,
* 类似于JDK中的InvocationHandler接口。
*/
proxyFactory.setHandler(new MethodHandler() {
/*
* self为由Javassist动态生成的代理类实例,
* thismethod为 当前要调用的方法
* proceed 为生成的代理类对方法的代理引用。
* Object[]为参数值列表,
* 返回:从代理实例的方法调用返回的值。
*
* 其中,proceed.invoke(self, args);
*
* 调用代理类实例上的代理方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
*/
public Object invoke(Object self, Method thismethod, Method proceed, Object[] args) throws Throwable {
System.out.println("--------------------------------");
System.out.println(self.getClass());
//class com.javassist.demo.A_$$_javassist_0
System.out.println("代理类对方法的代理引用:"+thismethod.getName());
System.out.println("开启事务 -------");
Object result = proceed.invoke(self, args);
System.out.println("提交事务 -------");
return result;
}
});
// 通过字节码技术动态创建子类实例
return proxyFactory.createClass().newInstance();
}
}
测试:
package com.javassist.demo;
public class JavassistTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
System.out.println("*******************方式一*******************");
JavassistProxyFactory<A> jpf = new JavassistProxyFactory<A>();
A a = new A();
jpf.setTarget(a);
A proxy = jpf.getProxy();
proxy.del();
System.out.println("*******************方式二*******************");
JavassistProxyFactory02 jpf02 = new JavassistProxyFactory02();
A a2 = (A) jpf02.getProxy(A.class);
a2.del();
a2.save();
}
}
结果:
*******************方式一*******************
--------------------------------
class com.javassist.demo.A_$$_javassist_0
要调用的方法名:del
_d1del
开启事务-------
删除商品
提交事务-------
*******************方式二*******************
--------------------------------
class com.javassist.demo.A_$$_javassist_1
代理类对方法的代理引用:del
开启事务 -------
删除商品
提交事务 -------
--------------------------------
class com.javassist.demo.A_$$_javassist_1
代理类对方法的代理引用:save
开启事务 -------
保存商品
提交事务 -------