深入浅出boot2.0第4章 aop

Aop:
即:约定编程。其核心切面(Interceptor)。包含了5种通知(advice)。
它们分别是:前置(befor),环绕(around),后置(after),事后返回(AfterReturning),异常(AfterThrowing)。
环绕通知,被称作最强大的通知,也意味着难以控制(取代原有方法,也提供原有方法的能力)。
人们对它的理解就是,它很强大,也很危险,不要用。
为啥呢?写一个简单的例子,你就会知道 ,它是有bug的(猜测这个bug大约从4.3直到现在一直存在)。

1,引入:starter-web 和 starter-aop 包
2,定义一个最简单的pojo,里面两个最简单的字段,username note。
3,写一个最简单的接口:
public interface UserService {
	public void printUser(User user);
}
4,然后实现它,如何实现看心情。
5,写一个最简单的切面
@Aspect
public class MySimpleAspect {

	//定义切点。指向包路径。
	@Pointcut("execution(* com.example.demobenaware.service.impl.UserServiceImpl.printUser(..))")
	public void pointCut() {
	}

	//前置通知 1
	@Before("pointCut()")
	public void before() {
		System.out.println("before ......"+new Date(System.currentTimeMillis()));
	}

	//环绕通知 2
	@Around("pointCut()")
	public void around(ProceedingJoinPoint jp) throws Throwable {
		System.out.println("around before......");
		jp.proceed();
		System.out.println("around after......");
	}

	//后置通知 3
	@After("pointCut()")
	public void after() {
		System.out.println("after ......"+new Date(System.currentTimeMillis()));
	}
	
	//事后返回通知 4
	@AfterReturning("pointCut()")
	public void afterReturning() {
		System.out.println("afterReturning ......");
	}

	//异常通知
	@AfterThrowing("pointCut()")
	public void afterThrowing() {
		System.out.println("afterThrowing ......");
	}
}
6,把这个切面交给ioc容器。(放在boot自带的@SpringBootApplication类里面,省事了@Configuration,@ComponentScan都是配置好的 )
    // 切面类注册给spring
    @Bean(name = "mySimpleAspect")
    public MySimpleAspect initMySimpleAspect() {
        return new MySimpleAspect();
    }
7,写个最简单的控制器。怎样写都行。记得调用要拦截的方法 userService.printUser(user);

切面的执行顺序(非异常)应该为:
前置,环绕,后置,事后返回。
而真实的执行顺序是:
环绕(proceed()方法前),前置,环绕(proceed()方法后),后置,事后返回。
对于先执行环绕通知,我是懵了(备注:如果不使用环绕通知,任何版本都没问题)。













Spring是java史上最伟大的框架,Aop更是核心所在。
即:约定编程。其核心切面(Interceptor)。包含了5种通知(advice)。
它们分别是:前置(befor),环绕(around),后置(after),事后返回(AfterReturning),异常(AfterThrowing)。
环绕,最强大的通知,难以控制(取代原有方法,也提供原有方法的能力)。
它很强大,也很危险。
它是有bug的(直到现在)。

1,引入:web 和 aop 包
2,pojo。
3,接口:
 public void printUser();
4,实现它。
5,切面
@Aspect
public class Aspect {

 //定义切点。指向包路径。
 @Pointcut("execution(* 包路径到方法(..))")
 public void pointCut() {
 }

 //前置通知 1 @Before("pointCut()")

 //环绕通知 2
 @Around("pointCut()")
 public void around(ProceedingJoinPoint jp){
  ("a");
  jp.proceed();
  ("b");
 }

 //后置通知 3 @After("pointCut()")
 //事后返回通知  @AfterReturning("pointCut()")  4
 //异常通知  @AfterThrowing("pointCut()")
}
6,切面给ioc容器。(放在boot自带的主类里面,省事了)
    @Bean(name = "aspect")
7,控制器。记得调用要拦截的 你的方法 ;
切面的执行顺序(非异常)应该为:
前置,环绕,后置,事后返回。
而真实顺序是:
环绕(proceed()前),前置,环绕(proceed()后),后置,事后返回。
懵了(备注:如果不使用环绕通知,没问题)。

约定编程 代理

  • 切点 通知 连接点 引入 织入
  • 你需要记住约定的流程是什么,然后完成对应的任务,却不需要知道 底层设计者 是怎么将 约定的内容 织入对应的流程中的。
