面向切面编程(AOP)

面向切面编程

 
Aspect Oriented Programming(AOP),面向切面 编程,是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了,举个例子,我们现在提供一个查询学生信息的服务,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现 类 (StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
中文名
面向切面编程
外文名
AOP(Aspect Oriented Programming)
语    言
java

基本信息

编辑
Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低 耦合性的隔离效果。  [1]   比如我们最常见的就是日志记录了,举个例子,我们现在提供一个服务查询学生信息的,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现类(StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为 紧耦合,违反了 面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了 耦合性,可以就某个功能进行编程。

代码分析

编辑
我们直接从代码入手吧,要实现以上的目标,我们可以使用一个 动态代理类(Proxy),通过拦截一个对象的行为并添加我们需要的功能来完成。Java中的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案,但是该方案针对的对象要实现某些接口;如果针对的目的是类的话, cglib为我们提供了另外一个实现方案。等下会说明两者的区别。

接口实现方案

1)首先编写我们的业务接口(StudentInfoService.java):
1
2
3
public  interface  StudentInfoService{
void  findInfo(String studentName);
}
及其实现类(StudentInfoServiceImpl.java):
1
2
3
4
5
public  class  StudentInfoServiceImpl  implements  StudentInfoService{
public  void  findInfo(String name){
System.out.println( "你目前输入的名字是:" +name);
}
}
2)现在我们需要一个日志功能,在findInfo行为之前执行并记录其行为,那么我们就首先要拦截该行为。在实际执行的过程中用一个代理类来替我们完成。Java中为我们提供了实现 动态代理类的方案:
1'处理拦截目的的类(MyHandler.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import  org.apache.log4j.Logger;
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Proxy;
import  java.lang.reflect.Method;
public  class  MyHandler  implements  InvocationHandler{
private  Object proxyObj;
private  static  Logger log=Logger.getLogger(MyHandler. class );
public  Object bind(Object obj){
this .proxyObj=obj;
return  Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this );
}
public  Object invoke(Object proxy,Method method,Object[] args)  throws  Throwable{
Object result= null ;
try {
//请在这里插入代码,在方法前调用
( "调用log日志方法" +method.getName());
result=method.invoke(proxyObj,args);  //原方法
//请在这里插入代码,方法后调用
} catch (Exception e){
e.printStackTrace();
}
return  result;
}
}
2'我们实现一个工厂,为了方便我们使用该拦截类(AOPFactory.java):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public  class  AOPFactory{
private  static  Object getClassInstance(String clzName){
Object obj= null ;
try {
Class cls=Class.forName(clzName);
obj=(Object)cls.newInstance();
} catch (ClassNotFoundException cnfe){
System.out.println( "ClassNotFoundException:" +cnfe.getMessage());
} catch (Exception e){
e.printStackTrace();
}
return  obj;
}
public  static  Object getAOPProxyedObject(String clzName){
Object proxy= null ;
MyHandler handler= new  MyHandler();
Object obj=getClassInstance(clzName);
if (obj!= null ) {
proxy=handler.bind(obj);
} else {
System.out.println( "Can't get the proxyobj" );
//throw
}
return  proxy;
}
}
3)基本的拦截与其工厂我们都实现了,现在测试(ClientTest.java):
1
2
3
4
5
6
public  class  ClientTest{
public  static  void  main(String[] args){
StudentInfoService studentInfo= (StudentInfoService)AOPFactory.getAOPProxyedObject( "StudentInfoServiceImpl" );
studentInfo.findInfo( "阿飞" );
}
}
输出结果(看你的log4j设置):
调用log日志方法findInfo
你目前输入的名字是:阿飞
这样我们需要的效果就出来了,业务处理自己在进行,但是我们实现了日志功能,而业务处理(StudentInfoService)根本不知道存在该行为的。但是Java中提供的 动态代理类的实现是针对实现了某些接口的类,如果没有实现接口的话,不能创建代理类,看以上部分:
1
return  Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this );
看到了没有?obj.getClass().getInterfaces()要求实现了某些接口。以下提供哪些没有实现接口的实现方案:

