代理模式(Proxy Pattern)的定义:为另一个对象提供一个替身或占位符,以控制对这个对象的访问(Head First设计模式给出的定义)。简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。
Java的代理模式一共有三种 静态代理 jdk代理和cglib代理,后两种都属于动态代理。
1.静态代理
静态代理通过目标类与代理类实现同一个接口,代理类中实例化一个目标类的对象来实现。
代码:
//以下代码实现的功能是将目标类中的doFirst()方法返回的值变为大写
//这是目标类和代理类实现的接口
package service;
public interface IService {
public String doFirst();
public void doSecond();
}
//这是目标类
package service;
public class ServiceImpl implements IService {
@Override
public String doFirst() {
System.out.println("执行doFirst()");
String result="abcde";
return result;
}
@Override
public void doSecond() {
// TODO 自动生成的方法存根
System.out.println("执行doSecond()");
}
}
//这是代理类
package service;
public class ServiceProxyImpl implements IService {
IService service;
public ServiceProxyImpl() {
service =new ServiceImpl();
}
@Override
public String doFirst() {
//使目标类中doFirst()返回的字符串变为大写
String result = service.doFirst();
result=result.toUpperCase();
return result;
}
@Override
public void doSecond() {
// TODO 自动生成的方法存根
service.doSecond();
}
}
//这是测试类
package main;
import service.IService;
import service.ServiceProxyImpl;
public class Test {
public static void main(String[] args) {
IService service=new ServiceProxyImpl();
String result=service.doFirst();
System.out.println(result);
service.doSecond();
}
}
静态代理在使用时,实例化的时代理类,而不是目标类。
缺点:这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。
2.jdk动态代理
jdk动态代理与静态代理的底层原理相同。jdk动态代理通过Proxy实现
Proxy通过静态方法newProxyInstance()返回代理类对象
newProxyInstance()方法有三个参数
- ClassLoader loader:指定当前目标对象使用类加载器,写法固定
- Class<?>[] interfaces:目标对象实现的接口的类型,写法固定
- InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类
代码:
//这是目标类和代理类实现的接口
package service;
public interface IService {
public String doFirst();
public void doSecond();
}
//这是目标类
package service;
public class ServiceImpl implements IService {
@Override
public String doFirst() {
System.out.println("执行doFirst()");
String result="abcde";
return result;
}
@Override
public void doSecond() {
// TODO 自动生成的方法存根
System.out.println("执行doSecond()");
}
}
//这个是测试类 使用代理时时用Proxy的newProxyInstance方法动态创建的代理类
package main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import service.IService;
import service.ServiceImpl;
public class Test {
public static void main(String[] args) {
//首先实例化一个目标类的对象
final IService target=new ServiceImpl();
//通过Proxy的newProxyInstance方法获取一个代理类对象
IService service=(IService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
if(result!=null) {
result=((String)result).toUpperCase();
}
return result;
}
});
String result=service.doFirst();
System.out.println(result);
service.doSecond();
}
}
缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,假如没有,则可以使用Cglib代理,当然Cglib代理也可以用于目标对象有接口的情况。
3.Cglib代理
使用Cglib的前提条件:
- 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
- 目标类不能为final
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
代码:
//这是目标类
package service;
public class Service {
public String doFirst() {
System.out.println("执行doFirst()");
String result="abcde";
return result;
}
public void doSecond() {
System.out.println("执行doSecond()");
}
}
//使用Cglib创建代理类
package factory;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import service.Service;
public class ServiceProxyFactory implements MethodInterceptor {
Service target;
public ServiceProxyFactory() {
target=new Service();
}
public Service serviceProxy() {
//创建增强器对象
Enhancer enhancer=new Enhancer();
//指定目标类 即父类
enhancer.setSuperclass(Service.class);
//设置回调接口对象
enhancer.setCallback(this);
return (Service) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result=method.invoke(target, args);
if(result!=null) {
result=((String)result).toUpperCase();
}
return result;
}
}
//测试类
package main;
import factory.ServiceProxyFactory;
import service.Service;
public class Test {
public static void main(String[] args) {
Service s=new ServiceProxyFactory().serviceProxy();
String result =s.doFirst();
System.out.println(result);
s.doSecond();
}
}
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理