什么是代理模式?
代理模式提供了对目标对象的另外的访问模式,即通过代理对象可以访问到目标对象,代理模式的好处是,可以在不修改目标对象方法的基础上进行扩展,比如:打印日志、计算方法执行时间等。
代理模式中有两个关键点:
代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象。
举个简单的例子租房子的例子,目标对象就是房东,代理对象是中介,房东把房子交给中介帮忙出租,租房子的人可以直接通过中介进行租赁。
静态代理
静态代理在使用时,需要定义接口或者父类,代理对象与目标对象一起实现相同的接口或者是继承相同父类。举个简单的例子:
定义实现接口IUserDao:
interface IUserDao {
void add();
void delete();
void update();
void select();
}
目标对象UserDao:
public class UserDao implements IUserDao {
@Override
public void add() {
System.out.println("目标对象执行add方法");
}
@Override
public void delete() {
System.out.println("目标对象执行delete方法");
}
@Override
public void update() {
System.out.println("目标对象执行update方法");
}
@Override
public void select() {
System.out.println("目标对象执行select方法");
}
}
代理对象UserDaoProxy:
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void add() {
long t1 = System.currentTimeMillis();
System.out.println("方法开始执行时间:" + t1);
target.add(); //调用目标方法
System.out.println("方法执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
}
@Override
public void delete() {
}
@Override
public void update() {
}
@Override
public void select() {
}
}
测试案例:
public class ProxyTest {
public static void main(String[] args) {
//目标对象
IUserDao userDao = new UserDao();
//代理对象,把目标对象传给代理对象,建立代理关系
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.add();
}
}
在实例化代理对象的时候,通过构造器把目标对象传给代理对象,从而建立起联系,代理对象就可以获取调用目标对象的方法,并且在此基础上进行扩展,如:记录日志等。
优点:可以做到在不修改目标对象的基础上,对目标对象进行扩展。
缺点:由于目标对象跟代理对象要实现相同的接口,如果接口新增方法,代理对象也要新增,目标对象跟代理对象需要同时进行维护,维护比较麻烦。
动态代理
动态代理的特点:
1. 动态代理代理对象不需要实现接口;
2. 代理对象是通过JDK的api在内存中动态生成的;
3. .动态代理也叫JDK代理或接口代理;
动态代理很好的弥补了静态代理的缺点。生成代理类的API方法java.lang.reflect.Proxy,调用newProxyInstance方法可以生成代理类,该方法有三个参数:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的;
interfaces:目标对象实现的接口的类型,使用泛型方式确认类型;
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入;
示例:
代理工厂类,利用getProxyInstance可以生成代理对象:
public class ProxyFactory {
//目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 生成代理对象
* @return
*/
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 {
long t1 = System.currentTimeMillis();
System.out.println("方法"+ method.getName() +"开始执行时间:" + t1);
//执行目标对象方法
//设置成可以访问
method.setAccessible(true);
Object returnValue = method.invoke(target,args);
System.out.println("方法" + method.getName() +"执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
return returnValue;
}
});
}
}
测试:
public class ProxyTest {
public static void main(String[] args) {
//目标对象
IUserDao userDao = new UserDao();
System.out.println("目标对象类型:" + userDao.getClass());
ProxyFactory proxyFactory = new ProxyFactory(userDao);
IUserDao proxyObject = (IUserDao)proxyFactory.getProxyInstance();
System.out.println("代理对象的类型:"+proxyObject.getClass());
//执行方法
proxyObject.add();
}
}
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
CGLIB动态代理
无论是静态代理还是JDK动态代理,要求目标方法都必须实现接口,但是有时候目标对象只是一个独立的对象,没有实现任何接口,这个时候可以通过以目标对象子类的方式实现代理,这种方法就叫CGLIB代理,也叫子类代理,它在内存中创建一个子类对象,并且实现对目标对象的扩展。
示例:
public class CglibProxy implements MethodInterceptor{
//目标对象
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
//给目标对象创建代理对象
public Object getProxyInstance(){
//工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long t1 = System.currentTimeMillis();
System.out.println("方法"+ method.getName() +"开始执行时间:" + t1);
//执行目标对象方法
Object returnValue = method.invoke(target,args);
System.out.println("方法" + method.getName() +"执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
return returnValue;
}
}
测试:
public class ProxyTest {
public static void main(String[] args) {
// //目标对象
IUserDao userDao = new UserDao();
CglibProxy cglibProxy = new CglibProxy(userDao);
IUserDao proxyObject = (IUserDao)cglibProxy.getProxyInstance();
//执行方法
proxyObject.add();
}
}
比较:
JDK动态代理目标对象必须实现一个或多个接口,但是CGLIB代理可以直接通过创建目标对象的一个子类就可以实现动态代理,只需引入cglib的jar就可以了,Spring AOP就实现了代理模式,底层就是实现了cglib,实现了方法的拦截。
在Spring AOP编程中,如果容器中目标对象有实现接口,可以用JDK代理,如果没有实现任何接口,可以使用CGLIB代理。