Spring AOP(三)代理模式和动态代理

536 阅读5分钟

Spring AOP(三)代理模式和动态代理

在了解了核心概念和基于Springboot结合自定义注解实现AOP之后,在来了解下AOP的本身代理再不使用spring帮我们分装好的前提下,我们自己怎么实现代理。

Spring AOP(一)核心概念

Spring AOP(二)AOP配合自定义注解应用

在准备展示代理模式之前,我们先了解下为什么使用代理模式?

使用代理模式就是为了提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,实现对于原目标的访问和扩展。那么动态代理就是在代理模式的基础上可以动态的来生成代理对象,更大的提高了灵活性和效率。

文章示例代码:github.com/swallretu/s…

静态代理(代理模式)

定义一个接口和实现类

package com.troyqu.javaaop.staticproxy.service;

public interface UserService {

    void add(String name);
}
package com.troyqu.javaaop.staticproxy.service.impl;

import com.troyqu.javaaop.staticproxy.service.UserService;

public class UserServiceImpl implements UserService {

    @Override
    public void add(String name) {
        System.out.println("添加用户" + name);
    }
}

使用代理模式创建一个代理

package com.troyqu.javaaop.staticproxy.proxy;

import com.troyqu.javaaop.staticproxy.service.UserService;

public class UserServiceProxy implements UserService {

    private UserService target;

    public UserServiceProxy(UserService target){
        this.target = target;
    }

    @Override
    public void add(String name) {
        System.out.println("准备添加用户");
        target.add(name);
        System.out.println("添加用户完成");
    }
}
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService userProxy = new UserServiceProxy(userService);
        userProxy.add("troyqu");
    }

测试输出

准备添加用户
添加用户troyqu
添加用户完成

通过结果可以看到,我们没有在原有实现类的逻辑中添加任何代码最终达到了添加我们新业务逻辑处理的目的,虽然用起来也方便,但是我们代理类也和UserService产生了很强的关联,而且如果我们业务类中添加了新的方法,那么我们还需要在更新代理类,对于扩展而言不是很方便。

动态代理

在Spring AOP(二)AOP配合自定义注解应用中已经展示了基于@Aspectj的方式实现AOP,这里主要看下基于JDK实现动态代理和CGlib实现动态代理的方式;

这里分别创建两个代理对象,一个是包含实现接口的另一个没有实现任何接口,分别通过JDK和CGlib来实现代理。

创建一个接口和实现类

package com.troyqu.javaaop.dynamicproxy.service;

public interface UserService {

    void add(String name);

    String query(int id);
}
package com.troyqu.javaaop.dynamicproxy.service.impl;

import com.troyqu.javaaop.dynamicproxy.service.UserService;

import java.util.HashMap;
import java.util.Map;

public class UserServiceImpl implements UserService {

    static Map<Integer, String> users = new HashMap<Integer, String>();

    static{
        users.put(1,"troyqu1");
        users.put(2,"troyqu2");
        users.put(3,"troyqu3");
    }

    @Override
    public void add(String name) {
        users.put(users.size() + 1, name);
        System.out.println("添加用户" + name);
    }

    @Override
    public String query(int id) {
        Map.Entry<Integer, String> user = users.entrySet().stream().filter(entry -> entry.getKey().equals(id)).findFirst().get();
        System.out.println("查找Id="+id+"的用户,用户名"+user.getValue());
        return user.getValue();
    }
}

创建一个没有实现任何接口的类

package com.troyqu.javaaop.dynamicproxy.service;

public class UserStoryService {
    public void show(){
        System.out.println("展示UserStory信息");
    }
}

JDK动态代理

package com.troyqu.javaaop.dynamicproxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * jdk动态代理要求被代理的对象必须实现1个或多个接口,如果没有实现接口就无法使用jdk的动态代理
 */
public class LogProxyFactory {

    private Object target;

    public LogProxyFactory(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("操作开始记录日志");
                method.invoke(target, args);
                System.out.println("操作完成记录日志,退出程序");
                return null;
            }
        });
    }

}
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        System.out.println("target目标对象信息"+target.getClass());
        UserService proxy = (UserService) new LogProxyFactory(target).getProxyInstance();
        System.out.println("proxy目标对象信息"+proxy.getClass());
        proxy.add("qujf");
        proxy.query(4);
        UserStoryService userStoryService = new UserStoryService();
        System.out.println("target目标对象信息"+userStoryService.getClass());
