背景
最近在翻阅Spring和Mybatis事务相关源码时发现代码中大量使用动态代理,且早前开启的JAVASE专题中代理部分的文章一直没有补充,趁着容器化和事务专题的空隙补充一下。
本文主题内容是代理:包括静态代理和动态代理,其中动态代理是重点,该部分包括JDK代理和CGLIB代理,以下通过案例的方式进行介绍。
1.代理设计模式
代理设计模式的核心是分离代理对象与真实被调用的对象,降低系统的耦合性。一方面可以起到保护被代理对象的作用,另一方面可以实现对既有功能的增强。
代理按照实现可以分为:静态代理和动态代理,静态代理在编译期生成,动态代理在程序运行的过程中生成。静态代理比较简单,需要为每个接口类编写一个代理类;当接口类很多、代理的处理逻辑相同的场景或者作为框架时(不可知被代理类场景),静态代理不再适用,此时可以使用动态代理代替。
比如:一个接口中有N个方法,需要对每个方法添加事务处理机制,此时需要重复N次与业务无关的代码,使得代码显得肿胀😭。这种场景使用1个动态代理就能实现上述功能,且把业务和业务无关的重复代码进行分割👍;分离变化与不变量、降低代码重复度可以提高代码的可读性和可维护性。
另外,Java提倡面向接口编程,但并非所有类都基于接口,因此Java提供了两种类型的动态代理:CGLIB代理和JDK代理。其中:JDK代理接口类,而CGLIB可以代理非接口类。以下通过案例的方式对上述三种代理模式分别介绍。
2.静态代理
静态代理基于接口,要求代理类和被代理类(后续使用目标类表示🥸)属于同一接口的实现类。在代理类中实现接口中定义的方法,并在方法实现体中调用目标类的方法,从而实现代理功能。
以下通过案例进行介绍:
// 接口类
public interface AccountDao {
void reduce(int cost);
}
// 目标类
public class AccountDaoImpl implements AccountDao {
@Override
public void reduce(int cost) {
System.out.println("cost " + cost + " yuan.");
// ... database operation
}
}
// 代理类
public class AccountDaoProxy implements AccountDao {
private AccountDao target;
public AccountDaoProxy(AccountDao target) {
this.target = target;
}
@Override
public void reduce(int cost) {
//...
connection.setAutoCommit(false);
try {
target.reduce(cost);
connection.commit();
} catch(Exception e) {
connection.rollback();
} finally {
//...
}
}
}
// demo start
public class Application {
public static void main(String[] args) {
AccountDao accountDao = new AccountDaoImpl();
AccountDaoProxy accountProxy = new AccountDaoProxy(accountDao);
accountProxy.reduce(100);
}
}
上述案例中代理类AccountDaoProxy对目标类AccountDaoImpl的功能进行了增强,为其添加了事务机制。当AccountDao中新增方法是,需要再次添加重复的事务处理代码,较为繁琐;此时可以借助动态代理实现。
3.JDK动态代理
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object build() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new Interceptor());
}
class Interceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
method.invoke(target, args);
System.out.println("after...");
return null;
}
}
}
public class Application {
public static void main(String[] args) {
Printer printer = new PrinterImpl();
ProxyFactory proxyFactory = new ProxyFactory(printer);
Printer printer2 = (Printer)proxyFactory.build();
printer2.print();
printer2.print("nameB","ageB");
}
}
4.CGLIBd动态代理
public class Printer {
public void print() {
System.out.println("print here!");
}
public void print(String name, String age) {
System.out.println("print here! name is: " + name + "; " + "age is:" + age);
}
}
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object build() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
method.invoke(target,args);
System.out.println("after...");
return null;
}
}
public class Application {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new Printer());
Printer printer = (Printer)proxyFactory.build();
printer.print();
printer.print("nameA","ageA");
}
}
todo: 明天补充
5.应用场景
6.AOP和ASPECT