代理模式
- 静态代理
- 动态代理
静态代理
角色分析
- 抽象角色:一般用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色的角色,在代理成功后,会做一些附属操作
- 客户:找代理的角色
文件目录
代码步骤
1. 抽象角色
2.真实角色
3.代理角色
4.客户
优点
- 可以使真实角色做的事更纯粹,不用关注公共业务
- 公共业务可以交给代理角色,实现了业务的分工
- 公共业务的扩展很方便,便于集中管理
- 可以在不改变原有业务的前提下,实现业务的增强
缺点
- 一个真实角色就会对应一个代理角色,如果要新增一个真实角色,会使代码量成倍增加
优点的第四点扩展
需求:在不改动原有代码的前提下,新增操作日志打印 步骤:
- 把原有实现类转化为Impl类(proxy只能代理接口的实现类)
- 逆向新增一个Impl类的interface
- 新增一个Impl类的Proxy代理类,并继承implements interface(这一步是为了快速继承所有的原有方法)
- 在Proxy类中,设置Impl类为其属性,并复用其中的方法
- 最后在Proxy类中实现日志打印方法,并加载每个@override方法中
**
* 需求:在不改动原有代码下,新增每个操作的日志打印
* 解析:即代码增强
* 解答:使用代理类
*/
public class UserServiceProxy implements UserService {
private UserServiceImpl userServiceImpl;
public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
@Override
public void add() {
log("add");
userServiceImpl.add();
}
@Override
public void delete() {
log("delete");
userServiceImpl.delete();
}
@Override
public void update() {
log("update");
userServiceImpl.update();
}
@Override
public void query() {
log("query");
userServiceImpl.query();
}
public void log(String msg) {
System.err.println("log " + msg);
}
}
动态代理(通过反射机制实现代理)
- 动态代理和静态代理的角色是一样的
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理、基于类的动态代理
- 基于接口的动态代理:JDK动态代理
- 基于类的动态代理:cglib,利用字节码技术生成代理对象
- java字节码原理:javassist(放在jboss应用服务器,不是tomcat)
JDK自带的Proxy动态代理
代理接口的实现类
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.err.println("新增");
}
@Override
public void delete() {
System.err.println("删除");
}
@Override
public void update() {
System.err.println("修改");
}
@Override
public void query() {
System.err.println("查询");
}
}
动态代理工具类的编写
/**
* JDK自带的动态代理
* 可编写为代理工具类,此代码基本上是不变的
* InvocationHandler: 调用处理程序(被代理接口中的程序)并返回结果
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理的接口
*/
private Object target;
/**
* 外部设置该接口
*
* @param target 被代理的接口
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* 通过反射动态获取接口的代理类
* Proxy类:生成动态代理实例
*
* @return 代理类
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 执行接口中的方法
*
* @param proxy 代理类
* @param method 被代理的接口
* @param args 参数
* @return 结果
* @throws Throwable 异常信息
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
System.err.println(new SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new Date()));
System.err.println("-------------");
return result;
}
private void log(String msg) {
System.err.println("正在执行" + msg + "方法");
}
}
测试类
public class Test {
public static void main(String[] args) {
// 真实角色
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 动态代理角色,此时代理角色还不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 设置需要代理的对象
pih.setTarget(userServiceImpl);
// 动态生成代理角色
UserService proxy = (UserService) pih.getProxy();
// 执行代理行为
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
动态代理完成,此时猜想是否可以代理“非”接口的实现类
public class Car implements CarService {
private String brand;
private String color;
private boolean ifNow;
private long price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isIfNow() {
return ifNow;
}
public void setIfNow(boolean ifNow) {
this.ifNow = ifNow;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", color='" + color + ''' +
", ifNow=" + ifNow +
", price=" + price +
'}';
}
}
测试类
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.setBrand("Porsche");
car.setColor("DarkGreen");
car.setIfNow(true);
car.setPrice(100000L);
ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
pih2.setTarget(car);
Car carProxy = (Car) pih2.getProxy();
System.err.println(carProxy.toString());
}
}
结果会报错
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.shinefriends.juc.proxy.activeProxy.jdkProxy.Car at com.shinefriends.juc.proxy.activeProxy.jdkProxy.Test.main(Test.java:15)
把Car改为接口的实现类
public interface CarService{
String toString();
}
public class Car implements CarService {
private String brand;
private String color;
private boolean ifNow;
private long price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isIfNow() {
return ifNow;
}
public void setIfNow(boolean ifNow) {
this.ifNow = ifNow;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", color='" + color + ''' +
", ifNow=" + ifNow +
", price=" + price +
'}';
}
}
改写测试类,成功执行
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.setBrand("Porsche");
car.setColor("DarkGreen");
car.setIfNow(true);
car.setPrice(100000L);
ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
pih2.setTarget(car);
CarService carProxy = (CarService) pih2.getProxy();
System.err.println(carProxy.toString());
}
}
此时我还想通过代理类对被代理类的属性进行设置, 比如 carProxy.setColor("blue");会发现这个方法并不允许调用。因此尝试到CarService 接口中定义一个 void setCarColor(String color); 并实现
public interface CarService{
String toString();
void setCarColor(String color);
}
Car实现类中
@Override
public void setCarColor(String color) {
this.color = color;
}
修改测试类,成功执行
ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
pih2.setTarget(car);
Car carProxy = (Car) pih2.getProxy();
System.err.println(carProxy.toString());
System.err.println("-------");
carProxy.setCarColor("blue"); // 该方法为接口中的方法
System.err.println(carProxy.toString());
总结:proxy代理方式只能代理接口的实现类,且只能调用接口中定义的方法
如果只能代理接口的实现类,一定会改动很多代码,由此引申出“基于类的动态代理”->"cglib"
cglib
首先引入cglib的依赖
implementation 'cglib:cglib:3.3.0'
对象类
public class Car {
private String brand;
private String color;
private boolean ifNow;
private long price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isIfNow() {
return ifNow;
}
public void setIfNow(boolean ifNow) {
this.ifNow = ifNow;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", color='" + color + ''' +
", ifNow=" + ifNow +
", price=" + price +
'}';
}
}
代理类(包含泛型的写法、Object的写法)
/**
* 基于类的代理
* 通过继承,实现方法的覆盖并增强,因此不支持final方法
* @param <T> 也可以使用Object
*/
public class CglibProxy<T> implements MethodInterceptor { // 泛型的写发
private T target;
public T getProxy(T target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
// public class CglibProxy implements MethodInterceptor { // Object的写法
// private Object target;
//
// public void setTarget(Object target) {
// this.target = target;
// }
//
// public Object getProxy() {
// Enhancer enhancer = new Enhancer();
// enhancer.setSuperclass(target.getClass());
// enhancer.setCallback(this);
// return enhancer.create();
// }
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.err.println("cglib proxy method name: " + method.getName());
Object result = proxy.invoke(target, args);
System.err.println("cglib proxy finish");
return result;
}
}
测试类
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.setBrand("Porsche");
car.setColor("DarkGreen");
car.setIfNow(true);
car.setPrice(100000L);
// 泛型的写法
Car carProxy = new CglibProxy<Car>().getProxy(car);
System.err.println(carProxy.toString());
car.setColor("red");
System.err.println(carProxy.toString());
// // Object的写法
// CglibProxy cglibProxy = new CglibProxy();
// cglibProxy.setTarget(car);
// Car carProxy = (Car) cglibProxy.getProxy();
//
// System.err.println(carProxy.toString());
// car.setColor("red");
// System.err.println(carProxy.toString());
}
}
总结,cglib可以直接代理类,并且可以使用类中的方法
JDK Proxy动态代理与cglib动态代理的对比
JDK动态代理的优点:
- JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
- JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
- 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
JDK动态代理的缺点:
- JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
- JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
CGLIB代理的优点:
- CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
- CGLIB代理可以代理没有实现接口的类。
CGLIB代理的缺点:
- CGLIB代理需要引入外部依赖。
- CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。
综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。
JAVA动态字节技术之Javassist
待完善