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; } }