浅谈 Java 代理 (jdk 静态代理动态代理和 cglib 动态代理)
这里有新鲜出炉的 Java 设计模式, 程序狗速度看过来!
Java 程序设计语言
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言, 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台 (即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se)) 的总称
下面小编就为大家带来一篇浅谈 Java 代理 (jdk 静态代理动态代理和 cglib 动态代理) 小编觉得挺不错的, 现在就分享给大家, 也给大家做个参考一起跟随小编过来看看吧
一代理是 Java 常用的设计模式, 代理类通过调用被代理类的相关方法, 并对相关方法进行增强加入一些非业务性代码, 比如事务日志报警发邮件等操作
二 jdk 静态代理
1 业务接口
- /**
- * 业务接口
- * @author pc
- *
- */
- public interface UserService {
- // 增加一个用户
- public void addUser();
- // 编辑账户
- public void editUser();
- }
2 业务实现类
- /**
- * 业务实现类
- * @author pc
- *
- */
- public class UserServiceImpl implements UserService {
- public void addUser() {
- System.out.println("增加一个用户");
- }
- public void editUser() {
- System.out.println("编辑一个用户");
- }
- }
3 代理类
- /**
- * 代理类
- *
- * @author pc
- *
- */
- public class UserServiceProxy implements UserService {
- private UserServiceImpl userImpl;
- public UserServiceProxy(UserServiceImpl countImpl) {
- this.userImpl = countImpl;
- }
- public void addUser() {
- System.out.println("代理类方法, 进行了增强");
- System.out.println("事务开始");
- // 调用委托类的方法;
- userImpl.addUser();
- System.out.println("处理结束");
- }
- public void editUser() {
- System.out.println("代理类方法, 进行了增强");
- System.out.println("事务开始");
- // 调用委托类的方法;
- userImpl.editUser();
- System.out.println("事务结束");
- }
- }
4 测试类
- public static void main(String[] args) {
- UserServiceImpl userImpl = new UserServiceImpl();
- UserServiceProxy proxy = new UserServiceProxy(userImpl);
- proxy.addUser();
- System.out.println("---------- 分割线 ----------");
- proxy.editUser();
- }
5 结果
代理类方法, 进行了增强
事务开始
增加一个用户
处理结束
---------- 分割线 ----------
代理类方法, 进行了增强
事务开始
编辑一个用户
事务结束
三 jdk 动态代理
1 业务接口
- /**
- * 业务接口
- * @author pc
- *
- */
- public interface UserService {
- // 增加一个用户
- public void addUser();
- // 编辑账户
- public void editUser();
- }
2 业务接口实现类
- /**
- * 业务接口实现类
- * @author pc
- *
- */
- public class UserServiceImpl implements UserService {
- public void addUser() {
- System.out.println("增加一个用户");
- }
- public void editUser() {
- System.out.println("编辑一个用户");
- }
- }
3 代理类
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- *
- * @author pc
- *
- */
- public class ServiceInvocationHandler implements InvocationHandler {
- // 目标对象
- private Object target;
- public ServiceInvocationHandler(Object target) {
- super();
- this.target = target;
- }
- /**
- * 创建代理实例
- * @return
- * @throws Throwable
- */
- public Object getProxy() throws Throwable {
- return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), this.target.getClass().getInterfaces(), this);
- // 这样写只返回了目标对象, 没有生成代理对象
- // return target;
- }
- /**
- * 实现 InvocationHandler 接口方法
- * 执行目标对象的方法, 并进行增强
- */
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = null;
- System.out.println("代理类方法, 进行了增强");
- System.out.println("事务开始");
- // 执行目标方法对象
- result = method.invoke(target, args);
- System.out.println("事务结束");
- return result;
- }
- }
4 测试类
- public class Test {
- /**
- * jdk 动态代理会生成一个动态代理类, 生成相应的字节码, 然后通过 ClassLoader 加载字节码
- * 该实例继承了 Proxy 类, 并实现了业务接口, 在实现的方法里通过反射调用了 InvocationHandler 接口实现类
- * 的 invoke()回调方法
- * @param args
- * @throws Throwable
- */
- public static void main(String[] args) throws Throwable {
- UserService userService = new UserServiceImpl();
- ServiceInvocationHandler handler = new ServiceInvocationHandler(userService);
- // 根据目标生成代理对象
- UserService proxy = (UserService) handler.getProxy();
- proxy.addUser();
- // proxy.editUser();
- }
- }
5 测试结果
代理类方法, 进行了增强
事务开始
增加一个用户
事务结束
四 cglib 动态代理
需要引入 cglib 的 jar 包,
在 pom.xml 加入依赖:
- <!-- https://mvnrepository.com/artifact/cglib/cglib -->
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib</artifactId>
- <version>2.2.2</version>
- </dependency>
1 业务类, 没有实现接口
- /**
- * 业务类
- * 没有实现接口
- * 如果类是 final 的, 则没法生成代理对象, 报错
- * 如果方法是 final 的, 代理无效
- * @author pc
- *
- */
- public class UserServiceImpl {
- public void addUser() {
- System.out.println("增加一个用户");
- }
- public void editUser() {
- System.out.println("编辑一个用户");
- }
- }
2 代理类
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- /**
- * 使用 Cglib 动态代理
- * @author pc
- *
- */
- public class UserServiceCglib 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();
- }
- /**
- * 实现 MethodInterceptor 接口要重写的方法
- * 回调方法
- */
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- System.out.println("事务开始");
- Object result = proxy.invokeSuper(obj, args);
- System.out.println("事务结束");
- return result;
- }
- }
3 测试类
- public class TestCglib {
- public static void main(String[] args) {
- UserServiceCglib cglib = new UserServiceCglib();
- UserServiceImpl bookFacadeImpl = (UserServiceImpl) cglib.getInstance(new UserServiceImpl());
- bookFacadeImpl.addUser();
- // bookFacadeImpl.editUser();
- }
- }
4 结果:
事务开始
增加一个用户
事务结束
5 如果业务实现类被定义成 final 类, 就会报以下错误
- Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class cn.xx.xx.cgilb.UserServiceImpl
- at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
- at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
- at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
- at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
- at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
- at cn.pconline.proxy.cgilb.UserServiceCglib.getInstance(UserServiceCglib.java:30)
- at cn.pconline.proxy.cgilb.TestCglib.main(TestCglib.java:7)
五总结
1 原理
jdk 静态代理实现比较简单, 一般是直接代理对象直接包装了被代理对象
jdk 动态代理是接口代理, 被代理类 A 需要实现业务接口, 业务代理类 B 需要实现 InvocationHandler 接口
jdk 动态代理会根据被代理对象生成一个继承了 Proxy 类, 并实现了该业务接口的 jdk 代理类, 该类的字节码会被传进去的 ClassLoader 加载, 创建了 jdk 代理对象实例,
jdk 代理对象实例在创建时, 业务代理对象实例会被赋值给 Proxy 类, jdk 代理对象实例也就有了业务代理对象实例, 同时 jdk 代理对象实例通过反射根据被代理类的业务方法创建了相应的 Method 对象 m(可能有多个)当 jdk 代理对象实例调用业务方法, 如 proxy.addUser(); 这个会先把对应的 m 对象作为参数传给 invoke()方法 (就是 invoke 方法的第二个参数), 调用了 jdk 代理对象实例的 invoke() 回调方法, 在 invoke 方法里面再通过反射来调用被代理对象的因为方法, 即 result = method.invoke(target, args);
cglib 动态代理是继承代理, 通过 ASM 字节码框架修改字节码生成新的子类, 重写并增强方法的功能
2 优缺点
jdk 静态代理类只能为一个被代理类服务, 如果需要代理的类比较多, 那么会产生过多的代理类 jdk 静态代理在编译时产生 class 文件, 运行时无需产生, 可直接使用, 效率好
jdk 动态代理必须实现接口, 通过反射来动态代理方法, 消耗系统性能但是无需产生过多的代理类, 避免了重复代码的产生, 系统更加灵活
cglib 动态代理无需实现接口, 通过生成子类字节码来实现, 比反射快一点, 没有性能问题但是由于 cglib 会继承被代理类, 需要重写被代理方法, 所以被代理类不能是 final 类, 被代理方法不能是 final
因此, cglib 的应用更加广泛一点
以上这篇浅谈 Java 代理 (jdk 静态代理动态代理和 cglib 动态代理) 就是小编分享给大家的全部内容了, 希望能给大家一个参考, 也希望大家多多支持 PHPERZ
来源: http://www.phperz.com/article/18/0205/358712.html