Spring---AOP学习

       AOP本质:将切面类和目标类交给Spring去进行动态代理(后置处理器)(Around注解)。
       Spring容器只要有被切的目标类,则IOC容器中存放的就是对应类的代理对象,最后通过代理对象调用方法实现面向切面编程。
       如果目标类实现了接口,当通过类型取得Bean的时候,必须传入接口的类型,不能是本类的类型,才能获得代理对象。
       如果目标类没有实现接口,则Spring的cglib会为它创建代理对象。

       开启注解的AOP功能:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
       maven工程还需要通过pom.xml增加以下依赖导入jar包:

		<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>


1、AOP注解注入的术语

       目标类:交给后置处理器(本质应该也是动态代理)代理的类。
       切面类:委托类。
       通知方法:切面类中的各个方法。
       连接点:委托类中每个通知方法的每个位置。
       切入点:连接点中感兴趣的位置。


2、连接点的位置

       单个方法的连接点位置,见下面的灵魂制图:
Alt

3、AOP普通注释

       @Aspect:在该Bean已经加入IOC容器的前提下,指定其为切面类。

       以下注解的执行顺序按下面解释的顺序执行,After和AfterThrowing、AfterReturning顺序是反的。

       @Before(“execution(访问修饰符 返回类型 全类名.方法名(参数类型))”):代理方法执行前执行的方法。
       @After(“execution(访问修饰符 返回类型 全类名.方法名(参数类型))”):代理方法执行后执行的方法。
       @AfterThrowing(“execution(访问修饰符 返回类型 全类名.方法名(参数类型))”):代理方法执行抛出异常后执行的方法。
       @AfterReturning(“execution(访问修饰符 返回类型 全类名.方法名(参数类型))”):代理方法正常执行后执行的方法。括号里的execution用于指定切入点的位置。

       抽取可重用的切入点表达式:@Pointcut(括号里放需要重用的切入点表达式)。

       可以通过JoinPoint可以获得方法参数和方法名等等。通过指定方法位置注解的returning属性,可以获得方法的返回值。

       目标类1代码:

	public interface BookDao {
	    public void buy(String bookName);
	}
	
	@Repository
	public class BookDaoImpl implements BookDao {
	    public void buy(String bookName) {
	        System.out.println("BookDao买了一本" + bookName + "书");
	    }
	}

       目标类2代码:

	@Repository
	public class BookDao2 {
	    public String buy2(String bookName) {
	        System.out.println("BookDao2买了一本" + bookName + "书");
	        return bookName;
	    }
	}

       切面类代码:

	@Aspect
	@Component
	public class LogUtils {
	    @Pointcut("execution(public void cj.dao.BookDaoImpl.buy(String)) || execution(public String cj.dao.BookDao2.buy2(String))")
	    public void formal() {
	
	    }
	
	    @Before("formal()")
	    public void before(JoinPoint joinPoint) {   //该方法是Spring利用反射创建的, 需要正确指定参数
	        Object[] objects = joinPoint.getArgs();
	        System.out.println("Before方法" + Arrays.asList(objects));
	    }
	
	    @AfterReturning(value = "formal()", returning = "result")
	    public void afterReturning(Object result) {
	        System.out.println("AfterReturning方法, 返回为" + result);
	    }
	
	    @AfterThrowing(value = "formal()", throwing = "e")
	    public void afterThrowing(Exception e) {
	        System.out.println("异常" + e + "出现了, AfterThrowing方法");
	    }
	
	    @After("formal()")
	    public void after(JoinPoint joinPoint) {
	        Signature signature = joinPoint.getSignature();
	        String name = signature.getName();
	        System.out.println(name + "方法执行结束, After方法");
	    }
	}

       测试代码:

	public class Exp5 {
	    public static void main(String[] args) {
	        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");
	        BookDao o = (BookDao) applicationContext.getBean("bookDaoImpl");
	        System.out.println(o.getClass());
	        o.buy("中华上下五年上册");
	
	        BookDao2 bookDao2 = (BookDao2) applicationContext.getBean("bookDao2");
	        System.out.println(bookDao2.getClass());
	        bookDao2.buy2("中华上下五年下册");
	    }
	}

       运行结果:
Alt

4、Spring中最牛逼的环绕通知

       @Around:就是动态代理。

       目标类代码:

	@Repository
	public class BookDao3 {
	    @Override
	    public String toString() {
	        return "BookDao3{}";
	    }
	
	    public String buy3(String bookName) {
	        System.out.println("BookDao2买了一本" + bookName + "书");
	        return bookName;
	    }
	}

       切面类代码:

	public class LogUtils {
	
	    @Around("execution(public String cj.dao.BookDao3.buy3(String))")
	    public Object NB(ProceedingJoinPoint pjp) {	
	        Object result = null;
	        Object[] args = pjp.getArgs();
	        try {
	            System.out.println("之前");
	            result = pjp.proceed(args);//相当于通过这个方法代理了目标类的方法
	            System.out.println("之后");
	        } catch (Throwable throwable) {
	            throwable.printStackTrace();
	            System.out.println("异常");
	        }
	        System.out.println("结束");
	        return result;
	    }
	}

       测试代码:

	public class Exp5 {
	    public static void main(String[] args) {
	        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");
	        BookDao3 bookDao3 = (BookDao3) applicationContext.getBean("bookDao3");  //取得的是代理对象
	        Object result = bookDao3.buy3("美丽心灵");
	        System.out.println(result);
	    }
	}

       测试结果:
Alt
       最强环绕通知注解成功,而且执行顺序也是对的,没有像之前的那四个通知那样After和AfterReturning顺序是反的。

       还可以通过@Order注解指定多切面的执行顺序,value越小越先执行。

发布了33 篇原创文章 · 获赞 5 · 访问量 2289

猜你喜欢

转载自blog.csdn.net/cj1561435010/article/details/103968230