(一)什么是代理模式?
我们在编程中有一个思想:不要随便改动源代码 !那么我们要给一个对象提供更多的方法功能,我们第一时间想到的是修改源码,实际上这种方法并不可取,我们提供了一个更为科学的方法,大体逻辑如下图:
代理(proxy)模式 是一种设计模式,提供了对目标对象的另一种访问形式,也就是通过代理对象来访问目标对象,通过代理对象间接的为目标对象进行功能的增强,扩展目标对象的功能,用户调用代理对象实现我们的需求。
(二)静态代理
静态代理 必须定义接口或者父类,实现目标对象与代理对象时必须实现同一个接口或者父类。
举个栗子:
- 接口:UserDao
/**
* 接口
*/
public interface UserDao {
void save();
}
- 目标对象:UserDaoImp
/**
* 接口实现
* 目标对象
*/
public class UserDaoImp implements UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
- 代理对象:UserDapImpProxy
/**
* 代理对象,静态代理
*/
public class UserDaoImpProxy implements UserDao{
//接收保存目标对象
private UserDao target;
public UserDaoImpProxy(UserDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();
//执行目标对象的方法
System.out.println("提交事务...");
}
}
- 测试类:Test
/**
* 测试类
*/
public class Test{
public static void main(String[] args) {
//目标对象
UserDao target = new UserDaoImp();
//代理对象,把目标对象传给代理对象,建立代理关系
UserDaoImpProxy proxy = new UserDaoImpProxy(target);
proxy.save();//执行的是代理的方法
}
}
- 测试类输出数据
开始事务…
----已经保存数据!----
提交事务…
我们发现并没有改动源码,但是我们不单单执行了我们需要的功能,还对功能进行了扩展,这是一种非常优秀的设计思想,对我来说算是涨知识啦~但是如此优秀的静态代理仍然有着其致命的缺陷:代理对象和目标对象都要实现相同的接口或父类,因此当需要扩展的方法或多,会出现许多代理对象类,当接口的方法发生变化,代理对象类也要随着修改。这无疑是一件非常头疼的事情,于是我们可以引出我们的 动态代理,完美解决这个问题。
(三)动态代理
动态代理 也叫JDK原生代理,代理对象不需要实现接口,根据JDK的API动态的在内存中创建代理对象。
我们查一查JDK中创建代理对象的API:
1.代理类所在的包:java.lang.reflect.Proxy
2.我们实现代理只需要使用Proxy中一个又长又臭的静态方法:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
我们需要关注方法的三个参数:
- ClassLoader loade:目标对象的类加载器
- Class<?>[] interfaces:目标对象实现的接口
- InvocationHandler h:事件处理,调用目标对象的方法时被触发,拦截被执行的方法进行功能扩展后放行
举个栗子理解一下:
- 代理工厂类:ProxyFactory
/**
* 代理工厂
* 负责动态创建代理对象
*/
public class ProxyFactory {
//目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成代理对象
public Object getProxyInstance() {
//代理类的静态方法
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务...");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
);
}
}
- 测试类:Test
/**
* 测试类
*/
public class App {
public static void main(String[] args) {
// 目标对象
UserDaoImp target = new UserDaoImp ();
// 在代理工厂创建代理对象
UserDaoImp proxy = (UserDaoImp ) new ProxyFactory(target).getProxyInstance();
// 执行代理对象被增强后的方法
proxy.save();
}
}
- 测试类输出数据
开始事务…
----已经保存数据!----
提交事务…
动态代理中代理对象无需实现接口,因此不会存在静态代理中存在的致命问题,但是目标对象仍然需要实现接口,否则无法使用动态代理,因此动态代理的缺陷就产生了:当目标对象是一个单独的对象,没有实现接口,我们要如何对其进行代理?聪明的程序猿们想出了一种解决方式:Cglib代理。
(四)Cglib代理
Cglib代理,也叫作子类代理,它是在内存中动态构建一个目标对象的子类对象,对子类进行功能的扩展与增强,从而达到子类对象即代理对象的目的。
我们先百度一下什么是Cglib:
1.Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截).
2.Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
然后我们百度一下怎么使用Cglib:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入
pring-core-3.2.5.jar
即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
多说无益举个栗子:
- 目标对象:UserDao
/**
* 目标对象,没有实现任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
- Cglib代理工厂:ProxyFactory
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor {
//目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个子类对象
public Object getProxyInstance() {
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
- 测试类:Test
/**
* 测试类
*/
public class App {
@Test
public void test() {
//目标对象
UserDao target = new UserDao();
//目标对象的子类对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
//执行子类对象的方法
proxy.save();
}
}
- 测试类输出数据
开始事务…
----已经保存数据!----
提交事务…