浅析spring中的AOP(面向切面编程)
1、AOP 是什么?
都知道面向对象无非是封装、继承、多态,根据某一个类,实例化一个对象,然后操作这一个对象。
AOP 按我自己的理解就是,面向多个对象,或是面向N个对象。
比如我们在service中,可能要对所有的DML操作添加一个transaction,如果说service很少的话,我们可以直接在service中添加一个transaction,那么如果再开发中,有几十个甚至几百个service都需要给他们添加一个transaction,难道我们就写上几十遍、上百遍的transaction。当然不是,在spring中,提供了一个AOP,面向切面的编程,说白了就是面向N个对象的编程,就是将这所有的service中的共同部分提取出来,比如将所有的transaction提取出来,写成一个通知,然后织入(注意别写错字),织入action的代理对象之中,这个actionproxy会先执行织入的通知,然后执行其他。
2、首先明白几个概念(概念可以先略过,还是先后面代码,再来明白吧),
代理模式:代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
Aspect(切面):是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容—-它的功能、在何时和何地完成其功能
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些joinpoint进行拦截的定义.
通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象):代理的目标对象
Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
spring在运行期创建代理,不需要特殊的编译器.
spring有两种代理方式:
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
使用该方式时需要注意:
1.**对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。**
对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,
这种方式应该是备用方案。
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要
被通知的方法都被复写,将通知织入。final方法是不允许重写的。
spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了
封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的
结果。
3、概念可以先略过。下面写个简单的,来描述下(前置通知)。
比如实现一个功能,就是在所有请求到达action之前为这个请求添加一个日志(先导入spring的必备的jar包),下面的5段代码放在同一个包中即可运行,直接用java application运行就行
* com.springsource.net.sf.cglib-2.2.0.jar
cglib代理
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
* org.springframework.aop-3.0.2.RELEASE.jar
spring的面向切面编程,提供AOP(面向切面编程)实现
* org.springframework.aspects-3.0.2.RELEASE.jar
spring提供对AspectJ框架的整合
定义接口
TestInterface1
package com.niit.spring.aop;
/**
* @author Emine_wang
*/
public interface TestInterface1 {
public void sayHello();
public void sayh();
}
实现:
TestInterface1Impl
package com.niit.spring.aop;
/**
* @author Emine_wang
*/
public class TestInterface1Impl implements TestInterface1 {
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("Hello Emine");
}
public void sayh(){
System.out.println("Hello Wang");
}
}
通知(就是概念中的Advice):
MyMethodBeforeAdvice.java
别忘了继承org.springframework.aop.MethodBeforeAdvice;需要导包
package com.niit.spring.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* @author Emine_wang
* 编写通知,advice
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object object)
throws Throwable {
/**
* method指表示被调用的方法
* args给这个method方法传递的参数
* object目标对象
* 其实是利用反射机制得到方法中的名字、参数等信息
*
*/
System.out.println("记录日志"+method.getName());
}
}
配值spring的配置文件beans.xml
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 配置目标对象(被代理对象) -->
<bean id="TestInterface1Impl" class="com.niit.spring.aop.TestInterface1Impl">
</bean>
<!-- 配置前置通知 -->
<bean id="MyMethodBeforeAdvice" class="com.niit.spring.aop.MyMethodBeforeAdvice"></bean>
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置代理的接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.niit.spring.aop.TestInterface1</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于把前置通知 MyMethodBeforeAdvice和代理对象关联起来,我们也可以把通知看成是拦截器
struts2核心拦截器
-->
<value>MyMethodBeforeAdvice</value>
</property>
<!-- 配置被代理对象,没有被代理对象,那么context.getBean("proxyFactoryBean")为空 -->
<property name="target" ref="TestInterface1Impl"></property>
</bean>
</beans>
测试:
package com.niit.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.niit.spring.bean.User;
public class TestAop {
/**
* @param Emine_Wang
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context = new ClassPathXmlApplicationContext("com/niit/spring/aop/beans.xml");//加载beans配置文件
// TestInterface1Impl testInterface1Impl = (TestInterface1Impl) context.getBean("TestInterface1Impl");//这样写相当于没有用到代理对象,当然不会执行通知了
// testInterface1Impl.sayHello();
TestInterface1 testInterface1 = (TestInterface1)context.getBean("proxyFactoryBean");//反射生成代理对象
// System.out.println(testInterface1.getClass());
testInterface1.sayHello();
testInterface1.sayh();
}
}
结果:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
记录日志sayHello
Hello Emine
记录日志sayh
Hello Wang
我们会发现,我并没有在TestInterface1Impl中的sayHello()和sayh()方法中打印”记录日志“,却发现这里打印了。说明,通知advice已经被执行,是由代理对象帮我们执行。
解释的有点直白。。。。
还没有评论,来说两句吧...