Spring-AOP
来源:互联网 发布:熊片数据库手机版 2.1 编辑:程序博客网 时间:2024/06/06 03:05
- AOP简介
- 理解
- 术语
- 通知advice
- 连接点join point
- 切点point cut
- Aspect切面
- 目标对象Target
- 引入introductions
- Spring对AOP的支持
- AOP应用
- XML形式的AOP
- Annotation形式的AOP
- 一个Annotation与AOP结合的例子
- AOP原理
- JDK动态代理
- 基于CGLIB的动态代理
- ASM介绍
- Javassist介绍
- JDK动态代理与CGLIB性能比较
- AOP简介
AOP简介
理解
AOP(Aspect-Oriented Programming), 即 面向切面编程,其基本思想是在极少影响原程序的代码的前提下,在程序中的某些地方,使用某些方式,不可见的(即不在原程序中添加其他代码)为原程序切入一些额外的功能。
优点:
- 减少代码间的耦合性,使功能具有拔插性,保证自己代码的清洁性。
- 能够让你只关注自己的代码,不需要关注切面是如何实现的。
术语
通知(advice)
其定义了切点什么时候去增强,是在方法调用前,还是调用之后,还是前后都是,还是抛出异常时。
- Before 某方法调用之前发出通知。
- After 某方法完成之后发出通知,不考虑方法运行的结果。
- After-returning 将通知放置在被通知的方法成功执行之后。
- After-throwing 将通知放置在被通知的方法抛出异常之后。
- Around 通知包裹在被通知的方法的周围,在方法调用之前和之后发出通知
连接点(join point)
可以被作为切点的地方,都可以被认为是链接点。
切点(point cut)
按照规则被选中的链接点,可以被称作为切点。
Aspect(切面)
aspect
由 pointcount
和 advice
组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
目标对象(Target)
织入 advice 后的目标对象. 目标对象也被称为 advised object
.
引入(introductions):
- 引入允许你添加一个新的方法给已经存在的类。
Spring对AOP的支持
- Spring建议在Java中书写AOP
- Spring是在运行阶段才将切面编织进bean中,是使用代理类。
- Spring只支持方法级别的连接点。
AOP应用
XML形式的AOP
proxy-target-class="true"
指定使用GCLIB代理,如果proxy-target-class="false"
或者没设置,则默认使用动态代理,但是如果代理类没有实现接口,则依然会使用GCLIB代理。
aop:pointcut
指定了切点。
aop:advisor
指定了通知时机,同样的还有aop:before
aop:after
。
需要注意的是spiritCommonInterceptor实现了MethodInterceptor接口
<bean id="spiritCommonInterceptor" class="com.mogujie.stable.spirit.point.methond.CommonInterceptor"/> <aop:config proxy-target-class="true"> <aop:pointcut id="modulePoint" expression="@target(com.mogujie.stable.spirit.point.annotation.ClassSpirit) and @annotation(com.mogujie.stable.spirit.point.annotation.MethodSpirit)"/> <aop:advisor advice-ref="spiritCommonInterceptor" pointcut-ref="modulePoint"/> </aop:config>
public class CommonInterceptor implements MethodInterceptor { private static final Logger LOG = LoggerFactory.getLogger(CommonInterceptor.class); @Override public Object invoke(MethodInvocation invocation) throws Throwable { Method executed = invocation.getMethod(); Class<?> clazz = invocation.getThis().getClass(); ClassSpirit classSpirit = clazz.getAnnotation(ClassSpirit.class); MethodSpirit methodSpirit = executed.getAnnotation(MethodSpirit.class); // 不做限流降级处理 if (executed.getName().equals("toString") || executed.getName().equals("hashCode") || executed.getName().equals("equals") || (null != classSpirit && !classSpirit.trace()) || (null == classSpirit && null != methodSpirit && !methodSpirit.trace()) || ((null != classSpirit && classSpirit.trace()) && (null == methodSpirit || !methodSpirit.trace()))) { return invocation.proceed(); } Entry entry = null; try { String methodName = MethodUtil.getMethodName(executed); // 初始化Context ContextUtil.enter(methodName); // 初始化Entry entry = EntryUtil.entry(executed); // 执行方法 Object result = invocation.proceed(); return result; } catch (Throwable e) { throw ExceptionUtil.dealProxyException(e); } finally { if (entry != null) { entry.exit(); } ContextUtil.exit(); } }}
Annotation形式的AOP
Spring除了支持Schema方式配置AOP,还支持注解方式:使用@Aspect来配置。但Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持:
<aop:aspectj-autoproxy/>
package com.sxit;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class AspectStyle { @Pointcut("execution(* com.sxit..*.*(..))") public void init(){ } @Before(value="init()") public void before(){ System.out.println("方法执行前执行....."); } @AfterReturning(value="init()") public void afterReturning(){ System.out.println("方法执行完执行....."); } @AfterThrowing(value="init()") public void throwss(){ System.out.println("方法异常时执行....."); } @After(value="init()") public void after(){ System.out.println("方法最后执行....."); } @Around(value="init()") public Object around(ProceedingJoinPoint pjp){ System.out.println("方法环绕start....."); Object o = null; try { o = pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("方法环绕end....."); return o; }}
一个Annotation与AOP结合的例子
要实现一个aop的功能,关键在于三个地方。
- 通知(Advice) 定义了何时
切。比如:before、around等。
- 切点(PointCut) 定义了何处
切。比如:execution(* com.mogujie.houston.openapi.api.impl..*(..))
- 连接点(JoinPoint) 连接点是在应用执行过程中能够插入切面的一个点。能够利用它拿到应用的方法和参数等。
aspect
@Aspect@Componentpublic class ValidatorAspect implements ApplicationContextAware { private static Logger logger = LoggerFactory.getLogger(ValidatorAspect.class); protected static ApplicationContext context; @Around("execution(* com.mogujie.houston.openapi.api.impl..*(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); if (method.getDeclaringClass().isInterface()) { try { method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes()); } catch (final SecurityException exception) { } } // check passport Object[] args = joinPoint.getArgs(); Validator validatorClass = method.getAnnotation(Validator.class); if (null != validatorClass) { ValidationHandler validationHandler = validatorClass.handler().newInstance(); HoustonOpenApiResult result = validationHandler.validate(args); if (!result.isSuccess()) { return result; } } TokenValidator tokenValidatorClass = method.getAnnotation(TokenValidator.class); if (tokenValidatorClass != null) { TokenValidationHandler tokenValidationHandler = tokenValidatorClass.handler().newInstance(); HoustonOpenApiResult result = tokenValidationHandler.check(args, context); if (!result.isSuccess()) { return result; } } } catch (Exception e) { logger.error("ValidatorAspect验证出现异常", e); return HoustonOpenApiResult.error(OpenApiResultCode.INNER_ERROR, "系统异常 请@Houston答疑, error:" + e.getMessage()); } try { return joinPoint.proceed(); } catch (Exception e) { logger.error("Service服务出现异常", e); return HoustonOpenApiResult.error(OpenApiResultCode.INNER_ERROR, "Service出现异常 请@Houston答疑, error:" + e.getMessage()); } } public static ApplicationContext getContext() { return context; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ValidatorAspect.context = applicationContext; }}
@TokenValidator
@Documented@Target({ElementType.METHOD})//只能在方法上使用@Retention(RetentionPolicy.RUNTIME)//运行时使用public @interface TokenValidator { Class<? extends TokenValidationHandler> handler() default TokenValidationHandler.class;//定义了一个接口类}
TokenValidationHandler
public interface TokenValidationHandler { HoustonOpenApiResult check(Object[] args, ApplicationContext applicationContext); HoustonOpenApiResult getGroupResult(Object[] args, DefaultGroupBiz defaultGroupBiz);}
一个Handler的实现
public abstract class BaseTVHandler implements TokenValidationHandler { @Override public HoustonOpenApiResult check(Object[] args, ApplicationContext context) { if (args.length >= 2) { Token token = (Token) args[0]; DefaultGroupBiz defaultGroupBiz = context.getBean(DefaultGroupBiz.class); HoustonOpenApiResult<Group> groupResult = getGroupResult(args, defaultGroupBiz); if (!groupResult.isSuccess()) { return groupResult; } if (TokenUtil.check(token, groupResult.getData().getKeyName())) { return HoustonOpenApiResult.success(true); } return new HoustonOpenApiResult(OpenApiResultCode.TOKEN_ILLEGAL); } else { return new HoustonOpenApiResult(OpenApiResultCode.TOKEN_PARAM_ERROR); } }}
public class ConfigValueTokenValidator { public static class DetailHandler extends BaseTVHandler { @Override public HoustonOpenApiResult getGroupResult(Object[] args, DefaultGroupBiz defaultGroupBiz) { ConfigValueDetail configValueDetail = (ConfigValueDetail) args[1]; return defaultGroupBiz.queryByConfigId(configValueDetail.getConfigId()); } }}
AOP原理
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。
JDK动态代理
步骤
- 通过实现InvocationHandler接口创建自己的调用处理器
- 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
public class DynamicTest implements InvocationHandler { private Test target; private DynamicTest(Test target) { this.target = target; } public static Test newProxyInstance(Test test) { return (Test) Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), new DynamicTest(test)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); }}
基于CGLIB的动态代理
CGLIB直接生成代理目标类的子类,不能对目标类中的final方法进行代理。
查找A上的所有非final 的public类型的方法定义;
将这些方法的定义转换成字节码;
将组成的字节码转换成相应的代理的class对象;
实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
public class CglibTest implements MethodInterceptor { private CglibTest() { } public static <T extends Test> Test newProxyInstance(Class<T> targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(new CglibTest()); return (Test) enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); }}
ASM(介绍)
ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。
Javassist(介绍)
Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。
JDK动态代理与CGLIB性能比较
- 被代理接口
public interface Test { public int test(int i);}
- 实现类
public class TestImpl implements Test { @Override public int test(int i) { return i + 1; } public void print() { System.out.println("111111"); }}
- JDK代理类
public class DynamicTest implements InvocationHandler { private Test target; private DynamicTest(Test target) { this.target = target; } public static Test newProxyInstance(Test test) { return (Test) Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), new DynamicTest(test)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); }}
- CGLIB代理类
public class CglibTest implements MethodInterceptor { private CglibTest() { } public static <T extends Test> Test newProxyInstance(Class<T> targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(new CglibTest()); return (Test) enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); }}
- 测试类
public class ProxyPerfTester { public static void main(String[] args) { //创建测试对象; Test nativeTest = new TestImpl(); Test dynamicProxy = DynamicTest.newProxyInstance(nativeTest); Test cglibProxy = CglibTest.newProxyInstance(TestImpl.class); //预热一下; int preRunCount = 100000; runWithoutMonitor(nativeTest, preRunCount); runWithoutMonitor(cglibProxy, preRunCount); runWithoutMonitor(dynamicProxy, preRunCount); //执行测试; Map<String, Test> tests = new LinkedHashMap<String, Test>(); tests.put("Native ", nativeTest); tests.put("Dynamic ", dynamicProxy); tests.put("Cglib ", cglibProxy); int repeatCount = 3; int runCount = 1000000; runTest(repeatCount, runCount, tests); runCount = 50000000; runTest(repeatCount, runCount, tests); } private static void runTest(int repeatCount, int runCount, Map<String, Test> tests){ System.out.println(String.format("\n==================== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] ====================", repeatCount, runCount, System.getProperty("java.version"))); for (int i = 0; i < repeatCount; i++) { System.out.println(String.format("\n--------- test : [%s] ---------", (i+1))); for (String key : tests.keySet()) { runWithMonitor(tests.get(key), runCount, key); } } } private static void runWithoutMonitor(Test test, int runCount) { for (int i = 0; i < runCount; i++) { test.test(i); } } private static void runWithMonitor(Test test, int runCount, String tag) { long start = System.currentTimeMillis(); for (int i = 0; i < runCount; i++) { test.test(i); } long end = System.currentTimeMillis(); System.out.println("["+tag + "] Elapsed Time:" + (end-start) + "ms"); }}
- 结果
Create Native Proxy:1msCreate Dynamic Proxy17msCreate Cglib Proxy521ms==================== run test : [repeatCount=3] [runCount=1000000] [java.version=1.7.0_79] ====================--------- test : [1] ---------[Native ] Elapsed Time:7ms[Dynamic ] Elapsed Time:289ms[Cglib ] Elapsed Time:93ms--------- test : [2] ---------[Native ] Elapsed Time:7ms[Dynamic ] Elapsed Time:12ms[Cglib ] Elapsed Time:51ms--------- test : [3] ---------[Native ] Elapsed Time:6ms[Dynamic ] Elapsed Time:14ms[Cglib ] Elapsed Time:45ms==================== run test : [repeatCount=3] [runCount=50000000] [java.version=1.7.0_79] ====================--------- test : [1] ---------[Native ] Elapsed Time:468ms[Dynamic ] Elapsed Time:1855ms[Cglib ] Elapsed Time:1577ms--------- test : [2] ---------[Native ] Elapsed Time:165ms[Dynamic ] Elapsed Time:418ms[Cglib ] Elapsed Time:807ms--------- test : [3] ---------[Native ] Elapsed Time:161ms[Dynamic ] Elapsed Time:484ms[Cglib ] Elapsed Time:889ms
可见在JDK1.7下:
- 运行速度,Native是最快的,JDK动态代理稍次之,CGLIB最慢。
- 创建速度,Native是最快的,JDK动态代理稍次之,CGLIB最慢。
- AOP、Spring的AOP
- AOP--Spring AOP
- Spring AOP 嵌套AOP
- spring AOP
- Spring AOP
- Spring AOP
- spring aop
- Spring AOP
- spring AOP
- spring aop
- Spring aop
- Spring-AOP
- Spring AOP
- spring aop
- spring aop
- Spring AOP
- Spring AOP
- Spring Aop
- 唤醒生成器的方法send()
- Android SDK根目录中的SDK Manager.exe双击打不开,为什么?
- git flow介绍
- 小仙女滴大二修仙之路
- 每一次技术进步,都有大公司接盘的身影
- Spring-AOP
- 关于Unity粒子系统优化,你可能遇到这些问题
- 日期类的实现
- 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字
- 虚拟机Hadoop-2.7.1环境搭建(CentOS-6.5)
- 2017第三届智能制造与工业4.0国际峰会即将于11月震撼来袭!
- 二.ARM裸机学习之S5PV210刷机
- 将spring cloud打包docker镜像
- ansible-playbook批量部署nginx