代理模式有三种实现:静态代理,动态代理。动态代理有分有:cglib代理和jdk代理
静态代理代码示例:
public interface UserInterfice {
void speak();
}
public class User implements UserInterfice {
@Override
public void speak() {
// TODO Auto-generated method stub
System.out.println("用户功能");
}
}
public class UserProxy implements UserInterfice{
private User user;
public UserProxy(User user) {
// TODO Auto-generated constructor stub
this.user=user;
}
private Customer customer;
public UserProxy(Customer customer) {
// TODO Auto-generated constructor stub
this.customer=customer;
}
@Override
public void speak() {
// TODO Auto-generated method stub
System.out.println("在用户功能前增加功能");
user.speak();
System.out.println("在用户功能后增加功能");
}
public void jump() {
System.out.println("在客户专用功能之前增加功能");
customer.jump();
System.out.println("在客户专用功能之后增加功能");
}
}
public class Test {
public static void main(String[] args) {
User user = new User();
UserProxy userProxy = new UserProxy(user);
userProxy.speak();
}
}
- 使用代理对象将真实对象(被代理对象)包装起来,然后用该代理对象取代真实对象。
- 任何通过对真实对象的调用都要通过代理对象调用,不能直接调用真实对象。
- 代理对象决定是否以及何时将调用转移到真实对象上。
通过代理模式的特点可以看出任何对真实对象的调用都需要通过代理对象调用,不能通过真实对象直接调用。我们从传统的静态代理模式来看,一个真实类都需要一个代理类,假如我们增加一个真实类就需要增加一个代理类。这样将会让系统中的类个数急剧增加,显然是不友好的。动态代理可以让程序能够根据实际需要(真实对象)来动态创建代理对象,让一个代理类能够代理多个真实类。
1、JDK的动态代理机制
Jdk为RealSubject对象创建动态代理对象,主要做了以下工作:
1) 获取RealSubject上的所有接口列表。
2) 确定要生成的动态代理类的类名,默认为com.sun.proxy.$ProxyXXX
3) 根据需要实现接口信息,在代码中动态创建该Proxy的字节码
4) 将对应的字节码转换为对应的class对象
5) 创建InvocationHandler实例,用来处理Proxy所有方法调用
6) Proxy的class对象以创建的handler对象为参,实例化Proxy对象
Jdk通过java.lang.reflect.Proxy来支持动态代理,一般情况下,使用方法newProxyInstanceof来创建Proxy类,而对于InvocationHandler,需要实现它的invoke方法,在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法。
接口类
public interface UserI {
void speak();
}
代理类
public class User implements UserI{
@Override
public void speak() {
System.out.println("讲话..............");
}
}
委托类
public class UserProxy implements InvocationHandler{
private Object target;
//动态绑定
public Object bind(Object target) {
//接收对象参数
this.target=target;
//返回代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
System.out.println("预处理---------");
result=method.invoke(target, args);
System.out.println("调用后操作---------");
return result;
}
}
测试类
public class JdkProxyTest {
public static void main(String[] args) {
User user = new User();
UserProxy userProxy = new UserProxy();
UserI userProxyI = (UserI) userProxy.bind(user);
userProxyI.speak();
}
}
2、cglib的动态代理机制
JDK中提供的生成动态代理类的机制有个鲜明的特点是: 某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法,果某个类没有实现接口,那么这个类就不能同JDK产生动态代理了!CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
cglib 创建某动态代理类的模式是:
1) 查找类上的所有非final的public类型的方法定义
2) 将这些方法的定义转换成字节码
3) 将组成的字节码转换成相应的代理的class对象
4) 实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(和InvocationHandler的功能和角色是一样的)
代理类
public class User {
public void speak() {
System.out.println("speak............");
}
}
委托类
public class UserCglib implements MethodInterceptor{
private Object target;
//类似与jdk动态绑定
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 object, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("预处理--------");
proxy.invokeSuper(object, arg);
System.out.println("调用后处理-----------");
return null;
}
}
测试类
public class Test {
public static void main(String[] args) {
User user = new User();
UserCglib userCglib = new UserCglib();
User instance = (User) userCglib.getInstance(user);
instance.speak();
}
}
一、简单来说:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
二、Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
三、CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。