------------恢复内容开始------------
Spring AOP
面向切面编程,举个例子,在一个项目中在不改变原有的代码情况下添加一个权限管理,去掉权限管理模块也不回对原有的代码有任何影响。也就是说在不改变原来的代码条件,增强原有的方法,这就是面向切边编程。
AOP动态代理
AOP动态代理有两种情况:
2、没有接口的情况,使用Cglib动态代理, Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,增强类的方法
JDK动态代理
1、使用JDK动态代理,使用Proxy类里面的方法创建代理对象:
调用newProxyInstance方法,方法有三个参数:
第一个参数:类加载器;
第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口,这个参数是个数组;
第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强的方法。
模拟JDK动态代理
模拟底层的动态代理实现原理
1)创建接口
1 public interface UserDao { 2 public int add(int a,int b); 3 4 public String update(String id); 5 }
2)实现接口
1 import com.riven.dao.UserDao; 2 3 public class UserDaoImpl implements UserDao { 4 @Override 5 public int add(int a, int b) { 6 System.out.println("add方法执行了"); 7 return a+b; 8 } 9 10 @Override 11 public String update(String id) { 12 return id; 13 } 14 }
3)编写代理代码
1 package com.riven.test; 2 3 import com.riven.dao.UserDao; 4 import com.riven.impl.UserDaoImpl; 5 6 import java.lang.reflect.InvocationHandler; 7 import java.lang.reflect.Method; 8 import java.lang.reflect.Proxy; 9 import java.util.Arrays; 10 11 public class JDKProxy { 12 public static void main(String[] args) { 13 //创建接口实现类代理对象 14 Class[] interfaces = {UserDao.class}; 15 /*方式一:第三个参数使用匿名内部类方式创建代理对象,编写增强类代码 16 Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { 17 @Override 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 return null; 20 } 21 }); 22 */ 23 // 方式二: 24 //要被代理的对象 25 UserDaoImpl userDao = new UserDaoImpl(); 26 //返回代理对象 27 UserDao dao =(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)); 28 int result = dao.add(1,2); 29 System.out.println("result = " + result); 30 } 31 } 32 //方式二 33 //创建代理对象代码 34 class UserDaoProxy implements InvocationHandler{ 35 private Object obj; 36 //1、创建的是谁的代理对象,把谁传递进来 37 //有参构造传递 38 public UserDaoProxy(Object obj){ 39 this.obj = obj; 40 } 41 //增强的逻辑 42 @Override 43 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 44 /** 45 * proxy: 代理对象 46 * method:当前的方法 47 * args:存放传递的参数 48 */ 49 50 //方法之前 51 System.out.println("方法之前执行..." + method.getName() + ":传递的参数..."+ Arrays.toString(args)); 52 //被增强的方法执行 53 //传入被代理的对象、调用该方法的参数 54 Object res = method.invoke(obj,args); 55 //方法之后 56 System.out.println("方法执行之后..." + obj); 57 return res; 58 } 59 }
Spring对动态代理做了封装,我们可以通过配置,然后调用Spring内的动态代理。
AOP术语
1)连接点
类里面哪些方法可以被增强,这些方法称为连接点。
2)切入点
实际被增强了的方法,称为切入点。
3)通知(增强)
(1)实际增强的逻辑部分称为通知(增强)
(2)通知有多种类型
1、前置通知:增强方法之前,前置通知会执行;
2、后置通知:增强方法之后,后置通知会执行;
3、环绕通知:增强方法之前之后,环绕通知都会执行;
4、异常通知:增强的方法出现异常,异常通知会执行;
5、最终通知:最终通知类似finally,无论会不会出现异常都会执行。
4)切面
是一个动作,把通知应用到切入点的过程。例如,为登录方法添加权限管理功能的过程。
AOP操作准备
1、Spring框架一般都是基于AspectJ实现AOP操作
1)什么是AspectJ
AspectJ不是Spring组成部分,它是独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作
1)基于xml配置文件实现
2)基于注解方式实现(推荐)
3、引入AOP依赖
4、切入点表达式
1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
2)语法结构
execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))
举例1:对com.springaop.UserDao类里面的add进行增强
execution(* com.springaop.UserDao.add(..)):
* com.springaop.UserDao.add(..):*:表示任意权限修饰符,返回类型可以不写(这里没写)类全路径点方法名,".." 表示所有参数。
举例3:对com.springaop.UserDao类里面的所有方法进行增强
execution(* com.springaop.UserDao.*(..)):
AOP示例演示
1、创建类,在类里面定义方法
1 //被增强的类 2 public class User { 3 public void add(){ 4 System.out.println("add..."); 5 } 6 }
2、创建增强类(编写增强逻辑)
1 //增强的类 2 public class UserProxy { 3 //前置通知 4 public void before(){ 5 System.out.println("before...."); 6 } 7 }
3、进行通知的配置
1)在 spring 配置文件中,开启注解扫描
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 9 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 10 "> 11 12 <!-- 自动扫描 13 base-package值为扫描该包下(包括子包)的所有类 14 --> 15 <context:component-scan base-package="com.riven.aopanno"></context:component-scan> 16 17 <!-- 是Aspect注解生效,为目标类自动生成代理对象 --> 18 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 19 20 </beans>
2)使用注解创建 User 和 UserProxt
在这两个类上添加 @Component 注解
3)在增强类上面添加注解 @Aspect
4)在 spring配置文件中开启生成代理对象
4、配置不同类型的通知
1)在增强类的里面,在作为通知方法上面添加通知类行注解,使用切入点表达式配置
1 import org.aspectj.lang.annotation.Aspect; 2 import org.aspectj.lang.annotation.Before; 3 import org.springframework.stereotype.Component; 4 5 //增强的类 6 @Component 7 @Aspect //生成代理对象 8 public class UserProxy { 9 10 //前置通知 11 @Before(value = "execution(* com.riven.aopanno.User.add(..))") 12 public void before(){ 13 System.out.println("before...."); 14 } 15 }
5、相同的切入点抽取
1 //相同切入点抽取 2 @Pointcut(value = "execution(* com.riven.aopanno.User.add(..))") 3 public void pointdemo(){ 4 5 } 6 //前置通知 7 @Before(value = "pointdemo()") 8 public void before(){ 9 System.out.println("你人呢before...."); 10 }
6、有多个增强类对同一个方法进行增强,进行设置优先级
1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
UserProxy
1 //被增强的类 2 @Component 3 @Aspect //生成代理对象 4 @Order(2)
5 public class UserProxy {
6 //相同切入点抽取
7 @Pointcut(value = "execution(* com.riven.aopanno.User.add(..))")
8 public void pointdemo(){
9
10 }
11 //前置通知
12 @Before(value = "pointdemo()")
13 public void before(){
14 System.out.println("你人呢before....");
15 }
16 @AfterReturning(value = "execution(* com.riven.aopanno.User.add(..))")
17 public void afterReturning(){
18 System.out.println("你人呢afterReturning....");
19 }
20 @After(value = "execution(* com.riven.aopanno.User.add(..))")
21 public void aftere(){
22 System.out.println("你人呢after....");
23 }
24 @AfterThrowing(value = "execution(* com.riven.aopanno.User.add(..))")
25 public void afterThrowing(){
26 System.out.println("你人呢afterThrowing....");
27 }
28 //环绕通知,在方法执行之前之后都会执行
29 //方法执行之前先执行这个方法然后再执行前置通知
30 //方法执行之后先执行这个方法然后在执行后置通知
31 @Around(value = "execution(* com.riven.aopanno.User.add(..))")
32 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
33 System.out.println("环绕之前.....");
34
35 //被增强的方法执行
36 proceedingJoinPoint.proceed();
37
38 System.out.println("环绕之后....");
39 }
40 }
新创建UserProxy2
1 package com.riven.aopanno; 2 3 import org.aspectj.lang.annotation.Aspect; 4 import org.aspectj.lang.annotation.Before; 5 import org.springframework.core.annotation.Order; 6 import org.springframework.stereotype.Component; 7 8 @Component 9 @Aspect 10 @Order(1) 11 public class UserProxy2 { 12 @Before(value = "execution(* com.riven.aopanno.User.add(..))") 13 public void before(){ 14 System.out.println("你人呢before2...."); 15 } 16 }
创建运行类
1 public class TestAOP { 2 public static void main(String[] args) { 3 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); 4 User user = context.getBean("user", User.class); 5 user.add(); 6 } 7 }
运行结果:
1、没有出现异常
先运行UserProxy2的 @Before ==> UserProxy的@Around环绕(前)代码 ==> @Before ==> 执行被增强方法内容 ==> @Around环绕(后)代码 ==> @After ==>@AfterReturning
2、出现异常
先运行UserProxy2的 @Before ==> UserProxy的@Around环绕(前)代码 ==> @Before ==> @After ==>@AfterThrowing
2)使用配置文件