public interface HelloService {
	public void sayHello(String name);
}


public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        if (StringUtils.isEmpty(name)){
            throw new RuntimeException("参数为空!");
        }
        System.out.println(name.trim()+"你好!!!");
    }
}
  1. 定义拦截器接口

    public interface Interceptor {
        
    	//事前方法
    	public boolean before();
    	
    	//事后方法
    	public void after();
        
        //是否返回方法。事件没有发生异常执行
    	public void afterReturning();//无异常执行
    	
    	//事后异常方法,当事件发生异常后执行
    	public void afterThrowing();//有异常就执行
        
        
        //是否使用around(下面的)方法取代原有方法
    	boolean useAround();
        
        
    	/**
    	 * 取代原有事件方法
    	 * @param invocation -- 回调参数,可以通过它的proceed方法,回调原有事件
    	 * @return 原有事件返回对象
    	 
    	 * @throws InvocationTargetException 
    	 * @throws IllegalAccessException
    	 */
    	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
    	
    	
    }
    

后面会给出约定,将这些发方法织入 流程中

  1. Invocation定义
@Data
public class Invocation {

	private Object[] params;//参数
	private Method method;//方法
	private Object target;//目标
	
    //全参 构造赋值
	public Invocation(Object target, Method method, Object[] params) {
		this.target = target;
		this.method = method;
		this.params = params;
	}
	
	public Object proceed() throws InvocationTargetException, IllegalAccessException {
		return method.invoke(target, params);//方法执行 参数是:  要执行的目标,要传递的参数
	}//会以反射的形式 去调用 原有的方法
}

  1. 开发自己的拦截器

    public class MyInterceptor implements Interceptor {
    
    	@Override
    	public boolean before() {
    		System.out.println("before ......");
    		return true;
    	}
    
    	@Override
    	public void after() {
    		System.out.println("after ......");
    	}
        
        @Override
    	public boolean useAround() {
    		return true;
    	}
        
    
    	@Override
    	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
    		System.out.println("around before ......");//增强代码
    		Object obj = invocation.proceed();//执行原来的方法
    		System.out.println("around after ......");//增强代码
    		return obj;
    	}
    
    	@Override
    	public void afterReturning() {//事后,没发生于异常执行
    		System.out.println("afterReturning......");
    
    	}
    
    	@Override
    	public void afterThrowing() {//事后,发生了异常执行
    		System.out.println("afterThrowing 。。。。。。");
    	}
    
    }
    
    1. 开始约定了:
    • 约定是Spring Aop的本质

    • 提供一个ProxyBean类,有一个静态方法 给他人使用

      • ​ public static Object getProxyBean(Object target, Interceptor interceptor) {}

      • target 存在接口。interceptor 是上面定义的接口

      • 返回一个对象记作:proxy,使用target对象进行强制转换

        		HelloService helloService = new HelloServiceImpl();//定义一个类
        		HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
        		//用这个类得到一个代理,
        		proxy.sayHello("zhangsan");//使用代理调用方法
        		System.out.println("\n###############name is null!!#############\n");
        		proxy.sayHello(null);
        
      • 生成代理对象的时候会走一遍流程:

        before ......拦截器的before方法
        around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法
        around after ......拦截器的around的方法,invocation.proceed()后
        after ......拦截器的after方法
        afterReturning......原方法执行完毕的执行
        
      • 用这个代理调用方法的时候也会走一遍流程

        before ......拦截器的before方法
        around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法
        
        zhangsan你好!!!原方法执行了
        
        around after ......拦截器的around的方法,invocation.proceed()后
        
        after ......拦截器的after方法
        afterReturning......原方法执行完毕的执行
        
        cao,下面怎么又执行了一遍。好像每一个方法,都会执行下面的。哪怕sout.("hello word")
        
        before ......拦截器的before方法
        around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法
        
        around after ......拦截器的around的方法,invocation.proceed()后
        
        after ......拦截器的after方法
        afterReturning......原方法执行完毕的执行
        
      • 异常的执行了流程

        before ......拦截器的before方法
        around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法
        
        after ......拦截器的after方法
        afterThrowing 。。。。。。原方法异常了的执行
        
        //又有一个方法执行了
        before ......拦截器的before方法
        around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法
        
        around after ......拦截器的around的方法,invocation.proceed()后
        
        after ......拦截器的after方法
        afterReturning......原方法执行完毕的执行
        

        我们已经把服务和 拦截器的方法织入约定的流程中了。

  2. ProxyBean 代理生成类

