Spring学习(二)——Spring中的AOP的初步理解

【前面的话】

     Spring对我太重要了,做个关于web相关的项目都要使用Spring,每次去看Spring相关的知识,总是感觉一知半解,没有很好的系统去学习一下,现在抽点时间学习一下Spring。不知道为什么对Spring有一种莫名的喜欢感,也许是因为他的名字,有一种希望的感觉。

     Spring学习过程中的总结和心得,本文介绍了初次学习Spring的时候,对于AOP的理解,希望后面有时间可以进行再进一步的学习,请选择性阅读。

本文由牲口TT在博客园首次发表,转载请保持文章的完整性并注明:
作者:牲口TT
链接:http://www.cnblogs.com/xt0810/p/3596347.html

【相关文章】

Spring学习(一)——Spring中的依赖注入简介

AOP介绍】

     面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计、观点导向编程)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译作方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。

     侧面的概念源于对面向对象的程序设计的改进,但并不只限于此,它还可以用来改进传统的函数。与侧面相关的编程概念还包括元对象协议、主题(subject)、混入(mixin)和委托。

【面向切面】

一、     AOP要干什么?

      依赖注入让相互协作的软件组件保持松散耦合,而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件。

      也就是说AOP把一些常用的服务进行模块化,并且用声明的方式将这些组件使用到其他业务组件中去。这样做的结果是:每一个业务组件只关心自己的业务逻辑,不用去了解一些常用服务组件。这样就保证了更高的内聚性。

二、     不使用AOP的麻烦?

     由于系统会有很多不同的组件,每一个组件负责一块特定功能,我们希望每一个组件只关心它的自身核心功能,但是在系统中,会有一些组件比如:日志模块,事务管理和安全模块等这些组件会比较频繁的融入到其他核心业务逻辑组件中去,这些常用的组件会分散到其他多个组件中,这样带来的麻烦是:

  1. 如果这些常用的服务组件发生变化,那么我们需要在多个其他组件中进行修改。
  2. 这样使得我们的组件代码因为插入了与自身核心业务无关的服务性组件变得混乱。

三、     使用AOP的好处?

      我的理解是:一个组件A,不关心其他常用的服务组件B,但是这个组件A使用组件B的时候,不是组件A自身去调用,而是通过配置等其他方式,比如Spring中可以通过xml配置文件。这样就使得A压根就不需要知道服务组件B是怎样的,爱存在不存在,爱怎么存在都与A无关。A只关心自己的业务逻辑,具体A使用B的时候,配置文件去做,与具体的A组件无关。

四、     情形一:不使用AOP思想的代码

      我们继续使用上一篇文章中Person开Car的例子来进行说明。我们现在增加一个Assistant的类,这个就是一个助理,需要干的事情就是在我开车之前进行登记,在我开车回来的时候进行接待。

     如果使用一般思想的编码,我们需要在Person类中调用Assistant类的两个方法,如下代码。但是这样做的问题是:

  • 管理Assistant是不是Person类的职责,Assistant应该做他自己的分内的事情,压根不需要Person命令他,Person走的时候他进行登记,和回来的时候进行入库,这就是他的本职工作,不需要Person提醒。
  • 同样,由于Person类需要使用Assistant,他就必须把Assistant注入到他的类中,这样就使得他的代码变得复杂了。并且如果有一个Person没有Assistant,那么我们应该怎么办?

1. Person.java

 1 public class Person {
 2     private Car car;
 3     private Assistant assistant;
 4     public Person(Car car,Assistant assistant){//构造器注入,传入的是car,也就是一个所有车型都必须实现的接口
 5         this.car =car;//这里可以响应奥迪,宝马等任何一种车的实现。
 6         this.assistant=assistant;
 7     }//这里Person类没有与任何特定类型的车发生耦合,对于Person来说,任何一种特定的车,只需要实现Car接口即可。具体是哪一种车型,对Person来说无关紧要。
 8     public void driver(){//从这里可以看出,如果没有使用AOP,也就是每个人都需要用一个助理,这样是不合理的。
 9         assistant.BeforeDepart();//Person类使用Assistant的方法
10         car.GuaDang();
11         car.CaiYouMen();
12         car.DaFangXiang();
13         assistant.AfterBack();//Person类使用Assistant的方法
14     }
15 }

2. Assistant.java

1 public class Assistant {
2     public void BeforeDepart(){
3         System.out.println("您好,你今天开走了奥迪,我已经做了登记");
4     }
5     public void AfterBack(){
6         System.out.println("您回来了!请把车交给我,我自行入库就可以了");
7     }
8 
9 }

3. Car.java

1 public interface Car {
2     public abstract void GuaDang();
3     public abstract void CaiYouMen();
4     public abstract void DaFangXiang();
5 }

4. AuDi.java

 1 public class AuDi implements Car {
 2     public void GuaDang(){
 3         System.out.println("我是奥迪车,我在进行挂档");
 4     }
 5     public void CaiYouMen(){
 6         System.out.println("我是奥迪车,我在进行踩油门");
 7     }
 8     public void DaFangXiang(){
 9         System.out.println("我是奥迪车,我在进行打方向盘");
10     }
11 }

5. MainTest.java

1 public class MainTest {
2      public static void main(String[] args){
3         AuDi audi=new AuDi();
4         Assistant assistant=new Assistant();
5         Person boy =new Person(audi, assistant);
6         boy.driver();
7     }
8 }

