代理模式
静态代理:
- 定义业务接口
- 被代理类实现业务接口
- 代理类实现业务接口
- 然后调用
静态代理例子:
- 需要创建一个共同实现的接口:
package com.lzl.proxy;
public interface IUserService {
void add(String name);
}
- 创建被代理类:
package com.lzl.proxy.impl;
import com.lzl.proxy.IUserService;
/**
* 被代理类
*/
public class UserServiceImpl implements IUserService {
@Override
public void add(String name) {
System.out.println("添加用户: "+name);
}
}
- 创建代理类,并将被代理类通过构造方法注入,并实现相同的接口:
package com.lzl.proxy.impl;
import com.lzl.proxy.IUserService;
public class UserServiceProxy implements IUserService {
private IUserService service;
public UserServiceProxy(IUserService service) {
this.service = service;
}
@Override
public void add(String name) {
System.out.println("开启数据库。。。");
service.add(name);
System.out.println("关闭数据库。。。");
}
}
- 测试类:
public class Client {
public static void main(String[] args) {
IUserService userService = new UserServiceImpl();
IUserService userService1 = new UserServiceProxy(userService);
userService1.add("xxx");
静态代理的缺点:
- 代理类和被代理类都需要实现相同的接口
- 代理类型单一,维护复杂
动态代理(JDK动态代理):
- 创建被代理接口和类
- 创建InvocationHandler接口的实现类,在invoke方法中实现代理逻辑
- 通过Proxy的静态方法newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h) 创建一个代理对象
- 使用代理对象
动态代理例子:
- 还是先创建一个接口,并写出这个接口的实现类
- 直接编写测试类:
package com.lzl.proxy;
import com.lzl.proxy.impl.UserHandler;
import com.lzl.proxy.impl.UserServiceImpl;
import com.lzl.proxy.impl.UserServiceProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// IUserService userService = new UserServiceImpl();
// IUserService userService1 = new UserServiceProxy(userService);
// userService1.add("lzl");
IUserService iUserService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(iUserService);
//第一个参数:被代理类的类加载器
//第二个参数:被理类需要实现的接口(传入被代理类实现的接口,这样代理类和被代理类都实现了相同的接口)
//第三个参数:invocation handler,用来处理方法的调用,传入我们自己实现的handler,这个方法中实现代理
IUserService o = (IUserService) Proxy.newProxyInstance(new UserServiceImpl().getClass().getClassLoader(), iUserService.getClass().getInterfaces(), userHandler);
o.add("lzl");
IUserService o1 = (IUserService) Proxy.newProxyInstance(iUserService.getClass().getClassLoader(),
iUserService.getClass().getInterfaces(),
(proxy, method, args1) -> {
System.out.println("运行开始。。。");
Object invoke = method.invoke(iUserService, args1);
System.out.println("运行结束。。。");
return invoke;
}
);
o1.add("hhaah");
}
}
基于cglib实现动态代理:
只要被代理类不是最终类(final修饰)就可使用,由于cglib是第三方技术,需要导入jar包或者maven导入坐标:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
创建一个需要被代理的类:
package com.lzl.proxy.impl;
import com.lzl.proxy.IUserService;
/**
* 被代理类
*/
public class CglibUserService {
public void add(String name) {
System.out.println("添加用户: "+name);
}
}
直接编写测试类:
package com.lzl.proxy.impl;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibDemo {
public static void main(String[] args) {
//创建一个目标类对象
CglibUserService cglibUserService = new CglibUserService();
//使用cglib创建代理对象
//参数:
//class type : 指定我们要代理的目标类的字节码对象,指定目标类的类型
//Callback : 回调,意思是我们提供一个方法,它会在合适的时候回调,由于该接口只是一个名称定义没有具体的抽象方法,所以一般是使用它的子接口
//MethodInterceptor(方法拦截器)
CglibUserService o = (CglibUserService) Enhancer.create(cglibUserService.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("方法执行。。。");
Object invoke = method.invoke(cglibUserService, args);
System.out.println("方法结束。。。");
return invoke;
}
});
o.add("lzl");
}
}
MethodInterceptor(方法拦截器),它的抽象方法:
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
参数: | |
---|---|
Var1 | 是代理对象的引用,也是返回值 |
Var2 | 是执行的method方法 |
Var3 | 是参数列表 |
Var4 | 方法的代理参数 |
Cglib是基于父类的动态代理,是在内存中生成了一个对象,该对象继承了原对象,所以生成的对象是目标类对象的子类。
因为jdk动态代理要求比较多,更加难实现。
Cglib>jdk proxy>动态>静态
Spring里默认是根据被代理类是否实现接口来进行选择动态代理模式:如果实现了接口就是用jdk的proxy代理;如果没有实现接口,就是用cglib进行动态代理