代理模式(proxy)
什么是代理模式
- 所谓代理模式就是为目标对象提供另外一种访问方式,通过访问代理对象来间接访问目标对象。
- 优点是在不修改原来方法的情况下,给现有的对象中的方法追加额外的功能,即扩展目标对象的功能。
- 代理模式又分为
静态代理
和动态代理
两种,动态代理又分为JDK动态代理
和cglib动态代理
。
静态代理
1、简介
- 静态代理的实现需要三步:首先,我们需要定义
业务接口
,业务接口实现类
,事务类
然后定义代理类(需实现业务接口)
;最后写一个测试类
来调用。 - 优点:可以在不修改目标对象的情况下追加新的功能。
- 缺点:当接口方法中添加新的方法之后,目标对象和代理对象都要进行维护。
2、图解
3、实现代码
业务接口
package com.jp.dao;
import com.jp.domain.User;
/*
* 定义业务接口:
*/
public interface UserDao {
void save(User user);
}
目标类
package com.jp.dao;
import com.jp.domain.User;
/*
* 实现接口的目标类
*/
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
System.out.println("保存用户成功");
}
}
事务类
package com.jp.transaction;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 为目标类提供附加功能的事务类
*/
public class MyTransaction {
public void begin(){
System.out.println("开启事务...");
}
public void commit(){
System.out.println("提交事务...");
}
}
代理类
package com.jp.staticproxy;
import com.jp.dao.UserDao;
import com.jp.domain.User;
import com.jp.transaction.MyTransaction;
/*
* 实现业务接口的代理类
*/
public class UserDaoProxy implements UserDao {
private UserDao userDao;
private MyTransaction myTransaction;
public UserDaoProxy(UserDao userDao, MyTransaction myTransaction) {
this.userDao = userDao;
this.myTransaction = myTransaction;
}
@Override
public void save(User user) {
myTransaction.begin();
userDao.save(user);
myTransaction.commit();
}
}
测试类
package com.jp.staticproxy;
import com.jp.dao.UserDao;
import com.jp.dao.UserDaoImpl;
import com.jp.domain.User;
import com.jp.transaction.MyTransaction;
/*
* 静态代理功能测试
*/
public class StaticProxyTest {
public static void main(String[] args) {
//目标类
UserDao userDao = new UserDaoImpl();
//事务类
MyTransaction myTransaction = new MyTransaction();
//代理类
UserDaoProxy proxy = new UserDaoProxy(userDao, myTransaction);
User user = new User();
proxy.save(user);
}
}
运行结果
动态代理
1、JDK动态代理
简介
核心原理
① 通过实现InvocationHandlet接口
创建自己的拦截器
② 通过为Proxy类指定ClassLoader
对象和一组interface
来创建动态代理
③ 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用拦截器接口类型
④ 通过构造函数创建动态代理类实例,构造时调用拦截器对象作为参数参入
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
可以将InvocationHandler
接口的子类想象成一个代理的最终操作类,替换掉代理类。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
Ps:类加载器
在Proxy类中的newProxyInstance()
方法中需要一个ClassLoader
类的实例,ClassLoader
实际上对应的是类加载器,在Java中主要有一下三种类加载器:
Booststrap ClassLoader:
此加载器采用C++编写,一般开发中是看不到的;Extendsion ClassLoader:
用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;AppClassLoader:
(默认)加载classpath指定的类,是最常使用的是一种加载器。
实现代码
还是使用静态代理的代码,主要分为三步:定义业务接口,目标类,事务类然后写一个拦截器最后写个测试类永磊测试;
接口类
、目标类
、事务类
同上。
拦截器
package com.jp.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.jp.transaction.MyTransaction;
/*
* 自定义的一个拦截器
*
*/
public class MyInterceptor implements InvocationHandler{
private Object target; //目标类
private MyTransaction myTransaction; //事务类(切面)
public MyInterceptor(Object target, MyTransaction myTransaction) {
super();
this.target = target;
this.myTransaction = myTransaction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
myTransaction.begin();
result=method.invoke(target, args);
myTransaction.commit();
return result;
}
}
测试类
package com.jp.jdkproxy;
import java.lang.reflect.Proxy;
import com.jp.dao.UserDao;
import com.jp.dao.UserDaoImpl;
import com.jp.domain.User;
import com.jp.transaction.MyTransaction;
public class JdkProxyTest {
public static void main(String[] args) {
//目标类
UserDao userDao = new UserDaoImpl();
//事务类
MyTransaction myTransaction = new MyTransaction();
//创建拦截器对象
MyInterceptor myInterceptor = new MyInterceptor(userDao, myTransaction);
//这里才传入真正对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), myInterceptor);
User user = new User();
userDaoProxy.save(user);
}
}
运行结果
总结
- 可以看出jdk动态代理和静态代理的区别就是JDK动态代理帮我们讲代理类和委托类的绑定关系延迟了,什么时候用什么时候调,大大的简化了编程工作。
- 缺陷:目标类必须是有接口的,如果被代理目标没有接口则不能使用该方法进行动态代理。这种情况这一采用cglib来实现
2、CGLib动态代理
简介
- CGLib是一个强大的高性能的代码生成包。
- CGLIb采用底层的字节码技术, 可以为一个类创建子类, 在子类中采用方法拦截的技术拦截所有父类方法的调用, 并织入横切逻辑.
- CGLib可以为没有实现接口的目标类进行动态代理。
核心原理
① 通过实现MethodInterceptor接口
的intercept方法
创建自己的拦截器
② 通过Enhancer类
指定的动态代理类的父类和回调对象来进行动态代理。
③ 通过拦截器对象的createPeoxy方法来实例化动态代理类。
intercept() 方法
@Override
public Object intercept(Object sub, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
作用:拦截所有目标类方法的调用.
其中 sub代表目标类的实例,
method 为目标类方法的反射,
args为要被拦截方法的参数,
methodProxy为表示要触发父类的方法对象.
Enhancer 类
代理对象的生成由 Enhancer 类
实现. Enhancer
是CGLib
的字节码增强器. 可以很方便的对类进行扩展.
Enhancer 创建代理对象的大概步骤如下:
-
生成代理类 Class 二进制字节码.
-
通过
Class.forname()
加载字节码文件, 生成 Class 对象. -
通过反射机制获得实例构造, 并创建代理类对象.
代码实现
目标类
/*
*目标类(不实现接口)
*/
package com.jp.cglibproxy;
import com.jp.domain.User;
public class UserDaoImpl{
public void save(User user) {
System.out.println("保存用户成功");
}
}
事务类
package com.jp.transaction;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 为目标类提供附加功能的事务类
*/
public class MyTransaction {
public void begin(){
System.out.println("开启事务...");
}
public void commit(){
System.out.println("提交事务...");
}
}
拦截器
package com.jp.cglibproxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.jp.jdkproxy.Logger;
import com.jp.transaction.MyTransaction;
public class MyInterceptor implements MethodInterceptor {
private Object target;
private MyTransaction myTransaction;
//创建代理对象
public Object createProxy(){
//通过CGLib动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
//设置enhancer对象的父类
enhancer.setSuperclass(this.target.getClass());
// 设置enhancer的回调对象
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
public MyInterceptor(Object target, MyTransaction myTransaction) {
super();
this.target = target;
this.myTransaction = myTransaction;
}
@Override
public Object intercept(Object sub, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
myTransaction.begin();
//调用目标方法
//method.invoke(this.target, args);
Object object = methodProxy.invokeSuper(sub, args);
myTransaction.commit();
return object;
}
}
测试类
package com.jp.cglibproxy;
import com.jp.domain.User;
import com.jp.jdkproxy.Logger;
import com.jp.transaction.MyTransaction;
public class Demo {
public static void main(String[] args) {
//创建目标类
UserDaoImpl u = new UserDaoImpl();
//创建切面类
MyTransaction mt = new MyTransaction();
//创建拦截器
MyInterceptor m = new MyInterceptor(u, mt);
//利用cglib包创建代理对象
UserDaoImpl createProxy = (UserDaoImpl) m.createProxy();
User user = new User();
createProxy.save(user);
}
}
运行结果
3、两者区别
JDK动态代理:
- ① 主要通过代理类调用
InvocationHandler
实现类的重写方法invoke() 来实现动态代理. - ② 只能对实现接口的目标类进行代理
- ③ 底层使用反射机制进行方法的调用
CGLib动态代理:
- ① 代理类继承了目标类, 在代理方法中, 会判断是否存在实现了
MethodInterceptor
接口的对象, 若存在则调用对象的invoke() 方法
, 对目标方法进行代理. - ② 不能对 final 类以及 final , private方法进行代理.
- ③ CGLib是通过FastClass机制直接调用方法
区别
- ① JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理;CGLib必须依赖于CGLib的类库
- ② JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象
- ③ JDK和CGLib都是在运行期生成字节码,JDK直接写成Class字节码,CGLib使用ASM框架写Class字节码,CGLib实现更加复杂,生成代理类比JDK效率低
- ④ JDK是通过反射调用代理方法,CGLib是通过FastClass机制直接调用方法,所以CGLib动态代理的执行效率更高。