代理模式可以控制外界对对象的访问,在对象类型一样的情况下添加部分的中间操作,springaop是使用动态代理的一个应用实例。动态代理的实现方式有好几种,这里暂且只是贴出jdk自带的InvocationHandler、cglib,Javassist功能很强大,这里暂且没有例子。jdk的动态代理是依靠接口实现,如果类没有实现接口,则应当使用cglib实现,使用javassist直接创建一个类实现接口,为 实现类添加代码等也是可以的。
1.InvocationHandler
InvocationHandler的这个例子是HeadFirst设计模式中的PersonBean保护代理例子。OwnerInvocationHandler阻止Owner代理对象调用setHorOrNotRating方法,而NonOwnerInvocationHandler定义了可以调用NonOwner代理的setHotOrNotRating,但不能调用其他的set方法,代理方法主要通过Java的反射实现。
public class GetProxy {
public static PersonBean getOwnerProxy(PersonBean personBean) {
return (PersonBean) Proxy.newProxyInstance(personBean.getClass().getClassLoader(),
personBean.getClass().getInterfaces(), new OwnerInvocationHandler(personBean));
}
public static PersonBean getNonOwnerProxy(PersonBean personBean) {
return (PersonBean) Proxy.newProxyInstance(personBean.getClass().getClassLoader(),
personBean.getClass().getInterfaces(), new NonOwnerInvocationHandler(personBean));
}
}
public class OwnerInvocationHandler implements InvocationHandler {
private PersonBean personBean;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
try {
if (methodName.startsWith("get")) {
return method.invoke(personBean, args);
} else if (methodName.equals("setHotOrNotRating")) {
throw new IllegalAccessException();
} else if (methodName.startsWith("set")) {
return method.invoke(personBean, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public OwnerInvocationHandler(PersonBean personBean) {
super();
this.personBean = personBean;
}
}
public class NonOwnerInvocationHandler implements InvocationHandler {
private PersonBean personBean;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
try {
if (methodName.startsWith("get")) {
return method.invoke(personBean, args);
} else if (methodName.equals("setHotOrNotRating")) {
return method.invoke(personBean, args);
} else if (methodName.startsWith("set")) {
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public NonOwnerInvocationHandler(PersonBean personBean) {
super();
this.personBean = personBean;
}
}
2.cglib
同样来为PersonBeanImpl生成一个代理,这个也是在执行方法前通过方法名判断而进行访问的控制。这个是参考https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
if (methodName.equals("setHotOrNotRating")) {
throw new IllegalAccessException();
}
Object rvt = proxy.invokeSuper(obj, args);
return rvt;
}
}
public class PersonBeanProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T getAuthInstance(Class<T> t) {
Enhancer en = new Enhancer();
// 设置要代理的目标类
en.setSuperclass(t);
// 设置要代理的拦截器
en.setCallback(new MyInterceptor());
// 生成代理类的实例
return (T) en.create();
}
}
public class TestCGLIB {
public static void main(String[] args) {
PersonBean jeo = new PersonBeanImpl();
jeo.setName("joe");
jeo.setHotOrNotRating(10);
PersonBean personBean = PersonBeanProxyFactory.getAuthInstance(PersonBeanImpl.class);
personBean.setName("lusi");
System.out.println("owner is : " + personBean.getName());
try {
personBean.setHotOrNotRating(10);
} catch (Exception e) {
System.out.println("set rating error");
}
System.out.println("rating is : " + personBean.getHotOrNotRating());
}
}
上面的两个都是简单通过方法名控制对对象的访问,这个时候我们完全可以为访问添加访问日志等。在不采用其他框架情况下,我们要这个类型的对象都为代理对象的话,可以通过工厂或者是单例模式来实现,这样可以把日志代码隐藏,保持对象的简洁。