可以实现在其运行时动态添加一个类、实现一个或者多个接口,可以在不修改原有类基础上动态为通过该类获取的对象添加方法、修改行为。
动态代理是实现面向切面的编程AOP的基础。切面例子包含有日志、权限检查、数据库事务等。
一、静态代理
存在价值
- 节省成本比较高的实际对象的创建开销,按需延迟加载,创建代理是并不真正创建实际对象,而只是保存实际对象的地址,在需要时再加载或者创建。
- 执行权限检查,代理检查权限后,在调用实际对象
- 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理,本地代理请求其他服务器
demo
public class SimpleStaticProxyDemo {
static interface IService{//共同接口
public void sayHello();
}
static class RealService implements IService{//实际对象
@Override
public void sayHello() {
System.out.println ("hello");
}
}
static class TraceProxy implements IService{//代理
private IService realService;//定义成员变量
public TraceProxy(IService realService){//将成员变量指向实际对象,并在构造方法中初始化
this.realService = realService;
}
@Override
public void sayHello() {
System.out.println ("entering sayHello");
this.realService.sayHello ();
System.out.println ("leaving sayHello");
}
}
public static void main(String[] args) {
RealService realService = new RealService ();
TraceProxy traceProxy = new TraceProxy ( realService );
traceProxy.sayHello ();
}
}
//输出结果
entering sayHello
hello
leaving sayHello
二、动态代理
优点
可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。
与静态代理差异
与静态的区别在于代理对象ProxyService的创建方式变了,他是用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,方法声明为
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
InvocationHandler中只定义了一个方法invoke();对代理借口所有方法的调用的都会转给该方法。
newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型。
demo中强制转换成了IService类型,它不能强制转换为某个类的类型,比如RealService。
demo
package thread_practice.fanshe;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class GeneralProxyDemo {
static interface IServiceA {
public void sayHello();
}
static class ServiceAImpl implements IServiceA {
@Override
public void sayHello() {
System.out.println ( "hello" );
}
}
static interface IServiceB {
public void fly();
}
static class ServiceBImpl implements IServiceB {
@Override
public void fly() {
System.out.println ( "flying" );
}
}
static class SimpleInvocationHandle implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandle(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println ( "entering" + realObj.getClass ()
.getSimpleName () + ":" + method.getName () );
Object result = method.invoke ( realObj, args );
System.out.println ( "leaving" + realObj.getClass ()
.getSimpleName () + ":" + method.getName () );
return result;
}
}
private static <T> T getProxy(Class<T> intf, T realObj) {
return (T) Proxy.newProxyInstance ( intf.getClassLoader (),
new Class<?>[]{
intf
}, new SimpleInvocationHandle ( realObj ) );
}
public static void main(String[] args) {
IServiceA a = new ServiceAImpl ();
IServiceA aProxy = getProxy ( IServiceA.class, a );
aProxy.sayHello ();
ServiceBImpl b = new ServiceBImpl ();
IServiceB bProxy = getProxy ( IServiceB.class, b );
bProxy.fly ();
}
}
//输出结果
enteringServiceAImpl:sayHello
hello
leavingServiceAImpl:sayHello
enteringServiceBImpl:fly
flying
leavingServiceBImpl:fly
三、cglib动态代理
java SDK动态代理的局限在于,它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型。如果希望代理非接口中定义的方法,就可以采用第三方库cglib。