Fork me on GitHub

java 代理模式

代理:

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法(开闭原则)

代理模式3类:

静态代理:

看下UML,代码懒得写了:

优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.

缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

 动态代理:

 jdk原生动态代理:

代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

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

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

 看下面例子:

package com.springtest.proxy.jdk;

public interface IHero {
    void firstSkill();
}
package com.springtest.proxy.jdk;

public interface ISkillSay {
    void beforeSkillSay();
    void afterSkillSay();
}
package com.springtest.proxy.jdk;

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

public class MonkeyHero implements IHero,ISkillSay{
    @Override
    public void firstSkill() {
        System.out.println("粉碎打击....");
    }

    @Override
    public void beforeSkillSay() {
        System.out.println("吃俺老孙一棒....");
    }

    @Override
    public void afterSkillSay() {
        System.out.println("事了拂衣去 深藏身与名....");
    }
}
package com.springtest.proxy.jdk;

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

public class MonkeyInvocationHandler implements InvocationHandler {
    private Object target;

    private ISkillSay iSkillSay;

    public MonkeyInvocationHandler(Object target) {
        this.target = target;
        this.iSkillSay = (ISkillSay) target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        iSkillSay.beforeSkillSay();
        //执行目标对象方法
        Object returnValue = method.invoke(target, args);
        iSkillSay.afterSkillSay();
        return returnValue;
    }
}
package com.springtest.proxy.jdk;

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

public class HeroProxyFactory {

    private Object target;

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

    public Object getHeroProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new MonkeyInvocationHandler(target));
    }
}
package com.springtest.proxy;

import com.paic.phssp.springtest.dto.Hero;
import com.paic.phssp.springtest.dto.Weapon;
import com.paic.phssp.springtest.init.WeaponFactoryBean;
import com.paic.phssp.springtest.proxy.jdk.HeroProxyFactory;
import com.paic.phssp.springtest.proxy.jdk.IHero;
import com.paic.phssp.springtest.proxy.jdk.MonkeyHero;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ProxyTest {
    @Test
    public void jdkProxy() throws Exception {
        MonkeyHero monkey = new MonkeyHero();
        HeroProxyFactory monkeyProxyFat = new HeroProxyFactory(monkey);

        IHero monkeyProxy = (IHero) monkeyProxyFat.getHeroProxy();
        monkeyProxy.firstSkill();
    }
}

运行结果:

吃俺老孙一棒....
粉碎打击....
事了拂衣去 深藏身与名....

 说明:我们可以创建很多英雄,去实现技能和语言接口,通过代理工厂统一获得代理对象。

jdk动态代理,目标对象必须要实现接口,否则不能用动态代理。这里在Spring AOP中代理根据有无继承接口去做相应的动态代理(后面有空续上)

不同的是目标方法,可以final。如:

 @Override
    public final void firstSkill() {
        System.out.println("粉碎打击....");
    }

Cglib动态代理

有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>

看下面例子:

package com.springtest.proxy.cglib;

import com.springtest.proxy.jdk.ISkillSay;

/**
 * 邪恶小法师
 */
public class WickedLittleMage implements ISkillSay {
    private String name = "Veigar";

    @Override
    public void beforeSkillSay() {
        System.out.println("Before:我闻到死亡的气息了!");
    }

    @Override
    public void afterSkillSay() {
        System.out.println("After:我是魔鬼!不许笑!");
    }

    public void firstSkill(){
        System.out.println("神Q:黑暗祭祀....");
    }

    public final void secondSkill(){
        System.out.println("天上掉便便啦....");
    }
}
package com.springtest.proxy.cglib;

import com.springtest.proxy.jdk.ISkillSay;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class HeroCglibProxyFactory  implements MethodInterceptor {

    private Object target;

    private ISkillSay iSkillSay;

    public HeroCglibProxyFactory(Object target) {
        this.target = target;
        this.iSkillSay = (ISkillSay) target;
    }

    /**
     * 给目标对象创建一个代理对象
     * @return
     */
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        iSkillSay.beforeSkillSay();

        //执行目标对象的方法
        Object returnValue = method.invoke(target, objects);

        iSkillSay.afterSkillSay();
        return returnValue;
    }
}
@Test
    public void cglibProxy() throws Exception {

        WickedLittleMage veigar  = new WickedLittleMage();

        HeroCglibProxyFactory hCglibFac = new HeroCglibProxyFactory(veigar);

        WickedLittleMage veigarProxy = (WickedLittleMage) hCglibFac.getProxyInstance();

        veigarProxy.firstSkill();
        veigarProxy.secondSkill();//final 或static 不会执行代理方法
    }

结果:

注意:目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

 

Spring AOP动态代理

看下面UML: 

说明:

DefaultAopProxyFactory.class的createAopProxy()。创建代理对象的具体实现类。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
     //有没有设置proxyTargetClass="true" 并且是否继承接口
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
//jdk动态代理
return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } }

加载入口:AbstractAutoProxyCreator.class 实现了postProcessAfterInitialization()方法,初始化bean实例后,将调用此方法。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
        Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    } else {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}

 

posted @ 2019-02-24 15:03  小传风  阅读(309)  评论(0编辑  收藏  举报