Spring的AOP功能对于大部分的应用来说可以说是足够的,但这种AOP还是有不少情况下满足不了需求,而且Spring的AOP是通过其自身的代理实现的,如果因为某些原因不能或不想使用代理,例如ORM情况下的实体,一般由JPA、Hibernate,topLink等持久化框架创建的,并不在Spring容器的管理下,那就只好另想办法了。
AspectJ可以说是目前开源面向切面编程最强的实现,几乎能满足所有切面编程的需求,能管理任何你想管理的bean,并且能和Spring完美的整合在一起,那么有了能大小通吃的AspectJ,你还想用Spring自己的阉割过的AOP鸟炮么。。
管理容器内的切面大家一般都会用了,不会用的就看Spring文档的AOP部分。但管理容器外的切面,那就非得AspectJ亲自出面了,再进一步,如果能让 AspectJ 的容器外 切面也能享受 Spring Ioc 的待遇,那么Spring容器内外就能真正打成一片了,如此就能发挥无穷威力,做更多以前难以做到的事情。比如Entity的字段级授权控制。那就是小菜一碟了。
说了那么多虚的,下面给例子大家看看,就知道威力何在了(本例子需要给eclipse安装AJDT插件用来编译 切面类,也可以通过 iajc的 ant任务编译AspectJ切面类):
1.定义一些常用命名切点
package com.yotexs.aop;
import org.aspectj.lang.annotation.Pointcut;
/**
* 命名切点类,定义一些基本的切点匹配模式,可以在任何其他的AspectJ切面定义中引用
* <p>
* 用法:如匹配classpath中注解了@Repository的类的所有public的方法
* <pre>
* @Around("com.yotexs.aop.Pointcuts.publicMethodOnly() && com.yotexs.aop.Pointcuts.atRepository()")
* </pre>
* @author bencmai 麦田枭鹰 QQ:36767935
*/
public abstract class Pointcuts {
/** 匹配public方法 */
@Pointcut("execution(public * *.*(..))")
public void publicMethodOnly() {}
/** 匹配public get方法 */
@Pointcut("execution(public * *.get*(..))")
public void publicGetMethodOnly() {}
/** 匹配@Repository注解过的类 */
@Pointcut("@within(org.springframework.stereotype.Repository)")
public void atRepository() {}
/** 匹配@Service注解过的类 */
@Pointcut("@within(org.springframework.stereotype.Service)")
public void atService() {}
/** 匹配@Configurable注解过的类 */
@Pointcut("@within(org.springframework.beans.factory.annotation.Configurable)")
public void atConfigurable() {}
}
2.以注解方式定义一个AspectJ(如果你喜欢用AspectJ语言,也可以用AspectJ语言编码方式定义切面)
package com.yotexs.aop.spring;
import com.yotexs.ext.springsecurity.aspectj.AspectJCallback;
import com.yotexs.ext.springsecurity.aspectj.AspectJSecurityInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
/**
* Spring AspectJ 方式服切面配置
*
* @author bencmai 麦田枭鹰 QQ:36767935
*/
//@Component
@Aspect
public class SecurityAspectJ implements Ordered {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private int order = 1; //用于指定切面类之间的增强织入顺序
//注入Spring Security AspectJ方法安全拦截器处理器,此拦截器来自Spring容器内,
//但可以同时用于容器内外的所有需要方法调用的安全监控和事后返回数据过滤的审查(不知道什么原因,网上还没有发现有文章介绍Spring Security 这种比MethodSecurityInterceptor强悍的多的用法)
@Autowired
private AspectJSecurityInterceptor securityInterceptor;
/** 服务层命名切点--匹配注解了@Service的服务层暴露的public方法 */
@Pointcut("com.yotexs.aop.Pointcuts.publicMethodOnly()&& com.yotexs.aop.Pointcuts.atService()")
public void atServiceLayer() {}
/** 持久层命名切点--匹配注解了@Repository的持久层暴露的public方法 */
@Pointcut("com.yotexs.aop.Pointcuts.publicMethodOnly()&& com.yotexs.aop.Pointcuts.atRepository()")
public void atDaoLayer() {}
/** 领域层命名切点--匹配注解了@Configurable的持久层暴露的public get方法 */
@Pointcut("com.yotexs.aop.Pointcuts.publicGetMethodOnly()&& com.yotexs.aop.Pointcuts.atConfigurable()")
public void atEntityLayer() {}
/** 环绕增强容器内服务层--做服务调用安全和事后数据过滤检查。用于保护方法方法级 */
@Around("atServiceLayer()")
public Object atServiceLayerDoMethodSecurityCheck(final ProceedingJoinPoint joinPoint) throws Throwable {
final AspectJCallback callback = new AspectJCallback() {//回调封装
public Object proceedWithObject() throws Throwable {
Object obj = null;
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
System.out.println("----Service层发生非事务性异常:");
logger.info("----Service层发生异常:", e);
}
return obj;
}
};
return this.securityInterceptor.invoke(joinPoint, callback);
}
/** 环绕增强容器内持久层 */
@Around("atDaoLayer()")
public Object atDaoLayerDoMethodSecurityCheck(final ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("----执行:" + joinPoint.getSignature().toLongString());
Object obj = null;
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
System.out.println("----DAO层发生非事务性异常:");
logger.info("----DAO层发生异常:", e);
}
logger.info("----完成:" + joinPoint.getSignature().toLongString());
return obj;
}
/** 环绕增强容器外领域层对象--做字段访问权限检查 */
@Around("atEntityLayer()")
public Object atEntityLayerDoFieldMethodSecurityCheck(final ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("+++访问字段:" + joinPoint.getSignature().getName() + "方法");
final AspectJCallback callback = new AspectJCallback() {//回调封装
public Object proceedWithObject() throws Throwable {
Object obj = null;
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
logger.info("----Entity层发生异常:", e);
}
return obj;
}
};
System.out.println("+++完成字段:" + joinPoint.getSignature().getName());
return this.securityInterceptor.invoke(joinPoint, callback);
}
@Override
public int getOrder() { return order; }
/**
* 同一切面类中,依照增强在切面类中的声明顺序织入;<br>
* 不同切面类中,各切面类实现{@link Ordered} 接口,则顺序号小的先织入;<br>
* 不同切面类中,各切面类未实现{@link Ordered} 接口,织入顺序不确定;
*
* @param order
*/
public void setOrder(int order) { this.order = order; }
}
3.在spring Xml schema中注册切面
<bean id="securityAspectJ" class="com.yotexs.aop.spring.SecurityAspectJ" factory-method="aspectOf"/>
注意:factory-method="aspectOf",因为我们的securityAspectJ 切面被当成spring容器中的一个bean注册到容器中,但这个bean并不是由spring去创建的,而是由 AspectJ自己创建的单例,细心的你可能觉得这里有古怪,
可能会问,看了上面第2点的SecurityAspectJ的代码,里面并没有 aspectOf 这个静态工厂方法,这样注册到spring容器里不会报错吗。那么让我们看看返编译后的SecurityAspectJ 代码先:
反编译后的SecurityAspectJ切面类
package com.yotexs.aop.spring;
import ..;
public class SecurityAspectJ implements Ordered {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private int order;
private AspectJSecurityInterceptor securityInterceptor;
private static Throwable ajc$initFailureCause; /* AspectJ 编译后给切面类织入的代码*/
public static final SecurityAspectJ ajc$perSingletonInstance; /* AspectJ 编译后给切面类织入的代码*/
.......
public Object atServiceLayerDoMethodSecurityCheck(final ProceedingJoinPoint joinPoint) throws Throwable { ....... }
public Object atDaoLayerDoMethodSecurityCheck(ProceedingJoinPoint joinPoint) throws Throwable { ....... }
public Object atEntityLayerDoFieldMethodSecurityCheck(final ProceedingJoinPoint joinPoint) throws Throwable { ....... }
.....
/* AspectJ 编译后给切面类织入的代码*/
public static SecurityAspectJ aspectOf() {
if (ajc$perSingletonInstance == null)
throw new NoAspectBoundException("com.yotexs.aop.spring.SecurityAspectJ", ajc$initFailureCause);
else
return ajc$perSingletonInstance;
}
public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
private static void ajc$postClinit() {
ajc$perSingletonInstance = new SecurityAspectJ();
}
public static Logger ajc$inlineAccessFieldGet$com_yotexs_aop_spring_SecurityAspectJ$com_yotexs_aop_spring_SecurityAspectJ$logger(SecurityAspectJ securityaspectj) {
return securityaspectj.logger;
}
static {
try { ajc$postClinit(); } catch (Throwable throwable) { ajc$initFailureCause = throwable; }
}
}
所以,说SecurityAspectJ 是AspectJ自身通过静态工厂自己创建的,并不是由Spring去去创建的,而只是把它添加到容器中而已。
如果很不幸你在spring中注册AspectJ切面类时报错。请检查你有没有通过AJDT或者iajc的 ant任务编译AspectJ切面类,没编译的切面类里是不会存在上面反编译出来的代码的,就会报错。
4. 运行时织入(LTW) .
以上是静态织入(CTW)的做法。就是切面类了的增强方法中的代码在编译期间就已经分别织入到切点匹配上的目标类的链接点上了(织入到目标*.class的字节码中)。这种做法是性能比较好。但也存在局限,如你没有织入目标的源代码。或不想修改织入目标的字节码(可能这些*.class会用在不同的项目)时。就可以考虑使用运行时织入(LTW)。另外还有字节码织入(BTW).这里我们讨论LWT的方式
AspectJ默认情况下只支持代理方式的运行时织入,所以启动原生AspectJ的 LWT 程序,须指定 -javaagent:pathto/aspectjweaver.jar 的JVM参数,(CTW不用)。这就为同一个JVM多个Web应用带来了一点局限。尤其是托管环境下不好给JVM指定此参数(主机是你自己能完全控制的当然就没问题)。
在Spring容器整合环境下的无需指定-javaagent的JVM参数的 解决办法是在spring Xml schema添加:
4.A
<context:load-time-weaver />
此外如果是tomcat,改为添加,并把spring-tomcat-weaver.jar拷贝到tomcat安装目录的下的lib目录
<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" />
同时在发布应用的根目录或war中添加此配置文件([$approot]/META-INF/context.xml)
<!-- Tomcat context descriptor used for specifying a custom ClassLoader -->
<Context path="/yotexs" reloadable="false">
<!-- please note that useSystemClassLoaderAsParent is available since Tomcat 5.5.20 / remove if previous versions are being used -->
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/>
</Context>
4.B
在classpath下增加 [$classpath]/META-INF/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver options="-showWeaveInfo -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
<include within="com.yotexs..*" />
</weaver>
<aspects>
<aspect name="com.yotexs.aop.spring.SecurityAspectJ" />
<!--定义抽象切面
<concrete-aspect name="com.yotexs.aspectj.EntityFieldSecurityAspectJ"
extends="com.yotexs.aspectj.AbstractEntityFieldSecurityAspectJ">
<pointcut name="atServiceLayerScope"
expression="execution(public * *.get*(..)) &&
@within(org.springframework.beans.factory.annotation.Configurable)" />
</concrete-aspect>-->
</aspects>
</aspectj>
总结。
经过以上配置。你就可以在应用的任何“层”上应用 AspectJ 切面了。使用AspectJ的意义在于突破Spring AOP 自身的局限。让你有完全free的感觉。又能享受Spring 的Ioc待遇。如果你还想借助Spring Security实现字段级授权。有了AspestJ管理容器外bean,说白了也只是把容器内的AbstractSecurityInterceptor 的方法从本来应用到容器内的服务层服务层方法拦截,给他通过AspectJ切面拖到容器外切入领域层而已。道理同样是应用方法拦截。这样通过Spring Security 中的ACL 负责定义记录级的授权。RBAC中的资源(Rourse)负责定义定义URL、ServiceMethod,FieldMethod的访问授权。
AspectJ可以做的东西非常多。以上只是简单的例子。enjoy it !!!
分享到:
相关推荐
依赖注入是IoC的一种具体实现方式,它允许对象在运行时动态地获得依赖。Spring支持三种类型的依赖注入: 1. **构造器注入**:通过构造函数传入依赖对象。 2. **设值注入**:使用setter方法注入依赖。 3. **接口注入...
5. **织入(Weaving)**:将切面与目标对象结合的过程,可以在编译时、类加载时或运行时完成。 6. **代理(Proxy)**:Spring AOP通过动态代理实现切面的织入。对于接口,使用JDK动态代理;对于非接口类,使用CGLIB...
Spring支持运行时织入和编译时织入两种方式。 6. **AspectJ**:Spring支持使用AspectJ进行更强大的AOP编程,包括注解驱动的切面和类型安全的元数据表达式。 通过这些机制,Spring的IOC和AOP不仅简化了对象的管理和...
基于代理的织入通常用于Spring的IoC容器中的bean,而基于字节码的织入则是在运行时通过ASM库动态修改类的字节码来实现,这正是@AspectJ所采用的方式。 @AspectJ是Spring AOP的一个扩展,它提供了一种更接近传统编程...
《Spring IOC容器实现分析》 在Java开发领域,Spring框架无疑是使用最为广泛的轻量级框架之一,其中的核心组件就是IOC(Inversion of Control)容器。本文将深入剖析Spring的IOC容器,理解其工作原理和重要功能,以...
Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented Programming,面向切面编程),这些功能大大简化了Java应用程序的开发工作。在众多的...
pring源代码各个模块作用 核心模块: 1 spring-core:核心模块 依赖注入IOC和DI的最基本实现 spring-beans:Bean工厂与装配 spring-context:上下文,即IOC...spring-aspects:集成AspectJ,Aop应用框架 spring-instrume
### Spring IoC容器部署实现详解 #### 一、Spring IoC容器概述 Spring框架的核心特性之一就是Inversion of Control(IoC),也被称为Dependency Injection(DI)。IoC容器是Spring框架的重要组成部分,它负责管理...
Spring IoC容器是Spring框架的核心,它负责管理应用对象的生命周期和依赖关系。通过对IoC(Inversion of Control,控制反转)的实现,Spring容器将对象的创建和组装工作从应用代码中分离出来,使得应用更易于测试和...
在Spring框架中,依赖注入(Inversion of Control, IoC)和面向切面编程(Aspect Oriented Programming, AOP)是两大核心特性。本篇将深入探讨如何通过注解方式来模拟Spring的这两种机制,帮助你理解其底层原理。 #...
Spring框架是Java开发中不可或缺的一部分,它通过提供两种核心特性——控制反转(IoC)和面向切面编程(AOP)来简化应用的构建。理解并掌握这两种技术对于任何Java开发者来说都至关重要。 **控制反转(IoC)**,也...
**Spring IOC 容器应用实例** Spring 框架的核心组件之一是 Inversion of Control (IoC) 容器,也常被称为依赖注入(Dependency Injection)容器。IoC 是一种设计模式,它将对象的创建和管理从应用程序的业务逻辑中...
此外,对于复杂的切面逻辑,可能需要借助于AspectJ库,它是Spring AOP的底层实现,提供了更强大的表达式语言和更全面的织入支持。 通过阅读和实践《https://countme.iteye.com/blog/1132555》这篇博文,你可以深入...
9. `org.springframework.aspects-3.0.5.RELEASE.jar`: 提供了与AspectJ的集成,使得Spring可以支持更高级的面向切面编程特性,如编译时织入和类型级别的切点表达式。 这些jar包共同构成了Spring IOC的基础,通过...
模仿Spring IOC时,可以考虑如何实现简单的切面和通知。 5. **类型转换与自动装配**:Spring容器能自动将Bean的属性设置为正确类型的值,这需要一个类型转换系统。同时,容器还可以通过类型匹配进行自动装配,减少...
Spring AOP 通过动态代理技术实现了切面的织入,可以方便地对方法执行前、后或异常处理时进行拦截。 **Spring 框架的主要模块** 1. **Core Container(核心容器)**:包括 Core、Beans、Context 模块,提供 IoC 和...
Spring IOC(Inversion of Control,控制反转)是Spring框架的核心特性,它将对象的创建和管理权交给了Spring容器,从而解耦了应用代码与对象生命周期管理的关系。在这个例子中,我们将通过导入Excel数据来深入理解...
在Spring框架中,IOC具体体现在对依赖关系的管理上,使得对象的创建、依赖关系的绑定等过程不再由应用程序自身完成,而是转移到一个外部容器(IoC容器)中进行管理,这样就实现了控制权的反转。 首先,Spring IoC...
Spring支持编译时织入、加载时织入和运行时织入。 在实际开发中,Spring AOP常用于事务管理,通过声明式事务管理,开发者可以在配置文件或注解中定义事务规则,无需在业务代码中显式处理事务。 综上所述,Spring的...