在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,并在不修改原来代码的基础上加入了功能,经典应用:事务管理、性能监视、安全检查、缓存 、日志等,简而言之,
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
AOP底层就是生成动态代理,如果被代理的有接口,就使用jdk动态代理生成代理对象,反之,就用cglib字节码增强生成代理对象。
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7. Aspect(切面): 是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成一个特殊的
编程
需要编程的类: 目标类(被代理的类),Advise类(加强类),工厂类(生成代理)
UserServiceImpl类(target)
/** * 代理类 * @author Kuang * */ public class UserServiceImpl implements UserService {
@Override public void addUser() { System.out.println("addUser");
}
@Override public void remvoeUser() { System.out.println("remvoeUser"); }
} |
MyAspect类(advise)
public class MyAspect {
public void before(){ System.out.println("前"); } public void after(){ System.out.println("后"); } } |
工厂类
public class MyFactory { //生成代理对象 public static UserService createService(){ //目标类 UserService userService = new UserServiceImpl(); //增强类 MyAspect myAspect = new MyAspect(); /* * 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面 * Proxy.newProxyInstance * 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。 * 一般情况:当前类.class.getClassLoader(); * 目标类实例.getClass().get... * 参数2:Class[] interfaces 代理类需要实现的所有接口 * 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驱动 --> DriverManager 获得接口 Connection * 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部 * 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke * 参数31:Object proxy :代理对象 * 参数32:Method method : 代理对象当前执行的方法的描述对象(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) * 参数33:Object[] args :方法实际参数 * */ UserService proxyService = (UserService) Proxy.newProxyInstance( MyFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { myAspect.before(); System.out.println(method.getName()); //可用于过滤加强 method.invoke(userService, args); myAspect.after(); return proxy; } }); return proxyService; } } |
测试结果:
CGlib字节码加强使用于目标类没有接口的情况,需要目标类,加强类(advise),工厂类(产生代理)
准备
添加jar包:cglib-2.2.jar 和 sm-3.3.jar
值得一提的是spring-core..jar已经包含了以上两个jar包。
编程
目标类(UserServiceImpl)
/** * 被代理类(没有接口) * @author Kuang * */ public class UserServiceImpl{
public void addUser() { System.out.println("addUser");
}
public void remvoeUser() { System.out.println("remvoeUser"); }
} |
增强类(advice)
public class MyAspect {
public void before(){ System.out.println("前"); } public void after(){ System.out.println("后"); } } |
工厂类
** * CGlib字节码加强 * @author Kuang * */
public class MyFactory { //生成代理对象 public static UserServiceImpl createService(){ //目标类 UserServiceImpl userService = new UserServiceImpl(); //增强类 MyAspect myAspect = new MyAspect(); //CGlib加强主类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(userService.getClass()); //与jdk动态代理的InvocationHandler的invoke方法类似 enhancer.setCallback(new MethodInterceptor() {
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { myAspect.before(); Object obj = method.invoke(userService, args); myAspect.after(); return obj; } }); UserServiceImpl proxyService = (UserServiceImpl) enhancer.create(); return proxyService; } } |
测试
AOP联盟为通知Advice定义了org.aopalliance.aop.AdviceSpring按照通知Advice在目标类方法的连接点位置,可以分为5类
(1)前置通知 org.springframework.aop.MethodBeforeAdvice在目标方法执行前实施增强
(2)后置通知 org.springframework.aop.AfterReturningAdvice在目标方法执行后实施增强
(3)环绕通知 org.aopalliance.intercept.MethodInterceptor在目标方法执行前后实施增强
(4)异常抛出通知 org.springframework.aop.ThrowsAdvice在方法抛出异常后实施增强
(5)引介通知 org.springframework.aop.IntroductionInterceptor在目标类中添加一些新的方法和属性(一般没有用)
介绍AOP联盟的目的:替代增强类(advice)
例子:
Try{ 前置通知 目标方法 后置通知 }catch(){ 异常抛出通知 } |
准备jar包,分别为4+1 spring基本jar包, Aop联盟接口包,Aop联盟实现包
目标类接口 UserService
public interface UserService { void addUser(); void remvoeUser(); } |
目标类实现 UserServiceImpl
/** * 被代理类(目标类) * @author Kuang * */ public class UserServiceImpl implements UserService {
@Override public void addUser() { System.out.println("addUser");
}
@Override public void remvoeUser() { System.out.println("remvoeUser"); } } |
切面类(增强类)MyAspect
/** * 增强类,实现环绕通知接口MethodInterceptor * @author Kuang * */ public class MyAspect implements MethodInterceptor{
@Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("前方法"); Object object = mi.proceed(); //执行目标方法 System.out.println("后方法"); return object; } } |
xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="userServiceId" class="com.baidu.aop.banauto.UserServiceImpl">bean>
<bean id="myAspectId" class="com.baidu.aop.banauto.MyAspect">bean>
<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.baidu.aop.banauto.UserService">property> <property name="target" ref="userServiceId">property> <property name="interceptorNames" value="myAspectId">property> bean> beans>
|
测试类:
public class Demo { @Test public void test(){ String xmlPath = "com/baidu/aop/banauto/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
userService.addUser();
userService.remvoeUser(); } } |
添加jar包,加入Aspect表达式的jar包(依赖包下),见下图:
实现与上一节一致
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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceId" class="com.baidu.aop.auto.UserServiceImpl">bean>
<bean id="myAspectId" class="com.baidu.aop.auto.MyAspect">bean>
<aop:config> <aop:pointcut expression="execution(* com.baidu.aop.auto.*.*(..))" id="myPointCutId"/> <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCutId"/> aop:config> beans>
|
测试类:
测试结果: