Spring AOP

1、Spring AOP的一些概念

      IOC和AOP是Spring的两大核心,IOC(有的人也称其为DI)在Spring中是无处不在的。AOP在实际开发中貌似用的比较少,不少的博客说AOP可以用于日志打印、时间统计、运行监控等等。。但是在我遇到的项目中,日志还真不是用AOP来做的,基本都是强侵入性的。虽然AOP实际用的不多,但是还是需要学习的,因为码农的优良品质之一就是:永不停顿学习的脚步,无论快慢,都要保证自己在不断的前进。

      AOP(Aspect Oriented Programming)涉及的概念,随便Google一下就有很多,不过还是总结下,以后忘记了还能来看看。

A. Aspect(切面)
      AOP当然优先要有个A,就是Aspect(切面)。官方给出的解释是“一个关注点的模块化,这个关注点可能会横切多个对象”。切面就是监控的类所关注的行为。比如定义一个类 Monitor为切面,对象Car有行为Run()和stop()。那么Monitor就是关注点的模块化,被横切的对象就是Car,关注点关注的行为就是Car的Run()和Stop()。

B. JointPoint (连接点)
     连接点就是“程序执行过程中的某一行为”。比如car.Run()这个方法执行了,或者执行过程中抛出了异常,或者执行后返回了一个值,这三种行为都是叫做连接点。这个“点”不仅仅是点,而是一种行为的发生。

C.Advice (通知)
     Advice就是“切面对于某个连接点所产生的动作”。这个就更抽象了,啥叫通知呢?意思就是说当切面检测到连接点出现后,切面就得到一个通知。接着上面的例子:Monitor发现对象Car执行的函数Run(),那么Monitor就会得到一个通知,说这个Run行为发生了。个人觉得这个概念比较虚。

D.Pointcut(切点)
     现在有了监视的类(切面),知道了被监视对象的动作(连接点),还能收到行为发生的通知(Advice),剩下的就是监控类该关注那些动作,切面知道Car 在Run()了,Run() 方法就是需要关注的切点。

E.Target (目标)
     说了一通,忘了Car的角色了,Car就是目标。官方说法“被一个或者多个切面所通知的对象”。

F.Advice通知
     刚才说了一个概念:Advice。这个通知的类型有很多,什么时候产生这个通知呢?函数执行前?执行后?返回后?抛出异常后?都可以。Spring AOP定义了很多通知的类型。
前置通知:
     在函数执行前需要执行的步骤。比如Car.Run()执行前,需要检查安全带有没有扣好。那么ConfirmTheSeatBelt()就是前置通知。
后通知
     在函数执行后需要执行的步骤,比如Car.Run()执行后,车的里程表开始计算。此时milesCount()就是后置通知,注意:后置通知是只要这个函数执行过,它就会执行,即使这      个函数抛出异常了也会执行,那么问题来了,抛出异常了怎么办呢?好问题。。
抛出异常后通知:
     函数执行过程中出现了异常,比如做除法,除了一个0,那么这是个异常,出了异常了,就会触发“抛出异常后通知”,针对异常就可以做点什么了。那要是执行完,没有异常,需      要有返回值呢?
返回后通知:
    在函数执行完,返回了值,需要做处理。这个时候就需要对返回值做处理了。就是返回后通知要做的。

    好了,现在各种情况都考虑了,不过觉得比较麻烦。想在函数的执行前后都进行处理怎么办呢?环绕通知。

    这么多概念怎么玩起来呢?看栗子。。

    

二、Spring  AOP 示例

这个例子中,要做这样一件事:
  1、将文本里的内容读出来,将文本中所有的“HOT”子串都替换为 “Not Necessary”,然后将更改后的内容
写到新的文件中。
  2、建立一个切面,监控上述类的对象的行为,给每一个步骤打印日志。

首先,定义一个借口,规范文本操作的步骤:

1 public interface handleText {
2     public  String    readFile(String filePath);
3     public  String    processContent(String Content);
4     public  void      writeFile(String fileContent, String FilePath);
5 }

