AOP实现的三种方式
说说为什么要使用AOP编程:
AOP(面向切面编程)是OOP(面向对象编程)发展而来,在OOP编程中,每一个对象都有自己特定的功能,但是如果我们需要引入公共的功能时候,那么如果继续使用OOP编程思想的话,就会导致大量代码重用,而不利于模块的重用。而AOP编程则是将一些公共业务封装起来,为每一个需要使用这些公共业务的对象提供相应业务(通过代理实现)。一般将公共业务封装起来的类就是切面类,也就是我们切面编程所说的切面。
下面我们给对象添加一个公共业务:日志输出。
即在方法执行前后都在控制台输出一句话。
通过这个案例,简单介绍下AOP实现的三种方式。
先给出一个测试类代码:
接口类:
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
实现类:
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户"); }
public void update() {
System.out.println("更新了一个用户");
}
public void select() {
System.out.println("查询了一个用户");
}
}
AOP实现的三种方式:
1、使用Spring API原生接口
方法执行后输出日志:
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("Spring API接口实现AOP,add方法执行后");
}
}
方法执行前输出日志:
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("Spring API接口实现AOP,add方法执行前");
}
}
xml配置文件:
<bean id="userService" class="com.zm.service.UserServiceImpl"/>
<bean id="log" class="com.zm.log.Log"/>
<bean id="AfterLog" class="com.zm.log.AfterLog"/>
<!--方式一:使用Spring API原生接口-->
<!--配置aop,添加此配置之后,开启动态代理模式来自动获取代理类对象,且测试类获取对象要转换成接口类型,否则保错-->
<aop:config>
<!--切入点 execution(要执行的位置),第一个*代表所有返回类型,(..)代表方法中所有参数。这里将add方法设置为切入点,即当调用add方法,公共业务将被引入-->
<aop:pointcut id="pointcut" expression="execution(* com.zm.service.UserServiceImpl.add(..))"/>
<!--执行环绕增加! advice代表要增加的功能 pointcut代表要把功能加给谁
和动态代理对象会实现更多的功能原理一样-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="AfterLog" pointcut-ref="pointcut"/>
</aop:config>
2、自定义切面类 ,即切面自定义
自定义切面类,且封装公共业务(这里是日志功能)
public class DiyPointCut {
public void before(){
System.out.println("使用自定义类实现AOP,delete方法执行前");
}
public void after(){
System.out.println("使用自定义类实现AOP,delete方法执行后");
}
}
xml配置文件:
<bean id="diy" class="com.zm.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref引用类-->
<aop:aspect ref="diy">
<!--切入点 ,这里的切入点设置为delete方法,即调用delete方法时引入公共业务-->
<aop:pointcut id="point" expression="execution(* com.zm.service.UserServiceImpl.delete(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
3、通过注解实现:
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//注解实现Aop
@Aspect //标注这个类是切面类
public class AnnotationPointCut {
@Before("execution(* com.zm.service.UserServiceImpl.update(..))")
public void before(){
System.out.println("使用注解实现AOP,update方法执行前");
}
@After("execution(* com.zm.service.UserServiceImpl.update(..))")
public void after(){
System.out.println("使用注解实现AOP,update方法执行后");
}
}
xml配置:
<bean id="annotationPointCut" class="com.zm.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
来测试一下结果:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理类实现了userService接口
//通过userServiceImpl对象反射,动态代理生成对应代理类对象,并转换成UserService接口类型。
UserService userService = (UserService) context.getBean("userService");
userService.add();
userService.delete();
userService.update();
}
}
这里通过三种方式实现AOP编程,这三种方式分别选择了三个不同的方法作为切入点,因此三个方法被执行时,分别会引入三个不同公共业务(这里的公共业务是日志输出)
注意:
很多原理写在注释中了,如果有不清楚的可以多看一下注释,或者直接评论留言,欢迎一起讨论,欢迎纠错!!