设计模式之代理模式(Proxy)
代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
静态代理
场景
登录功能已经完成,需要对登录前后进行日志记录
代理接口
public interface Login {
void login();
}
被代理类
public class AdminLogin implements Login {
@Override
public void login() {
System.out.println(" Admin Login...");
}
}
代理类
public class AdminLoginProxy implements Login {
private AdminLogin adminLogin;
public AdminLoginProxy(){
this.adminLogin = new AdminLogin();
}
@Override
public void login() {
System.out.println("write log IP.");
adminLogin.login();
System.out.println("write log User.");
}
}
测试
public class APP {
public static void main(String[] args) {
Login login = new AdminLoginProxy();
login.login();
}
}
动态代理
JDK动态代理
实现InvocationHandler
接口
public class JDKProxy implements InvocationHandler {
private Object target;
public JDKProxy(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("====before===");
Object result = method.invoke(target, args);
System.out.println("===after===");
return result;
}
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试
public static void main(String[] args) {
Login login = new AdminLogin();
JDKProxy jdkProxy = new JDKProxy(login);
Login proxy = (Login) jdkProxy.getProxy();
proxy.login();
}
CGLIB
导包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
实现MethodInterceptor
接口
public class CGLIBProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
//给业务对象赋值
this.target = target;
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(this.target.getClass());
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("====before===");
methodProxy.invokeSuper(o,args);
System.out.println("===after===");
return null;
}
测试
public static void main(String[] args) {
AdminLogin adminLogin = new AdminLogin();
CGLIBProxy cglibProxy = new CGLIBProxy();
AdminLogin loginProxy = (AdminLogin) cglibProxy.getInstance(adminLogin);
loginProxy.login();
}
以下总结来自动态代理:JDK动态代理和CGLIB代理的区别
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
Spring在选择用JDK还是CGLiB的依据:
-
当Bean实现接口时,Spring就会用JDK的动态代理
-
当Bean没有实现接口时,Spring使用CGlib是实现
-
可以强制使用CGlib(在spring配置中加入
<aop:aspectj-autoproxy proxy-target-class="true"/>
)
CGlib比JDK快?
- 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
- 在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。