代理(Proxy)是一种设计模式,通俗的讲就是通过别人达到自己不可告人的目的(玩笑)。
举个例子如下图:
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象
这三个代理模式,就像是更新换代,越来越先进。动态代理解决了静态代理必须同目标对象继承
同一个接口或类,CGlib解决了动态代理目标对象必须继承一个接口的问题。
一.静态代理
条件:代理对象必须和目标对象继承同一个接口或者类
代码如下:
/**
* 接口
*/public interface IUserDao {
void save();
}
/**
* 接口实现
* 目标对象
*/public class UserDao implements IUserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
/**
* 代理对象,静态代理
*/public class UserDaoProxy implements IUserDao{
//接收保存目标对象 private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法 System.out.println("提交事务...");
}
}
/**
* 测试类
*/public class App {
public static void main(String[] args) {
//目标对象 UserDao target = new UserDao();
//代理对象,把目标对象传给代理对象,建立代理关系 UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();//执行的是代理的方法 }
}
静态代理总结:
1.可以做到在不修改目标对象的情况下,为目标对象条件功能。
2.缺点:必须同目标对象继承同一接口或类
二.动态代理(JDK代理)
不需要实现接口,代理对象的生成是利用JDK的API,在内存中生存代理对象。但是目标对象必须继承一个接口
代理对象的包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h )
三个参数分别表示:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代码示例如下:
代理工厂类 ProxyFactory.java
/**
* 创建动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/public class ProxyFactory{
//维护一个目标对象 private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象 public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法 Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
测试类 App.java
/**
* 测试类
*/public class App {
public static void main(String[] args) {
// 目标对象 IUserDao target = new UserDao();
// 【原始的类型 class cn.itcast.b_dynamic.UserDao】 System.out.println(target.getClass());
// 给目标对象,创建代理对象 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// class $Proxy0 内存中动态生成的代理对象 System.out.println(proxy.getClass());
// 执行方法 【代理对象】 proxy.save();
}
}
3.Cglib代理
Cglib代理,目标对象不需要继承一个接口,它是已目标子类的方式实现代理的,所以Cglib代理也叫子类代理
Cglib子类代理的实现方法:
1.导包maven地址:
或者引入pring-core-3.2.5.jar 包
2.在内存中动态构建子类
注意:代理类不能为 final,否则报错,目标方法不能为final或static,否则不会执行目标方法额外的
方法。Cglib是如何实现代理的,还仔细没研究,有兴趣的可以去看看源码。
代码示例:
/**
* 目标对象,没有实现任何接口
*/public class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/public class ProxyFactory implements MethodInterceptor{
//维护目标对象 private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象 public Object getProxyInstance(){
//1.工具类 Enhancer en = new Enhancer();
//2.设置父类 en.setSuperclass(target.getClass());
//3.设置回调函数 en.setCallback(this);
//4.创建子类(代理对象) return en.create();
}
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法 Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
测试类:
/**
* 测试类
*/public class App {
@Test public void test(){
//目标对象 UserDao target = new UserDao();
//代理对象 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法 proxy.save();
}
}
在Spring的AOP编程中,如果加入容器的目标对象有实现的接口,用JDK代理,如果没有实现接口用Cglib代理
后面会介绍Spring内容,掌握动态代理会更容易理解Spring的AOP编程