当前位置: 首页 > 后端技术 > Java

Spring基础只是对AOP概念的介绍

时间:2023-04-01 16:04:49 Java

Spring容器包含两个重要的特性:面向方面编程(AOP)和控制反转(IOC)。面向方面编程是对面向对象(OOP)的补充。在面向对象编程的过程中,编程的目标是各个对象,而在面向方面的编程中,编程的目标是各个方面。方面支持跨类型和对象的模块化(例如,事务的方面可以添加到任何地方)。前言AOP是Spring的关键特性之一,虽然Spring的IOC特性不依赖于AOP(意思是你可以只使用Spring的IOC特性,不使用AOP特性),但是两者的结合就可以了可以灵活实现很多中间件方案。比如我们经常使用的事务(@Transaction)就是通过AOP方案实现的。本文重点介绍AOP编程中的一些术语,并不局限于Spring,而是适用于所有AOP编程。切面:面向切面的编程可以跨类、跨对象进行切面编程。方面是一类横切关注点的模块化。入口点(JoinPoint):程序执行过程中的一个点,如方法调用、字段访问、异常抛出等。增强(Advice):用于切片增强,包括前增强、后增强和环绕增强。大多数AOP框架拦截切入点并在切入点处维护一个拦截器链。目标对象(TargetObject):包含一个或多个方面的对象。AOP代理(AOPProxy):通过Java动态代理或CGLib增强获得的代理对象。编织:将方面集成到一个完整的流执行过程中。Spring的AOP功能和目标Spring的AOP是用纯Java语言实现的(比如AspectJ不是Java语言),不需要任何额外的编译过程,不需要修改类加载器,适用于任何Servlet容器和应用服务.Spring的AOP只支持方法拦截,不支持字段拦截。如果用户需要使用字段拦截,可以考虑引入AspectJ等类似的框架。Spring的AOP框架与其他框架有些不同。Spring的Aop框架不仅仅是提供一个AOP的功能,它更重要的功能是结合Spring的IOC容器提供一些企业应用服务的解决方案(比如事务等),我们可以通过同样的方式来定义切面定义一个普通的bean。Spring的AOP不支持非常细粒度的AOP,只支持容器内bean的AOP。如果需要更细粒度的AOP,可以考虑使用AspectJ。Spring容器的一个优秀特性就是非侵入性,所以你可以灵活选择自己的AOP方案,不一定非要使用Spring的方案。代理模式Spring对实现接口的方法默认使用Java动态代理实现AOP拦截,对非接口方法使用CGLIB字节码工具实现代理。@AspectJ的支持@AspectJ注解可以将一个普通的Java类声明为一个切面。@AspectJ注解是AspectJ5引入的注解。虽然Spring可以读取AspectJ5注解来配置切面元数据,但它仍然使用SpringAOP在运行时进行代理,而没有使用AspectJ的编译器或编织逻辑。.稍后我们将讨论如何在Spring中使用AspectJ的编译器和编织逻辑。启用@AspectJSpring默认不启用AspectJ。如果需要Spring支持@AspectJ注解对应的切面,可以在配置类中添加注解或者使用XML开启配置(AspectJ所在包为:aspectjweaver.jar)。通过Java注解开启AspectJ注解支持:@Configuration@EnableAspectJAutoProxypublicclassAppConfig{}通过XML配置开启AspectJ注解定义切面当开启AspectJ支持时,开发者定义的任何Aspect切面都会自动被使用Detected,然后SpringAOP会拦截切面。以下两个示例显示了如何配置AspectJ切面。您可以通过添加@Component注解向Spring容器注册切面Bean。packageorg.xyz;importorg.aspectj.lang.annotation.Aspect;@AspectpublicclassNotVeryUsefulAspect{}在程序运行过程中声明了一个我们感兴趣的pointcut切入点。Spring的AOP框架只支持在SpringBean方法上发现切入点,所以切入点可以简单理解为SpringBeanMethods。切入点识别感兴趣的连接点,允许我们控制建议何时运行。springaop只支持springbean的方法执行连接点,所以pointcut可以看作是匹配springbean上的方法执行。切入点声明由两部分组成:由名称和任何参数组成的签名,以及确定我们感兴趣的方法执行的切入点表达式。在AOP的@AspectJ注释样式中,切入点签名由常规方法提供定义和切入点表达式由@pointcut注释指示(用作切入点签名的方法必须具有void返回类型)。入口点由两部分组成,一部分是用于区分不同入口点的标识符(下例中的privatevoidanyOldTransfer(){}),另一部分是表达式,用于确定我们感兴趣的Bean方法(在下面的例子@Pointcut("execution(*transfer(..))")),下面的例子展示了切入点的定义:@Pointcut("execution(*transfer(..))")//切入点表达式privatevoidanyOldTransfer(){}//切入点签名Spring匹配切入点的语法使用AspectJ5中的表达式语法。我们可以参考AspectJ文档相关的语法。常见的切入点匹配表达式Spring支持以下常见的AspectJ切面定义语法:执行:匹配方法的连接点。@Pointcut("execution(public**(..))")privatevoidanyPublicOperation(){}within:用于匹配类型内的方法。@Pointcut("within(com.xyz.myapp.trading..*)")privatevoidinTrading(){}this:匹配当前AOP代理对象的执行方法@target(org.springframework.transaction.annotation.Transactional)target:target匹配目标对象的类型,即被代理对象的类型。比如A继承了B接口,那么使用target("B"),target("A")可以匹配A//当前AOP对象实现了IPointcutService接口的任意方法@Pointcut("target(cn.javas.spring.chapter6.service.IPointcutService)")privatevoidanyPublicOperation(){}args:用于限制切入点方法的参数类型。args(java.io.Serializable)@target:代理对象应该包含指定的注解。@target(org.springframework.transaction.annotation.Transactional)@args:被代理对象的参数包含指定的注解。@args(com.xyz.security.Classified)@within:代理对象应该包含指定的注解@within(org.springframework.transaction.annotation.Transactional)@annotation:入口点包含指定的注解。@annotation(org.springframework.transaction.annotation.Transactional)我们可以通过“&&”和“||”组合多个条件,AspectJ还有很多其他的表达方式,但是Spring不支持除上述其他表达方式之外的其他表达方式。其他的AspectJ表达式还有:call,get,set,preinitialization,staticinitialization,initialization,handler,adviceexecution,withincode,cflow,cflowbelow,if,@this,@withincode等。在我们使用Spring的代理方法之前,我们应该知道它的代理原则。Java动态代理只能拦截对公共接口方法的调用,而CGLib只能拦截public、protected和defult方法。如果需要更深层次的拦截,可以考虑使用底层的Aspectj。切面增强我们在上面的步骤中定义了一个入口点,现在我们可以在这个入口点上进行额外的操作。这些额外的操作称为增强。Spring支持四种增强方式:前置增强、后置增强、异常增强和环绕增强。Spring支持直接在增强方法的定义上定义切入点。前增强BeforeAdviceimportorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;@AspectpublicclassBeforeExample{@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")publicvoiddoAccessCheck(){//...}}importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;@AspectpublicclassBeforeExample{@Before("执行(*com.xyz.myapp.dao.*.*(..))")publicvoiddoAccessCheck(){//...}}后增强importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.AfterReturning;@AspectpublicclassAfterReturningExample{@AfterReturning(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",returning="retVal")publicvoiddoAccessCheck(ObjectretVal){//...}}异常增强导入org.aspectj。lang.annotation.Aspect;importorg.aspectj.lang.annotation.AfterThrowing;@AspectpublicclassAfterThrowingExample{@AfterThrowing(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",throwing="ex")publicvoiddoRecoveryActions(DataAccessExceptionex){//...}}环绕增强导入org.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.ProceedingJoinPoint;@AspectpublicclassAroundExample{@Around("com.xyz.myapp.CommonPointcuts.businessService()")公共对象doBasicProfiling(ProceedingJoinPointpjp)throwsThrowable{//启动秒表ObjectretVal=pjp.proceed();//停止秒表returnretVal;}}代理机制我们之前说过,SpringAOP是通过动态代理和CGLIB来实现AOP对象的代理,我们可以通过下面的配置来设置所有的动态代理使用CGLIB。代理工厂使用SpringAOP实现代理的核心类是AspectJProxyFactory,我们可以使用此类以编程方式生成代理对象://创建一个可以为给定目标对象生成代理的工厂可以根据需要使用不同的aspectfactory.addAspect(SecurityManager.class)多次调用它;//您还可以添加现有的方面实例,提供的对象类型必须是@AspectJaspectfactory.addAspect(usageTracker);//现在获取代理对象...MyInterfaceTypeproxy=factory.getProxy()本文首发于微信公众号,版权所有,禁止转载!