Spring 框架基础(04):AOP切面编程概念,几种实现方式演示 4年前

本文源码:GitHub·点这里 || GitEE·点这里

一、AOP基础简介

1、切面编程简介

2、AOP术语

(1)、通知类型:Advice

前置通知[Before]:目标方法被调用之前;
返回通知[After-returning]:目标方法执行成功之后;
异常通知[After-throwing]:在目标方法抛出异常之后; 
后置通知[After]:目标方法完成之后;
环绕通知[Around]:在目标方法执行前后环绕通知;

(2)、连接点:JoinPoint

程序执行的某一个特定位置,如类初始前后,方法的运行前后。

(3)、切点:Pointcut

连接点是指那些在指定策略下可能被拦截到的方法。

(4)、切面:Aspect

切面由切点和通知的结合。

(5)、引入:Introduction

特殊的增强,为类添加一些属性和方法。

(6)、织入:Weaving

将增强添加到目标类的具体连接点上的过程。编译期织入,这要求使用特殊编译器;类装载期织入,这要求使用特殊的类加载器;动态代理织入,在运行期为目标类添加增强生成子类的方式,Spring采用的是动态代理织入,而AspectJ采用编译期织入和类装载期织入。

(7)、代理:Proxy

类被AOP织入后生成一个结果类,它是融合了原类和增强逻辑的代理类。

二、AOP编程实现方式

案例基于如下类进行:

public class Book {
    private String bookName ;
    private String author ;
}
public interface BookService {
    void addBook (Book book) ;
}
public class BookServiceImpl implements BookService {
    @Override
    public void addBook(Book book) {
        System.out.println(book.getBookName());
        System.out.println(book.getAuthor());
    }
}

1、JDK动态代理

public class BookAopProxyFactory {
    public static BookService createService() {
        // 目标类
        final BookService bookService = new BookServiceImpl() ;
        // 切面类
        final BookAspect bookAspect = new BookAspect();
        /*
         * 代理类:将目标类(切入点)和 切面类(通知) 结合
         */
        BookService proxyBookService = (BookService) Proxy.newProxyInstance(
                BookAopProxyFactory.class.getClassLoader(),
                bookService.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method,
                                         Object[] args) throws Throwable {
                        // 前执行
                        bookAspect.before();
                        // 执行目标类的方法
                        Object obj = method.invoke(bookService, args);
                        // 后执行
                        bookAspect.after();
                        return obj;
                    }
                });
        return proxyBookService ;
    }
}

2、CgLib字节码增强

采用字节码增强框架cglib,在运行时创建目标类的子类,从而对目标类进行增强。

public class BookAopCgLibFactory {
    public static BookService createService() {
        // 目标类
        final BookService bookService = new BookServiceImpl() ;
        // 切面类
        final BookAspect bookAspect = new BookAspect();
        // 核心代理类
        Enhancer enhancer = new Enhancer();
        // 确定父类
        enhancer.setSuperclass(bookService.getClass());
        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method,
                                    Object[] args,
                                    MethodProxy methodProxy) throws Throwable {
                bookAspect.before();
                Object obj = method.invoke(bookService, args);
                bookAspect.after();
                return obj;
            }
        });
        BookServiceImpl proxyService = (BookServiceImpl) enhancer.create();
        return proxyService ;
    }
}

3、Spring半自动代理

spring 创建代理对象,从spring容器中手动的获取代理对象。

  • 配置文件

  • 切面类

    public class BookAopSpringHalf implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("Method Before ..."); Object obj = methodInvocation.proceed(); System.out.println("Method After ..."); return obj; } }

4、Spring全自动代理

从spring容器获得目标类,如果配置Aop,spring将自动生成代理。

  • 配置文件

5、综合测试

@Test
public void test1 (){
    BookService bookService = BookAopProxyFactory.createService() ;
    Book book = new Book() ;
    book.setBookName("Spring实战");
    book.setAuthor("Craig Walls");
    bookService.addBook(book);
}
@Test
public void test2 (){
    BookService bookService = BookAopCgLibFactory.createService() ;
    Book book = new Book() ;
    book.setBookName("MySQL");
    book.setAuthor("Baron");
    bookService.addBook(book);
}
@Test
public void test3 (){
    String xmlPath = "spring-aop-half.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
    BookService bookService = (BookService) context.getBean("proxyFactory");
    Book book = new Book() ;
    book.setBookName("红楼梦");
    book.setAuthor("曹雪芹");
    bookService.addBook(book);
}
@Test
public void test4 (){
    String xmlPath = "spring-aop-all.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
    BookService bookService = (BookService) context.getBean("bookService");
    Book book = new Book() ;
    book.setBookName("西游记");
    book.setAuthor("吴承恩");
    bookService.addBook(book);
}

三、AspectJ切面编程

1、基础简介

2、XML配置方式

  • 切面类

    public class BookAopAspectJ { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知:" + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕通知前"); Object obj = joinPoint.proceed(); System.out.println("环绕通知前后"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常通知 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最终通知"); } }

  • 配置文件

  • 测试方法

    @Test public void test1 (){ String xmlPath = "spring-aop-aspectj-01.xml"; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); BookService bookService = (BookService) context.getBean("bookService"); Book book = new Book() ; book.setBookName("三国演义"); book.setAuthor("罗贯中"); bookService.addBook(book); }

3、注解扫描方式

  • 配置文件

    <context:component-scan base-package="com.spring.mvc.service.impl" />

    <aop:aspectj-autoproxy />

  • 注解切面类

    @Component @Aspect public class AuthorAopAspectJ { @Pointcut("execution(* com.spring.mvc.service.impl.AuthorServiceImpl.(..))") private void myPointCut(){ } @Before("execution( com.spring.mvc.service.impl.AuthorServiceImpl.(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } @AfterReturning(value="myPointCut()" ,returning="ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知:" + joinPoint.getSignature().getName() + " , -->" + ret); } @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕通知前"); Object obj = joinPoint.proceed(); System.out.println("环绕通知前后"); return obj; } @AfterThrowing( value="execution( com.spring.mvc.service.impl.AuthorServiceImpl.*(..))", throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常通知 : " + e.getMessage()); } @After("myPointCut()") public void myAfter(JoinPoint joinPoint){ System.out.println("最终通知"); } }

  • 测试方法

    @Test public void test2 (){ String xmlPath = "spring-aop-aspectj-02.xml"; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); AuthorService authorService = (AuthorService) context.getBean("authorService"); System.out.println("作者:"+authorService.getAuthor()); }

四、源代码地址

GitHub·地址
https://github.com/cicadasmile/spring-mvc-parent
GitEE·地址
https://gitee.com/cicadasmile/spring-mvc-parent

奥加
能诚实地承认穷,诚恳地表达对于钱的兴趣,就是穷者的尊严。
5
发布数
1
关注者
1109
累计阅读

热门教程文档

Kotlin
68小节
Linux
51小节
Javascript
24小节
Redis
14小节
CSS
33小节
广告