如何将 服务类 和 拦截方法 织入对应的流程。是ProxyBean的功能。

动态代理:你需要采访一个儿童时候,需要他的父母同意。在一些问题上父母替他回答。

对于另一些问题,父母觉得不太适合这个小孩。会拒绝掉。

这时父母就是这名儿童的代理了。

通过代理可以增强或者控制对儿童这个真是对象(target)的访问。

ProxyBean源码在下面
  1. 约定(拦截器接口的方法)
    1. 会先执行before方法
    2. 如果useAround 返回true(返回false执行target对象的方法)。则执行around方法(不执行target对象的方法)
      1. around 参数invocation对象存在 存在一个 proceed方法,可调用target方法
    3. 无论怎么样都会执行after方法。 发生异常执行afterThrowing,不发生异常执行 afterReturning方法
  • jdk提供了类proxy的静态方法newProxyInstance 生成代理对象

        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
        
        }
    
    • ClassLoader 类加载器
    • interfaces绑定的接口,把代理对象绑定到那些接口下
    • InvocationHandler绑定代理对象逻辑实现
  • InvocationHandler 解析

    public Object invoke(Object proxy, Method method, Object[] args)  {}
    目标对象,方法,参数
    
  • ProxyBean源码

public class ProxyBean implements InvocationHandler {//implements InvocationHandler

	private Object target = null;
	private Interceptor interceptor = null;
	