//        以下代码执行会报错,因为JDK动态代理的target必须至少实现一个接口,UserStoryService没有实现任何接口无法正常代理
        UserStoryService userStoryServiceProxy = (UserStoryService) new LogProxyFactory(userStoryService).getProxyInstance();
        System.out.println("proxy目标对象信息"+userStoryServiceProxy.getClass());
        userStoryServiceProxy.show();
    }

测试输出

target目标对象信息class com.troyqu.javaaop.dynamicproxy.service.impl.UserServiceImpl
proxy目标对象信息class com.sun.proxy.$Proxy0
操作开始记录日志
添加用户qujf
操作完成记录日志,退出程序
操作开始记录日志
查找Id=4的用户,用户名qujf
操作完成记录日志,退出程序
target目标对象信息class com.troyqu.javaaop.dynamicproxy.service.UserStoryService
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy1 cannot be cast to com.troyqu.javaaop.dynamicproxy.service.UserStoryService
	at com.troyqu.javaaop.dynamicproxy.DynamicProxyApplication.main(DynamicProxyApplication.java:25)

Execution failed for task ':DynamicProxyApplication.main()'.
> Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1

通过结果可以看到JDK实现动态代理,代理对象必须要实现至少个接口,否则无法生成代理对象。

CGlib动态代理

使用CGlib创建一个代理

package com.troyqu.javaaop.dynamicproxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGlib实现动态代理,即使代理对象没有实现任何接口类,也可以生成代理
 */
public class TxProxyFactory implements MethodInterceptor {

    private Object target;

    public TxProxyFactory(Object target){
        this.target = target;
    }

    public Object getProxyInstance(){
        Enhancer en = new Enhancer();
        en.setSuperclass(target.getClass());
        en.setCallback(this);
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务");
        Object returnvalue = method.invoke(target, objects);
        System.out.println("关闭事务");
        return null;
    }
}

执行测试代码

public static void main(String[] args) {
    UserService target = new UserServiceImpl();
    System.out.println("target目标对象信息"+target.getClass());
    UserService proxy = (UserService) new TxProxyFactory(target).getProxyInstance();
    System.out.println("proxy目标对象信息"+proxy.getClass());
    proxy.add("qujf");
    proxy.query(4);

    //使用CGlib对于非接口类生成代理
    UserStoryService userStoryService = new UserStoryService();
    System.out.println("target目标对象信息"+userStoryService.getClass());
    UserStoryService userStoryServiceProxy = (UserStoryService) new TxProxyFactory(userStoryService).getProxyInstance();
    System.out.println("proxy目标对象信息"+userStoryServiceProxy.getClass());
    userStoryServiceProxy.show();
}

输出信息

target目标对象信息class com.troyqu.javaaop.dynamicproxy.service.impl.UserServiceImpl
proxy目标对象信息class com.troyqu.javaaop.dynamicproxy.service.impl.UserServiceImpl$$EnhancerByCGLIB$$8c468f66
开启事务
添加用户qujf
关闭事务
开启事务
查找Id=4的用户,用户名qujf
关闭事务
target目标对象信息class com.troyqu.javaaop.dynamicproxy.service.UserStoryService
proxy目标对象信息class com.troyqu.javaaop.dynamicproxy.service.UserStoryService$$EnhancerByCGLIB$$7a2e3913
开启事务
展示UserStory信息
关闭事务

通过结果可以看到CGlib实现动态代理,代理对象无论是否实现接口都可以生成代理对象,这样就很大程度上提高了代码的灵活性。

总结

静态代理和动态代理

  • 静态代理的代理是我们在编码的时候,通过编码产生的类文件,并且在最终也会产生相应的class文件;

  • 动态代理实在运行时通过反射机制动态生成的代理对象,并不会产生一个新的类和class文件; JDK和CGlib动态代理

  • 使用JDK和CGlib都可以动态的生成代理对象,大大的提高了代码的灵活性;

  • JDK实现代理不需要引入其他依赖,但是需要被代理对象至少实现1个接口,否则无法生成代理对象,并且会抛出异常;

  • CGlib实现代理不需要被代理对象必须要实现接口,因此当我们项目中已经有这样业场景的时候,那么CGlib会是更好的选择,但是需要引入CGlib依赖;