代理模式
来源:互联网 发布:淘宝不发货卖家不说话 编辑:程序博客网 时间:2024/05/29 05:10
一、什么是代理模式
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方 法。 举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目 的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想 在现实中的一个例子。 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。 代理模式两种方式: 1 静态代理:在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。 2 动态代理:代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方 法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理。
二、什么是spring aop
spring 使用 aop 面向切面编程将程序中的交叉业务逻辑(比如安全,日志,事务),封装成一个切面,然后注入到目标 业务逻辑中去。实现系统高内聚、低耦合,以弥补OOP编程思想的不足。
三、spring aop 如何实现的代理模式
1 创建时机:
在ioc容器初始化bean的过程中进行拦截,创建代理对象并“偷梁换柱”,替换原来的bean。
2 创建过程:
//创建代理对象 : DefaultAopProxyFactory的createAopProxy方法public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 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."); } if (targetClass.isInterface()) { //如果被代理的对象是接口,则使用jdk代理生成代理对象 return new JdkDynamicAopProxy(config); } //否则使用cglib代理生成代理对象 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
//jdk代理机制创建代理对象public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
//cglib代理机制创建代理对象public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class)); Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter(this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (Exception ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
以上,可以看出,jdk动态代理和cglib的区别:
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
四、spring aop 的两种使用方法
1 使用配置文件:
<!-- spring配置文件中添加配置切面: 通知 + 切入点 --><!--step1 : 编写通知类: 根据准备执行的方式实现不同接口,如下为throw通知--><!-- 通知有几种方式,before/after/afterReturning/around/throw等,可自主选择 -->public class ExceptionHandlerAdvice implements ThrowsAdvice{ public void afterThrowing(Method m, Object[] args, Object target, Exception ex) { log.error("Exception in method: " + m.getName() + " Exception is: {}", ex); }}<!--step2 : 组装通知类 --> <bean id="logger" class="com.etoak.util.LoggerAdvice"/><!--step3 : 配置切入点 --> <aop:config> <aop:pointcut expression="execution(* com.etoak.action.Stu*.add*(..))" id="pc"/> <!-- 将id="lc"这个通知类提供的功能引用给 id="pc"这个切入点指向的那组方法. --> <aop:advisor advice-ref="logger" pointcut-ref="pc"/> </aop:config>
2 使用注解:
<!--step1 : 首选添加注解配置,使aop注解生效 --><aop:aspectj-autoproxy/><!--step2 : 配置切点 -->private static final String POINTCUT ="execution(int com.web.aop.impl.ArithmeticCalculatorImpl.*(int , int ))";<!--step3 : 配置通知及方法,如下为返回通知,返回通知与after区别在可以取到返回值‘returnObj’ -->@AfterReturning(value = POINTCUT, returning = "returnObj") public void logArgAndReturn(final JoinPoint point, final Object returnObj) {}
关于 JoinPoint对象
- Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
- Object[] getArgs(); 获取传入目标方法的参数对象
- Object getTarget(); 获取被代理的对象
- Object getThis(); 获取代理对象
五、spring aop 使用时需注意的问题
使用代理时要明确需要使用的对象是代理对象还是目标对象
例如在以下方法中出现的问题:
@AfterReturning(value = POINTCUT, returning = "returnObj") public void logArgAndReturn(final JoinPoint point, final Object returnObj) { taskExecutor.execute(new Runnable() { @Override public void run() { try { Object[] args = point.getArgs(); ...... Method method=((MethodSignature) point.getSignature()).getMethod(); Annotation an = method.getAnnotation(UserChangeLog.class); ...... } catch (Exception e) { log.error("保存更新日志异常", e); } } }); }
我们拦截到了一个方法,方法上有@UserChangeLog注解,在下面的方法中却取不到,an 为null。这个问题的出现的原因我们简单来看,可能是因为我们通过代理对象来取注解,而代理对象生成是不会生成原始对象上带的注解,所以我们只能从目标对象上来取这个注解。
通过分析以上源码,可以看出,我们拦截的方法是实现接口的,所以采用了jdk动态代理,根据接口实现代理,而接口上是没有注解的,所以代理生成的方法也不会有注解。如果我们拦截的方法没有实现接口,那么使用cglib代理不会有问题。
如果我们必须用jdk代理,要解决此问题,有以下两种方法:
法1: 通过目标对象获取注解
Method method=((MethodSignature) point.getSignature()).getMethod(); Signature signature = point.getSignature();Method realMethod = point.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());Annotation an = realMethod.getAnnotation(UserChangeLog.class);
法2 : 接口及接口方法实现都加上注解。
阅读全文
0 0
- 代理模式--动态代理
- 代理模式-静态代理
- 代理模式-静态代理
- 代理模式 & 动态代理
- 代理模式--静态代理
- 代理模式--动态代理
- 代理模式(动态代理)
- 代理模式-动态代理
- 代理模式-动态代理
- 代理模式动态代理
- 代理模式-静态代理
- 代理模式-动态代理
- 代理模式 -动态代理
- 代理模式---动态代理
- 代理模式-动态代理
- 代理模式--静态代理
- 代理模式!
- 代理模式
- 视觉SLAM技术及其应用(章国锋--复杂环境下的鲁棒SfM与SLAM)
- 超级实用的Android磁盘缓存工具DiskDataCacher用法以及原理
- 谈谈 Python 程序的运行原理
- 3分钟带您了解Docker EE的访问控制原理(内附实操案例)
- python爬虫webdriver.Chrome 数据可视化简单案例matplotlib
- 代理模式
- Android 仿京东订单页面
- 数组
- 矩形的周长和面积
- Java的作业调度类库Quartz基本使用指南
- BioPerl-初探
- char、varchar 哪种的搜索效率高
- javax.script包
- 通过ajax将list传到后台的两种实现方式