代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子
用图表示如下:
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。
1、静态代理
/*接口
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();//执行的是代理的方法
}
}
实质就是在代理类中定义了与目标类相同的方法,然后通过修改、执行代理类的方法来达到在原来的方法基础上进行扩充的目的,不止执行了目标对象的目标方法,同时还可以自己添加其他的功能。
静态代理的缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
2、动态代理
动态代理的特点:可以实现AOP编程
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
基于JDK的动态代理就需要知道两个类:1.InvocationHandler(接口)、2.Proxy(类)
还要知道JDK是基于接口的动态代理
1.第一步,创建一个接口
public interface Subject {
void hello(String param);
}
2.第二步,实现接口
public class SubjectImpl implements Subject {
@Override
public void hello(String param) {
System.out.println("hello " + param);
}
}
3.第三步,创建SubjectImpl的代理类
public class SubjectProxy implements InvocationHandler {
private Subject subject;
public SubjectProxy(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--------------begin-------------");
Object invoke = method.invoke(subject, args);
System.out.println("--------------end-------------");
return invoke;
}//这个方法不是显式地调用,在这个方法里面添加功能扩展
}
4.编写代理类实际的调用,利用Proxy类创建代理之后的Subject类。
public class Main {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
InvocationHandler subjectProxy = new SubjectProxy(subject);
Subject proxyInstance = (Subject) Proxy.newProxyInstance
(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
proxyInstance.hello("world");
}
}
实际上在Subject类中只会输出一条hello world,但是在被代理之后,实际调用的方法是SubjectProxy的invoke方法,这样可以在不修改业务类的情况下对业务类增加一些日志等其他操作,甚至可以直接修改有返回值方法的返回值。
3、基于CGLIB的动态代理
因为基于JDK的动态代理一定要继承一个接口,而绝大部分情况是基于POJO类的动态代理,那么CGLIB就是一个很好的选择,在Hibernate框架中PO的字节码生产工作就是靠CGLIB来完成的。还是先看代码。
1、创建代理类
public class CGsubject {
public void sayHello(){
System.out.println("hello world");
}
}
2、实现MethodInterceptor接口,对方法进行拦截处理。
public class HelloInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("begin time -----> "+ System.currentTimeMillis());
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("end time -----> "+ System.currentTimeMillis());
return o1;
}
}
3、创建被代理类
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CGsubject.class);
enhancer.setCallback(new HelloInterceptor());
CGsubject cGsubject = (CGsubject) enhancer.create();
cGsubject.sayHello();
}
}