文章目录
一、参考
二、代理
代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
重要的三个对象:
- 真实实现
- 代理对象(增强处理)
- 接口(代理和真实实现要有相同的接口)
为什么需要动态代理?
动态代理最大的优势在于“解耦”,即不用更改原始业务代码,就能实现不同的功能。
动态代理的常见应用场景包括:
- 日志记录:在方法调用前后记录日志。
- 性能监控:在方法执行前后记录时间以计算执行效率。
- 事务管理:自动处理事务的开启和关闭。
- 权限控制:在方法执行前检查用户权限。
三、代理实现
3.1 静态代理
3.1 什么是静态代理
静态代理要求我们提前定义代理类
3.2 静态代理案例
package com.hb.proxy.jingtai;
public interface Service {
void execute();
}
package com.hb.proxy.jingtai;
public class RealService implements Service {
public void execute() {
System.out.println("执行实际服务逻辑");
}
}
package com.hb.proxy.jingtai;
public class StaticProxy implements Service {
private Service realService;
public StaticProxy(Service realService) {
this.realService = realService;
}
@Override
public void execute() {
System.out.println("执行前的操作...");
realService.execute();
System.out.println("执行后的操作...");
}
}
package com.hb.proxy.jingtai;
public class StaticProxyDemo {
public static void main(String[] args) {
Service realService = new RealService();
Service proxy = new StaticProxy(realService);
proxy.execute();
}
}
3.2 JDK 实现动态代理
3.2.1 JDK 实现动态代理原理
JDK的动态代理机制只能代理实现了接口的类,在程序运行时,运用反射机制动态创建代理实例对象。
3.2.2 JDK 实现动态代理例子
- 定义接口
package com.hb.proxy.dongtai.jdk;
public interface User {
void eat();
void run();
}
- 定义接口实现的类(需要被代理的对象)
package com.hb.proxy.dongtai.jdk;
public class RealUser implements User {
public void eat() {
System.out.println("执行实际 eat 服务逻辑");
}
public void run() {
System.out.println("执行实际 run 服务逻辑");
}
}
- 定义代理增强,需要实现
InvocationHandler
接口
package com.hb.proxy.dongtai.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前的操作...");
Object result = method.invoke(target, args);
System.out.println("执行后的操作...");
return result;
}
}
- 测试验证jdk 动态代理
package com.hb.proxy.dongtai.jdk;
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
User realService = new RealUser();
User proxy = (User) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new DynamicProxyHandler(realService)
);
proxy.run();
proxy.eat();
}
}
3.3 CGLIB 实现动态代理
3.3.1 CGLIB 实现动态代理的原理(子类继承)
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
3.3.2 CGLIB 实现动态代理例子
- pom.xml 引入依赖文件
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
- 定义被代理的类
package com.hb.proxy.dongtai.cglib;
public class BookFacadeImpl1 {
public void addBook() {
System.out.println("增加图书的普通方法...");
}
}
- 定义增强类,需要实现
MethodInterceptor
接口
package com.hb.proxy.dongtai.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("事物开始");
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}
}
- 测试类,验证 cglib 代理
package com.hb.proxy.dongtai.cglib;
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib = new BookFacadeCglib();
BookFacadeImpl1 bookCglib = (BookFacadeImpl1) cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
JDK动态代理与CGLIB代理的区别
-
接口 vs. 类: 动态代理要求被代理对象实现一个接口,而CGLIB代理可以代理普通类。
-
性能: 通常情况下,动态代理的性能较差,因为它需要使用反射机制,而CGLIB代理通过生成子类来调用方法,性能更高。
JDK 1.8 对反射做了优化,现在CGLIB 与 JDK 性能几乎一样,没有太大的区别
-
使用场景: 如果被代理对象已经实现了接口,或者你需要代理的是一个接口,那么动态代理是一个合适的选择。如果被代理对象是一个普通类,或者你无法修改被代理对象的源代码,那么CGLIB代理可能更适合。
-
依赖库: 动态代理是Java的标准库的一部分,无需额外的依赖。CGLIB代理需要引入CGLIB库。