6. 运行结果

1 您好,你今天开走了奥迪,我已经做了登记
2 我是奥迪车,我在进行挂档
3 我是奥迪车,我在进行踩油门
4 我是奥迪车,我在进行打方向盘
5 您回来了!请把车交给我,我自行入库就可以了

五、     情形二:使用AOP思想的代码

     使用了AOP,Person类就不需要关心Assistant类,我们只需要声明Assistant需要做的事情,Person类就不在直接访问Assistant类的方法了。

     本篇文章只是在理解AOP的思想,具体在Spring中AOP是怎么做?使用什么方法,以后在进行学习。

1. Person.java

 1 public class Person {
 2     private Car car;
 3     public Person(){
 4         
 5     }
 6     public Person(Car car){//构造器注入,传入的是car,也就是一个所有车型都必须实现的接口
 7         this.car =car;//这里可以响应奥迪,宝马等任何一种车的实现。
 8     }//这里Person类没有与任何特定类型的车发生耦合,对于Person来说,任何一种特定的车,只需要实现Car接口即可。具体是哪一种车型,对Person来说无关紧要。
 9     public void driver(){
10         car.GuaDang();
11         car.CaiYouMen();
12         car.DaFangXiang();
13     }
14 }

2. Assistant.java

1 public class Assistant {
2     public void BeforeDepart(){
3         System.out.println("您好,你今天开走了奥迪,我已经做了登记");
4     }
5     public void AfterBack(){
6         System.out.println("您回来了!请把车交给我,我自行入库就可以了");
7     }
8 
9 }

3. Car.java

1 public interface Car {
2     public abstract void GuaDang();
3     public abstract void CaiYouMen();
4     public abstract void DaFangXiang();
5 }

4. AuDi.java

 1 public class AuDi implements Car {
 2     public void GuaDang(){
 3         System.out.println("我是奥迪车,我在进行挂档");
 4     }
 5     public void CaiYouMen(){
 6         System.out.println("我是奥迪车,我在进行踩油门");
 7     }
 8     public void DaFangXiang(){
 9         System.out.println("我是奥迪车,我在进行打方向盘");
10     }
11 }

5. cartest.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/context
 9         http://www.springframework.org/schema/context/spring-context-3.0.xsd
10         http://www.springframework.org/schema/aop
11         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    
12     <bean id="cartest" class="Person">
13         <constructor-arg ref="Car" />
14     </bean>
15     <bean id="Car" class="AuDi" />
16     <bean id="assistant" class="Assistant" />
17     <aop:config>
18       <aop:aspect ref="assistant">
19       <aop:pointcut id="ass" expression="execution(* driver(..))"/>
20       <aop:before pointcut-ref="ass" method="BeforeDepart"/>
21       <aop:after pointcut-ref="ass" method="AfterBack"/>
22       </aop:aspect>
23     </aop:config>
24 </beans>

6. MainTest.java

1 import org.springframework.context.ApplicationContext;
2 import org.springframework.context.support.ClassPathXmlApplicationContext;
3 public class MainTest {
4      public static void main(String[] args){
5          ApplicationContext context = new ClassPathXmlApplicationContext("cartest.xml");
6          Person boy =(Person) context.getBean("cartest");
7          boy.driver();
8     }
9 }

7. 运行结果

1 您好,你今天开走了奥迪,我已经做了登记
2 我是奥迪车,我在进行挂档
3 我是奥迪车,我在进行踩油门
4 我是奥迪车,我在进行打方向盘
5 您回来了!请把车交给我,我自行入库就可以了

【注意问题】

     在学习过程中,总是会遇到很多问题,而本文是自己学习过程中的总结,所以对于新手来说,遇到一个问题可能需要找半天的问题,所以下面写了一下新手可能会遇到的问题,以及需要注意的事项,选择性阅读

一、         需要导入的jar包:

  1. asm.jar
  2. cglib-2.1.3.jar
  3. com.springsource.org.aopalliance-1.0.0.jar
  4. com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
  5. commons-logging-1.0.4.jar
  6. org.springframework.aop-3.0.1.RELEASE-A.jar
  7. org.springframework.asm-3.0.1.RELEASE-A.jar
  8. org.springframework.beans-3.0.2.RELEASE.jar
  9. org.springframework.context-3.0.2.RELEASE.jar
  10. org.springframework.core-3.0.2.RELEASE.jar
  11. org.springframework.expression-3.0.1.RELEASE-A.jar

二、         代码结构如下图:

红线部分代码属于同一个包中。

三、         可能遇到的错误:

1. 错误:

Caused by: java.lang.ClassNotFoundException:org.aopalliance.aop.Advice

解决办法:

导入:com.springsource.org.aopalliance-1.0.0.jar

2. 错误:

Caused by: org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

解决办法:

导入:cglib-2.1.3.jar

3. 错误:

Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given

解决办法:

在Person类中加入一个默认构造函数,关于这个错误,下面的文章中有较为深入的描述。http://netfork.iteye.com/blog/286215

【参考资料】

1.       《Spring in action》 Craig Walls著 耿渊 张卫滨译

【后面的话】

      有时感觉比较惶恐,有时有感觉比较安全,惶恐的时候就想着要好好学习。

——TT

原文地址:https://www.cnblogs.com/xt0810/p/3596347.html