代理模式 - spring aop 抛砖
Posted 没有梦想-何必远方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式 - spring aop 抛砖相关的知识,希望对你有一定的参考价值。
一、什么是代理模式
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方
法。
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目
的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想
在现实中的一个例子。
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。
代理模式两种方式:
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 : 接口及接口方法实现都加上注解。
以上是关于代理模式 - spring aop 抛砖的主要内容,如果未能解决你的问题,请参考以下文章