子类实现方案

首先,请上网下CGLib的包。设置好classpath路径,CGLib与java标准库提供的实现方案不同, cglib主要是基于实现类(如StudentInfoServiceImpl.java)扩展一个子类来实现。与Dynamic Proxy中的Proxy和InvocationHandler相对应,net.sf.cglib.proxy.Enhancer和MethodInterceptor在CGLib中负责完成代理对象创建和方法截获处理,产生的是目标类的子类而不是通过接口来实现方法拦截的,Enhancer主要是用于构造动态代理子类来实现拦截,MethodInterceptor(扩展了Callback接口)主要用于实现around advice(AOP中的概念):
1)我们的业务处理(StudentInfoServiceImpl.java):
1
2
3
4
5
public  class  StudentInfoServiceImpl{
public  void  findInfo(String name){
System.out.println( "你目前输入的名字是:" +name);
}
}
2)实行一个工具来处理日志功能(AOPInstrumenter.java):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import  net.sf.cglib.proxy.MethodInterceptor;
import  net.sf.cglib.proxy.Enhancer;
import  net.sf.cglib.proxy.MethodProxy;
import  java.lang.reflect.Method;
import  org.apache.log4j.Logger;
public  class  AOPInstrumenter  implements  MethodInterceptor{
private  Logger log=Logger.getLogger(AOPInstrumenter. class );
private  Enhancer enhancer= new  Enhancer();
public  Object getInstrumentedClass(Class clz){
enhancer.setSuperclass(clz);
enhancer.setCallback( this );
return  enhancer.create();
}
public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  throws  Throwable{
( "调用日志方法" +method.getName());
Object result=proxy.invokeSuper(o,args);
return  result;
}
}
3)我们来测试一下(AOPTest.java):
1
2
3
4
5
6
7
public  class  AOPTest{
public  static  void  main(String[] args){
AOPInstrumenter instrumenter= new  AOPInstrumenter();
StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl. class );
studentInfo.findInfo( "阿飞" );
}
}
输出结果与以上相同。
CGLib中为实现以上目的,主要提供的类
1)Enhancer:setCallback(Callback) ,setSuperclass(Class) ,create()返回动态子类Object
2)MethodInterceptor必须实现的接口:intercept(Object,Method,Object[],MethodProxy)返回的是原方法调用的结果。和Proxy原理一样。

AOP基本概念

1)aspect(切面):实现了cross-cutting功能,是针对切面的模块。最常见的是logging模块,这样,程序按功能被分为好几层,如果按传统的继承的话,商业模型继承日志模块的话根本没有什么意义,而通过创建一个logging切面就可以使用AOP来实现相同的功能了。
2)jointpoint(连接点):连接点是切面插入应用程序的地方,该点能被方法调用,而且也会被抛出意外。连接点是应用程序提供给切面插入的地方,可以添加新的方法。比如以上我们的切点可以认为是findInfo(String)方法。
3)advice(处理逻辑):advice是我们切面功能的实现,它通知程序新的行为。如在logging里,logging advice包括logging的实现代码,比如像写 日志到一个文件中。advice在jointpoint处插入到应用程序中。以上我们在MyHandler.java中实现了advice的功能
4)pointcut(切点):pointcut可以控制你把哪些advice应用于jointpoint上去,通常你使用pointcuts通过 正则表达式来把明显的名字和模式进行匹配应用。决定了那个jointpoint会获得通知。
5)introduction:允许添加新的方法和属性到类中。
6)target(目标类):是指那些将使用advice的类,一般是指独立的那些商务模型。比如以上的StudentInfoServiceImpl.
7)proxy(代理类):使用了proxy的模式。是指应用了advice的对象,看起来和target对象很相似。
8)weaving(插入):是指应用aspects到一个target对象创建proxy对象的过程:complie time,classload time,runtime  [2]  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值