1. 什么是代理 & 为什么使用代理
举个例子:小明的朋友去外地旅游,然后小明托朋友买一些当地的特产。此处小明的朋友则可以视为是代理。从案例来看,小明想买外地的东西,但他办不到,他发出买的动作,但实际买东西的是小明的朋友。
就好比在代码中想要增加一些功能,比如性能监测,而如果修改代码则违反了开闭原则,也可能会带来一系列问题。
于是我们可以创建一个代理类去增强我们原来类的功能,但具体的实现还是由原类负责,代理类只负责新增功能。
UML类图:
2. 怎么使用代理
静态代理:
每次创建代理类都需要人为的实现某个接口,或者以匿名内部类的形式,当类方法很多的时候没个方法都要重写,一来降低工作效率,二来可能会漏那么一两个方法。
动态代理:
jdk动态代理,代码如下
public interface ProxyInterface {
void work();
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.lastsummer.proxydao.ProxyInterface;
public class JdkProxyConcreteClass implements ProxyInterface {
@Override
public void work() {
System.out.println("开始工作");
}
private Object jdkProxy(Object bean) {
//产生代理对象 需强转
Object obj = Proxy.newProxyInstance(
JdkProxyConcreteClass.class.getClassLoader(), //一般为当前类的类加载器
bean.getClass().getInterfaces(), //具体类所实现的接口
new InvocationHandler() { //自定义执行处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录执行前毫秒数");
Object obj = method.invoke(bean, args); //源方法调用
System.out.println("记录执行后毫秒数");
return obj;
}
});
return obj;
}
public static void main(String[] args) {
JdkProxyConcreteClass obj = new JdkProxyConcreteClass();
ProxyInterface proxy = (ProxyInterface) obj.jdkProxy(obj);
proxy.work();
// 记录执行前毫秒数
// 开始工作
// 记录执行后毫秒数
}
}
这种方法的缺点是只能在有接口的时候使用
cglib动态代理,代码如下(必须导入cglib.jar 和 asm.jar)
public class ProxyClass {
public void work() {
System.out.println("开始工作");
}
}
import java.lang.reflect.Method;
import com.lastsummer.proxydao.ProxyClass;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxyConcreteClass {
private Object cglibProxy(Object bean) {
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(ProxyClass.class);
//设置自定义处理器
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println("记录执行前毫秒数");
//原对象方法执行
Object obj = method.invoke(bean, args);
System.out.println("记录执行后毫秒数");
return obj;
}
});
return enhancer.create();
}
public static void main(String[] args) {
CglibProxyConcreteClass obj = new CglibProxyConcreteClass();
ProxyClass proxy = (ProxyClass) obj.cglibProxy(new ProxyClass());
proxy.work();
// 记录执行前毫秒数
// 开始工作
// 记录执行后毫秒数
}
}
种方法在对类产生代理时父类或者父类中的方法不能是final的,如果是就会抛异常。(如果你要问为什么jdk动态代理没问题,那是因为接口上或者接口的方法不能被final修饰)。
3. 代理的使用
就像上面一样,我们可以在不修改之前的代码,而且在之前代码的基础上增加新的功能。最典型的思想就是aop(面向切面)。为什么叫切面,我们可以把之前的代码当成汉堡包,然后切面就是把汉堡包切开,然后在切面中加入想加的东西。
Spring事务就是基于aop,在方法执行前开启事务,方法执行后(回滚/提交)事务。