然后,实现定义一个类实现接口的具体方法:

 1 public  class TextHandler implements handleText {
 2     
 3     public  String readFile(String filePath)
 4     {
 5         String line = "";
 6         
 7         StringBuffer strBuffer = new StringBuffer();
 8         try{
 9                 BufferedReader  bf = new BufferedReader(new FileReader(new File(filePath)));
10                 while((line = bf.readLine())!=null)
11                 {
12                     strBuffer.append(line).append("#");
13                     System.out.println(line);
14                 }
15                 bf.close();
16         }catch (FileNotFoundException e)
17         {
18             System.out.println("File not Found");
19             return null;
20         }catch (IOException e)
21         {
22             System.out.println("IOExecption ");
23             return null;
24         }
25         System.out.println("FileContent =="+strBuffer.toString());
26         return strBuffer.toString();
27     }
28     
29     public String processContent(String targetStr)
30     {
31         String replaceMentStr = "No Necessary";
32         String rtnString = targetStr.replaceAll("HOT", replaceMentStr);
33         return rtnString;
34     }
35     
36     public void writeFile(String fileContent, String filePath)
37     {
38         try{
39                 BufferedWriter bufWriter = new BufferedWriter(new FileWriter(filePath));
40             
41                 String[] lines = fileContent.split("#");
42                 int lineCount = lines.length;
43                 int i = 0;
44                 while(i<lineCount)
45                 {
46                     bufWriter.write(lines[i]);
47                     bufWriter.newLine();
48                     i++;
49                 }
50                 bufWriter.flush();
51                 bufWriter.close();
52                 System.out.println("Write OK ");
53         }
54         catch(IOException e)
55         {
56             // Nothing To Do 
57         }
58     }
59 }

     到现在,我们的主逻辑已经实现了。现在定义一个切面,在读取文本前打印日志,在将处理后的文本写入指定文件后,输出写入完毕的日志。

     Spring AOP的实现方式有四种,我在实际开发中常用的是两种:AspectJ注解的方式,POJO的<AOP:Config>方式,AspectJ需要单独一篇文章介绍,这里使用POJO的方式实现。

     首先,定义一个类,就是个POJO。在这个类里面实现我们需要的日志打印操作。

 1 public class logForTextHandler {
 2     
 3     static Logger logger;
 4     static {
 5         logger = Logger.getLogger(TextHandler.class);
 6     }
 7     public void befoeReadFile()
 8     {
 9         logger.info("Now Ready To Read Text From File");
10     }
11     public void afterProcessContent()
12     {
13         
14         logger.info("Process Is Done and the Result is +" );
15     }
16     
17     public void aroundWriteFile(ProceedingJoinPoint jointPoint)
18     {
19         logger.info("Now Write The Processed Content Into File");
20         try {
21             jointPoint.proceed();
22             logger.info("Sucess Writing The File ");
23         } catch (Throwable e) {
24             System.out.println("Error When Writing File");
25             e.printStackTrace();
26         }
27     }
28 
29 }

剩下的事情就是在XML文件中进行配置了:

           <bean id = "logMonitor" class = "  com.springAOP.FocusOnDIAOP.ForBlog.Aspect.logForTextHandler" />
           <bean id = "handleFile" class = "  com.springAOP.FocusOnDIAOP.ForBlog.TextHandler" />
           
           <!-- 为博客的代码添加切面 -->
           <aop:config>
                      <aop:aspect ref = "logMonitor" >
                      <aop:pointcut id = "before-handle" expression ="execution (* com.springAOP.FocusOnDIAOP.ForBlog.TextHandler.readFile(..))" />
                      <aop:before pointcut-ref = "before-handle" method = "befoeReadFile" />
                      </aop:aspect>
           </aop:config>

           <aop:config>
                      <aop:aspect ref = "logMonitor" >
                                <aop:pointcut id = "around-handle" expression ="execution (* com.springAOP.FocusOnDIAOP.ForBlog.TextHandler.writeFile(..))" />
                              <aop:around pointcut-ref = "around-handle" method = "aroundWriteFile" />
                      </aop:aspect>
           </aop:config>

写个测试类:

public class TestCase {

    public static void main(String[] args) {
        
        String pathFrom = "C:/Users/shaoping.gsp/Desktop/fileForm.txt";
        String pathTo = "C:/Users/shaoping.gsp/Desktop/fileTo.txt";
        
        ApplicationContext appCtx = new ClassPathXmlApplicationContext("beans.xml");
        handleText handler = (handleText) appCtx.getBean("handleFile");
        
        String targetStr = handler.readFile(pathFrom);
        String fileContent = handler.processContent(targetStr);
        handler.writeFile(fileContent, pathTo);
    }
}

这样就OK了。

这里面用到了 Log4j,把log4j的配置文件也附上:

# Configuration For Root Logger : Console  FileLog
log4j.rootLogger=info, ServerDailyRollingFile, stdout 

#Configuration For File Log 
log4j.appender.ServerDailyRollingFile=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.ServerDailyRollingFile.DatePattern='.'yyyy-MM-dd 
log4j.appender.ServerDailyRollingFile.File=C:/Users/shaoping.gsp/Desktop/RunTime.log
log4j.appender.ServerDailyRollingFile.layout=org.apache.log4j.PatternLayout 
log4j.appender.ServerDailyRollingFile.layout.ConversionPattern=%d - %m%n 
log4j.appender.ServerDailyRollingFile.Append=true

#ConFiguration For Console Output
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.app

还有pom.xml文件中的依赖。

<dependencies>
        <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>3.8.1</version>
              <scope>test</scope>
        </dependency>
             <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-core</artifactId>  
            <version>3.2.8.RELEASE</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-context</artifactId>  
            <version>3.2.8.RELEASE</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-aop</artifactId>  
            <version>3.2.8.RELEASE</version>  
        </dependency>  
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
               <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>
       <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjrt</artifactId>
           <version>1.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        
       <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version> 1.2.9</version>
        </dependency> 
  </dependencies>

AOP,玩起来就这么简单,复杂的永远是:在合适的场景中灵活的应用,而不是技术本身。

更复杂的是知道AOP的运行机制,这个东西,以后慢慢写。

原文地址:https://www.cnblogs.com/tju-gsp/p/4622021.html