Spring 与 Aspectj 集成

首先,让我们从 AOP(Aspect Oriented Progarmming) 开始, AOP 是 OOP 的强有力的补充,那么关键的问题是:发现横切性的问题( Cross cutting concern ),把横切性的问题进行模块化,即抽象出类,我们把这个类叫做 Aspect( 切面 ) ,然后定义 Advice( 通知 ) 也就是在切点上执行的动作和 Pointcut( 切点 ) 即匹配的表达式,找到对应的 Pointcut ,织入通知,这也就是 AOP 的思想。那么什么是 AspectJ 的,它是一个 (AOP) 的强大工具。 Spirng 提供了对 Aspectj 的集成,好了让我们看看怎么做 AOP 的开发吧。

I . 先建立一个 Java 工程 , 拷贝相关的 jar 包,

★     SPEING-HOME/dist/spring.jar

★     SPEING-HOME/lib/jakarta-common/common-logging.jar

★     SPEING-HOME/lib/log4j/log4j-1.2.14.jar

★     SPEING-HOME/lib/aspectj/*.jar

II . 建立业务逻辑类,

UserManager
         package  com.aop;
public   interface  UserManager {
      public   void  addUser(String name, String passwor);
      public   void  delUser( int  id);
}
 
UserManagerImpl
package  com.aop;
public   class  UserManagerImpl  implements  UserManager {
      public   void  addUser(String name, String password) {
        System. out .println( "=========addUser==========" );
}
      public   void  delUser( int  id) {
        System. out .println( "=========delUser============" );
     }
}
 
SecurityHandler
package  com.aop;
import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Before;
import  org.aspectj.lang.annotation.Pointcut;
 
@Aspect
public   class  SecurityHandler {
     
      @Pointcut ( "execution(* add*(..))" )
      private   void allMethods()
 
      @Before ( "allMethods()" )
      private   void checkSecurity()
        System. out .println( "========checkSecurity==========" );
     }
}
 
Client
package  com.aop;
 
import  org.springframework.beans.factory.BeanFactory;
import  org.springframework.context.support.ClassPathXmlApplicationContext;
 
public   class Client
 
      public   static   void  main(String[] args) {
        BeanFactory factory =  new  ClassPathXmlApplicationContext( "applicationContext-common.xml" );
        UserManager userManager = (UserManager)factory.getBean( "userManager" );
        userManager.addUser( "name" ,  "password" );
     }
 
}
 
ApplicationContext-common.xml
< aop:aspectj-autoproxy />
           < bean id = "userManager" class = "com.aop.UserManagerImpl" />
      < bean id = "securityHanler" class = "com.aop.SecurityHandler" />           
好了,动手测试下吧,(注意启用 AspectJ 对 Annotation 的支持,并且将 Aspect 类和目标对象配置到 IOC 容器中)。
下面是静态配置到 .xml 文件中
SecurityHandler2
package com.aop;
 
public class SecurityHandler2 {
 
        private void checkSecurity() {
               System.out.println("========checkSecurity==========");
        }
}
 
ApplicationContext-common.xml
      < bean id = "userManager" class = "com.aop.UserManagerImpl" />
      < bean id = "securityHandler" class = "com.aop.SecurityHandler2" />  
      < aop:config >
         < aop:aspect id = "securityaop" ref = "securityHandler" >
             < aop:pointcut id = "allMethod" expression = "execution(* com.aop.UserManager.add*(..))" />
             < aop:before method = "checkSecurity" pointcut-ref = "allMethod" />
         </ aop:aspect >
      </ aop:config >

其他的不变。

(注意如果没有匹配的 poitcut, 那么将不生成 dynamic 代理,即使有相应的配置。)

可能大家感到不满?为什么呢,在动态代理中我们可以拿到 JoinPoint 的参数,那么在 AspectJ 中有这个功能吗?答案是肯定的,我们只要在相应的 Advice 中添加 JoinPoint 参数,从中就可以拿到你需要的参数和信息了。

private   void checkSecurity(JoinPoint joinPoint)
        Object[] args = joinPoint.getArgs();
         for  ( int  i = 0; i < args. length ; i++) {
            System. out .println(args[i]);
        }
        System. out .println(joinPoint.getSignature().getName());
        System. out .println( "========checkSecurity==========" );
     }

Spring AOP 默认情况下用 JDK 的动态代理,那么这个类必须实现接口。如果这个类没有实现接口那么要引入 CGLIB 库才可以。

总结:

1.    如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理。

2.       如果目标对象实现了接口,也可以使用 CGLIB 实现 AOP .

3.       如果目标对象没有实现接口,那么必须采用 CGLIB 库。 Spring 会在 JDK 动态代理和 CGLIB 之间转换。

如何强制使用 CGLIB 实现 AOP

☆     引入 %SPRING-HOME%/lib/cglib/*,jar

☆     在 spring 配置文件中加入 <aop:aspectj-autoproxy proxy-target-class=”true “/>

JDK 动态代理和 CGLIB 字节码生成的区别?

☆     JDK 动态代理只能针对实现了接口的类生成代理,而不针对类

☆     CGLIB 是针对类实现代理,主要是针对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类和方法最好不要声明成 final

但是,在实践中我们一般还是用 JDK 的动态代理 .