	/**
	 * 绑定代理对象
	 * @param target 被代理对象
	 * @param interceptor 拦截器
	 * @return 代理对象
	 */
	public static Object getProxyBean(Object target, Interceptor interceptor) {
		ProxyBean proxyBean = new ProxyBean(); // new ProxyBean(); 保存目标对象 和拦截器
		// 保存被代理对象
		proxyBean.target = target;
		// 保存拦截器
		proxyBean.interceptor = interceptor;
		// 生成代理对象 getProxyBean 生成代理对象。 
		Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				proxyBean);//类加载器,接口, 一个 implements InvocationHandler绑定代理对象
		// 返回代理对象
		return proxy;
	}
    /**
    *生成了一个代理对象,这个代理对象挂在target实现 的 接口之下
    *所以你 可以用 target对象 实现的接口 对这个代理对象 实现 强制装换
    *并且这个将这个代理对象的逻辑挂在 ProxyBean实例之下
    * 完成了目标对象(target) 和 代理对象(Proxy) 的绑定
    * 最后将代理对象返回给调用者
    
    * HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
    * 当我们使用 代理 调用方法时,就会进入ProxyBean的invoke方法里
    * 这就是我们通过一定的规则完成约定编成的原因。
    */
    
	
	/**
	 * 处理代理对象方法逻辑
	 * @param proxy 代理对象
	 * @param method 当前方法
	 * @param args  运行参数
	 * @return 方法调用结果
	 * @throws Throwable 异常
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)  {
		//异常标识
		boolean exceptionFlag = false;
		//创建Invocation对象
		Invocation invocation = new Invocation(target, method, args);
		Object retObj = null; 
		try {
			if (this.interceptor.before()) {//先执行before,如果before为true
				retObj = this.interceptor.around(invocation);//执行around方法
			} else {
				retObj = method.invoke(target, args);//如果before为false,还执行原来的方法
			}
		} catch (Exception ex) {
			//产生异常
			exceptionFlag = true;//执行异常了
		}
		this.interceptor.after();//执行after方法
		if (exceptionFlag) {//如果异常了,就执行afterThrowing
			this.interceptor.afterThrowing();
		} else {//否则执行afterReturning方法
			this.interceptor.afterReturning();
			return retObj;
		}
		return null;
	}

只要提供一定的约定规则,按照约定编程后

就可以把自己开发的代码织入约定的流程中

jdbc经典代码



Connection conn = null;
		int result = 0;
		try {
			//先注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//在通过驱动管理器,获取数据事务连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
			//非自动提交事务
			conn.setAutoCommit(false);
			//执行层
			PreparedStatement ps = null;
			try {
				//预设sql
				ps = conn.prepareStatement("insert into user(user_name,note) values (?,?)");
				//参数指定
				ps.setString(1, "张三");
				ps.setString(2, "study");
				//执行
				result = ps.executeUpdate();
			} finally {
				//怎样都关闭连接
				ps.close();
			}

			//提交事务
			conn.commit();
		} catch (Exception e) {
			try {
				//回滚事务
				conn.rollback();
			} catch (SQLException ex) {
				ex.printStackTrace();
			}
			e.printStackTrace();
		} finally {
			//释放连接池
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		if (result == 0) {
		} else {
		}

存在一个默认的过程

  • 打开数据库连接,然后对其进行设置
  • 这行SQL语句
  • 如果没有异常,就提交
  • 如果发生异常,则回滚
  • 关闭数据库事务连接

这个默认流程 可以通过 Aop来实现,你只需要编写SQL这一步,然后织入流程中

AOP概念

  • Aop 也是一种约定流程的编程
  • @AspectJ注解
  1. 为什么要用aop

    数据库事务的管控,当我们要保存一个用户时,连同它的角色信息一并保存到数据库中。

    要么一起成功,要么一起失败。(oop无能为力)

@Transactional //实现了 数据库的打开和关闭 ,事务的回滚 和 提交
public int inserUser(User user){
	return userDao.insertUser(user);
}
  • spring Aop 可以处理 OOP实现的业务逻辑

  • Spring Aop是一种基于方法的aop

SpringAop 流程约定:

  • 连接点 join point 。 HelloServiceImpl sayHello方法。是具体被拦截的对象。

  •     public void sayHello(String name) {
            if (StringUtils.isEmpty(name)){
                throw new RuntimeException("参数为空! 异常方法执行了");
            }
            System.out.println(name.trim()+"你好!!!原方法执行了");
        }
    
  • 切面 aspect 。 MyInterceptor 。包含前置通知 before ,环绕通知around,后置通知after,事后返回通知 afterReturning ,和 异常通知 afterThrowiing

    	@Override
    	public void after() {
    		System.out.println("after ......拦截器的after方法");
    	}
    
    	@Override
    	public boolean useAround() {
    		return true;
    	}
    	
    	@Override
    	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
    		System.out.println("around before ......拦截器的around的方法,invocation.proceed()前,下面执行原方法");
    		Object obj = invocation.proceed();
    		System.out.println("around after ......拦截器的around的方法,invocation.proceed()后");
    		return obj;
    	}
    
  • 切点 point cut 。有时候不单是单个方法,也可能是多个类的不同方法。通过正则 和 指示器的规则 去定义

  • 通知 advice,就是切面中的那些通知

  • 目标对象 target 。即:被代理对象,HelloServiceImpl 实例

  • 引入 introduction。引入新的类和其他方法,增强现有的bean功能

  • 织入 weaving。通过动态代理技术,为原服务生成代理对象。

aop 开发详细

确定连接点

public interface UserService {
	public void printUser(User user);
}

@Service
public class UserServiceImpl implements UserService {
	
	@Override
	public void printUser(User user) {
		if (user == null) {
			throw new RuntimeException("检查用户参数是否为空......");
		}
		System.out.print("id =" + user.getId());
		System.out.print("\tusername =" + user.getUsername());
		System.out.println("\tnote =" + user.getNote());
	}
}

开发切面

1.最简单的切面

@Aspect
public class MySimpleAspect {

	//定义切点
	@Pointcut("execution(* com.springboot.chapter4.aspect.service.impl.UserServiceImpl.printUser(..))")
	public void pointCut() {
	}

	//前置通知 1
	@Before("pointCut()")
	public void before() {
		System.out.println("before ......"+new Date(System.currentTimeMillis()));
	}

	//环绕通知 2
	@Around("pointCut()")
	public void around(ProceedingJoinPoint jp) throws Throwable {
		System.out.println("around before......");
		jp.proceed();
		System.out.println("around after......");
	}

	//后置通知 3
	@After("pointCut()")
	public void after() {
		System.out.println("after ......"+new Date(System.currentTimeMillis()));
	}
	
	//事后返回通知 4
	@AfterReturning("pointCut()")
	public void afterReturning() {
		System.out.println("afterReturning ......");
	}

	//异常通知
	@AfterThrowing("pointCut()")
	public void afterThrowing() {
		System.out.println("afterThrowing ......");
	}
	
}
  • 毕竟不是所有的功能都是需要启用Aop的,spring通过这个正则去匹配,去确定对应的方法,是否启用切面编程。

“execution( * com.springboot.chapter4.aspect.service.impl.UserServiceImpl.printUser(…) )”

execution 执行的时候,拦截里面的正则匹配的方法

*任意返回类型的方法

com.****.pringUser 指定目标对象的方法

(…) 任意参数进行匹配

AspectJ关于 Spring Aop切点的指示器

项目类型描述
arg()限定连接点方法参数
@args连接点方法参数上的注解进行限定
execution()连接点的执行方法
this()限制连接点匹配Aop代理Bean引用为指定的类型
target目标(被代理)对象
@target()限制目标对下你给的配置了指定的注解
within限制连接点匹配指定的注解
@within()限制连接点带有匹配注解类型
@annotation()限制带有指定注解的连接点
	@Pointcut("execution(* com.springboot.chapter4.*.*.*.*.print(..)) && bean('userServiceImpl')") bean没用,并且报错。前面的可用
	public void pointCut() {
	}

execution(* com.springboot.chapter4.*.*.*.*.printUser(..)) && bean('userServiceImpl')
			com.springboot.chapter4.aspect.service.impl.UserServiceImpl.printUser

&& 并且

bean(‘userServiceImpl’) 代表对Spring Bean名称的限定

测试Aop

pom
<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId> //必要依赖
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
	</dependencies>
控制器
//定义控制器
@Controller
// 定义类请求路径
@RequestMapping("/user")
public class UserController {

	// 注入用户服务
	@Autowired
	private UserService userService = null;

	// 定义请求
	@RequestMapping("/print")
	// 返回json
	@ResponseBody
	public User printUser(Long id, String userName, String note) {
		User user = new User();
		user.setId(id);
		user.setUsername(userName);
		user.setNote(note);
		userService.printUser(user);
		return user;
	}
}
配置
@SpringBootApplication(scanBasePackages = { "com.springboot.chapter4.aspect" })
public class Chapter4Application {

	// 启动切面
	public static void main(String[] args) {
		SpringApplication.run(Chapter4Application.class, args);
	}

	// 定义切面
	@Bean(name = "myAspect")
	public MyAspect initMyAspect() {
		return new MyAspect();
	}
}

userService 可以看出是个 jdk 动态代理对象

http://localhost:8080/user/print/?id=111&note=哈哈哈

before ......Sun May 03 18:27:10 CST 2020   之前
id =111	username =null	note =哈哈哈 方法体
after ......Sun May 03 18:27:25 CST 2020  之后
afterReturning ...... 事后返回

环绕通知

  • 最强大的通知,也意味着难以控制

  • 场景:你需要 大幅度修改原有 目标对象的服务逻辑时。否则:尽可能用其他通知。

  • 是一个取代 原有目标对象 方法的通知。也提供了回调原方法的能力。

    	切面类:
    
    	//环绕通知 2
    	@Around("pointCut()")
    	public void around(ProceedingJoinPoint jp) throws Throwable {
    		System.out.println("around before......");
    		jp.proceed();//回到原有的放
    		System.out.println("around after......");
    	}
    
around before......   这个顺序是错的,它很强大,却很危险

before ......Sun May 03 21:43:56 CST 2020 。这个在应该放在 第一位

id =111	username =null	note =哈哈哈
around after......

after ......Sun May 03 21:44:06 CST 2020

afterReturning ......

spring 4.3.9 xml测试,没问题

引入

  • 引入用户检测的接口

    public interface UserValidator {
    	public boolean validate(User user);
    }
    //检测用户信息是否为空,如果为空 则不再打印
    public class UserValidatorImpl implements UserValidator {
    	@Override
    	public boolean validate(User user) {
    		System.out.println("引入新的接口:"+ UserValidator.class.getSimpleName());
    		return user != null && user.getUsername() != null;//大概需要这样校验
    	}
    }
    
    
  • 使用

    @Aspect
    public class MyAspect {
    	@DeclareParents(value= "com.springboot.chapter4.aspect.service.impl.UserServiceImpl+", defaultImpl=UserValidatorImpl.class)
    	public UserValidator userValidator;
    }
    
    @DeclareParents(value= "com.UserServiceImpl+", defaultImpl=UserValidatorImpl.class)
    
    • @DeclareParents 引入新的类来增强服务
    • value XX + 要增强的对象
    • defaultImpl 引入增强的类
  • action 测试引入的验证器

    		// 注入用户服务
    	@Autowired
    	private UserService userService = null;
    
    	// 定义请求
    	@RequestMapping("/vp")
    	// 返回json
    	@ResponseBody
    	public User validateAndPrint(Long id, String userName, String note) {
    		User user = new User();
    		user.setId(id);
    		user.setUsername(userName);
    		user.setNote(note);
    		// 强制转换
    		UserValidator userValidator = (UserValidator) userService;
    		// 验证用户是否为空
    		if (userValidator.validate(user)) {
    			userService.printUser(user);
    		}
    		return user;
    
        }
    
  • 原理

Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				proxyBean);
//类加载器,接口, 一个 implements InvocationHandler绑定代理对象

	源码:
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • 第二个参数是一个对象数据。生成代理对象 会把 UserService 和 UserValidator 两个参数传递进去
  • 让代理对象挂到这两个接口下,

获取通知的参数

  • 传递参数给通知

  • 只需要在切点处 加入对应 的正则式 就可以了。

  • 非环绕通知 还可以 使用 一个连接点 JoinPoint 类型的参数

  • 环绕通知 还是 那个可执行 原方法的对象 ProceedingJoinPoint jp;jp.proceed();

    	@Before("pointCut() && args(user)")
    	public void beforeParam(JoinPoint point, User user) {
    		Object[] args = point.getArgs();
    		System.out.println("before ......");
    	} 
    
    
    
    	//前置通知 1
    	@Before("pointCut() && args(user)")
    	public void before(JoinPoint point, User user) {
    		Object[] args = point.getArgs();
    		System.out.println("所有的参数:"+args.toString());
    		System.out.println("before ......"+new Date(System.currentTimeMillis()));
    	}
    

织入

  • 是一个生成动态代理对象 并且将切面 和 目标对象方法 编织 成为约定流程 的过程

  • 动态代理,jdk,cglib,javassist,asm,

  • jdk 要求 ,被代理的目标对象 必须拥有接口。cglib都可以

  • 当你需要使用Aop的类拥有接口时,它会以Jdk动态代理运行。否则cglib。

  • 注意最新版的:5.2.5 我看到的,都是cglib

  • 最重要的还是这一句话

  Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
  				proxyBean);
  //类加载器,接口, 一个 implements InvocationHandler绑定代理对象

多个切面

@Aspect
public class MyAspect1 implements Ordered {
	
	@Override
	public int getOrder() {
		return 1; //执行顺序为1
	}

	@Pointcut("execution(* com.example.demobenaware.service.impl.UserServiceImpl.manyAspects(..))")
	public void manyAspects() {
	}

	@Before("manyAspects()")
	public void before() {
		System.out.println("MyAspect1 before ......");
	}

	@After("manyAspects()")
	public void after() {
		System.out.println("MyAspect1 after ......");
	}

	@After("manyAspects()")
	public void afterReturning() {
		System.out.println("MyAspect1 afterReturning ......");
	}

}


@Aspect
@Order(2) //执行顺序为2,任意选择一种继承 或者注解都行
public class MyAspect2 implements Ordered {

	@Override
	public int getOrder() {
		return 2;
	}
}


	// 定义切面
	@Bean(name = "myAspect2")
	public MyAspect2 initMyAspect2() {
		return new MyAspect2();
	}

	// 定义切面
	@Bean(name = "myAspect1")
	public MyAspect1 initMyAspect1() {
		return new MyAspect1();
	}

  • http://localhost:8080/user/manyAspects
从外 到里执行,在从里到外 执行
MyAspect1 before ......
MyAspect2 before ......
测试多个切面顺序
MyAspect2 after ......
MyAspect2 afterReturning ......

MyAspect1 after ......
MyAspect1 afterReturning ......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值