设计模式行为模式 (设计模式之静态代理动态代理)

springAOP,是相对于java中的面向对象(OOP)的一个概念。在面向对象中有一些公共的行为,像日志记录,权限验证等增强逻辑如果都使用面向对象来做,会在每个业务方法中都写上重复的代码,造成代码冗余。

而AOP指的是面向切面编程,定义一个切面,用切面去切相应的方法,就可以织入增强的逻辑相应的方法是指核心的业务逻辑,切面逻辑使用代理模式来实现

关于AOP的单元测试查看SpringAOP面向切面编程功能测试

设计代理模式,动态代理设计作用

什么是代理模式?

代理代表某个真实的对象代理提供了对真实对象另外的访问方式——即通过代理对象访问真实对象。真实对象又可称目标对象。

这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

涉及到的编程原则就是开闭原则:不要随意去修改别人已经写好的代码或者方法,如果需要修改,可以通过代理的方式来扩展该方法。

举个例子:假设我们想邀请一位明星,那么并不是直接对接明星,而是联系明星的经纪人来达到同样的目的。明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。

静态代理

静态代理,简单点来说就是在程序运行之前,代理类和被代理类的关系已经确定。静态代理的实现首先要定义一个公共的接口,然后代理类和被代理类都实现这个接口,如下:

//接口
public interface IUserDao {
    void save();
    String find();
}
//目标对象
public class UserDao implements IUserDao
{

	private ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();

	@Override
	public void save() {
		System.out.println("模拟保存用户");
		concurrentHashMap.put("1", "张三");
	}

	@Override
	public String find() {
		System.out.println("模拟查找用户");
		return concurrentHashMap.get("1");
	}

}
//代理对象
public class UserDaoProxy implements IUserDao
{
	private UserDao ud = new UserDao();

	@Override
	public void save() {
		System.out.println("代理操作,开启事务");//模拟的增强处理
		ud.save();
		System.out.println("代理操作,关闭事务");//模拟的增强处理
	}

	@Override
	public String find() {
		return ud.find();
	}

}

静态代理最大的缺点:我们得为每一个服务创建代理类,工作量太大不易管理;同时接口一旦发生改变,代理类也得相应修改。

设计代理模式,动态代理设计作用

动态代理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建

JDK动态代理:Java为我们创建了Proxy类,动态生成的代理类有一个共同的父类叫Proxy,我们需要告诉Proxy做什么。我们不能像静态代理那样把自己的代码放在Proxy类,因为Proxy类不是我们自己创建的。Java提供了动态处理器InvocationHandler接口。

设计代理模式,动态代理设计作用

InvocationHandler的工作是响应代理的任何调用,它是代理收到方法调用后,请求做实际工作的对象

JDK实现动态代理,是通过Proxy的静态方法newProxyInstance来创建代理对象的,该方法有三个参数:

public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)

ClassLoader loader:指定当前目标对象使用的类加载器

Class<?>[] interfaces:目标对象实现的接口的集合,使用泛型方式确认类型

InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的invoke方法,会把当前执行目标对象的方法作为参数传入

public void useJDKProxy() {
        System.out.println("-------useJDKProxy-------");
        final UserDao ud = new UserDao();
        // 生成代理对象
        IUserDao iud = (IUserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(),
            ud.getClass().getInterfaces(),
            /**
             *  因为InvocationHandler是个接口,所以可以使用匿名内部类的方式进行实现
             */
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                	System.out.println("proxy================"+proxy.getClass().getName());//模拟的增强处理
                    Object result = null;
                    result = method.invoke(ud, args);
                    System.out.println(method.getName()+"================"+result);//模拟的增强处理
                    return result;
                }
            });
        iud.save();
        iud.find();
    }

执行结果:

-------useJDKProxy-------
proxy================com.sun.proxy.$Proxy0
模拟保存用户
save================null
proxy================com.sun.proxy.$Proxy0
模拟查找用户
find================张三

从输出可以看出JDK生成的代理对象名称为:com.sun.proxy.$Proxy0,关于动态生成代理的名称,debug调用栈到Proxy.ProxyClassFactory.apply:

设计代理模式,动态代理设计作用

以下两行代码就是生成代理对象名称的源码:

private static final String proxyClassNamePrefix = "$Proxy";
String proxyName = proxyPkg + proxyClassNamePrefix + num;

设计代理模式,动态代理设计作用

CGLIB代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,需要通过CGLib实现动态代理。

CGLib创建代理对象是通过net.sf.cglib.proxy.Enhancer实现的,代理收到方法调用后,请求做实际工作的对象是MethodInterceptor

public class CglibProxy implements MethodInterceptor {
    
    private Object target;
    public Object getInstance(final Object target) {
        this.target = target;
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(this.target.getClass());//设置目标对象
        enhancer.setCallback(this);//设置代理拦截器MethodInterceptor
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("obj============="+obj.getClass().getName());//模拟的增强处理
        System.out.println("method============="+method.getName());
        System.out.println("proxy============="+proxy.getSignature());
        Object result=proxy.invokeSuper(obj, args);//目标方法
        System.out.println("方法执行返回============="+result);//模拟的增强处理
        return result;
    }

}

执行结果:

--------useCGlibProxy--------
obj=============proxy.staticProxy.UserDao$EnhancerByCGLIB$d229b515
method=============save
proxy=============save()V
模拟保存用户
方法执行返回=============null
obj=============proxy.staticProxy.UserDao$EnhancerByCGLIB$d229b515
method=============find
proxy=============find()Ljava/lang/String;
模拟查找用户
方法执行返回=============张三

CGlib生成的代理对象包含一个子对象EnhancerByCGLIB字样在名称里面。debug调用栈

Enhancer.create()->Enhancer.createHelper()->AbstractClassGenerator.create(Object key)->DefaultGeneratorStrategy.generate(ClassGenerator cg)->Enhancer.generateClass()->AbstractClassGenerator.getClassName()->AbstractClassGenerator.getClassName()->DefaultNamingPolicy.getClassName(final ClassLoader loader),有一个变量bese定义了cglib代理类名称,如下:

String base =
            prefix + "$" + 
            source.substring(source.lastIndexOf('.') + 1) +
            getTag() + "$" +
            Integer.toHexString(key.hashCode());
   //变量值
   prefix=proxy.staticProxy.UserDao
   source=net.sf.cglib.proxy.Enhancer
   key=proxy.staticProxy.UserDao
   protected String getTag() {
    return "ByCGLIB";
   }
   base=proxy.staticProxy.UserDao$EnhancerByCGLIB$3b964c77

#程序员#