当前位置:   article > 正文

初学Spring——Spring内核之AOP(面向切面编程)_切类是代理类吗

切类是代理类吗


AOP的全称是:Aspect Oriented Programming,也就是面向切面的编程(也叫面向方面的编程)。它是面向对象编程的(OOP)一种补充。OOP采取的是纵向的代码机制,所以只能实现父子关系的代码重用;而AOP是横向的代码机制,采取的是切入的方式执行代码。AOP是对OOP的一种补充,但并不是OOP的替代品。

怎么理解AOP呢?

在这里插入图片描述
这些执行的通用的内容可以是记录日志、开启事务。

JDK实现代理

jdk代理必须要实现InvocationHandler接口
package com.dz.aop_jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理类:LogHandler;实现InvocationHandler接口
 *
 */
public class LogHandler implements InvocationHandler {

	private Object targetObject; // 目标对象,或者是被代理类

	/**
	 * 生成被代理类的对象
	 * 
	 * @param targetObject
	 * @return
	 */
	public Object creatProxy(Object targetObject) {
		this.targetObject = targetObject;
		/*
		 * 创建一个被代理类 (参数 详情) -获取被代理类的类加载器 -获取被代理类的接口 -使用当前的代理类this来执行被代理的类
		 */
		return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
				this.targetObject.getClass().getInterfaces(), this);
	}

	/**
	 * 代理类执行的方法
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result =null;
		try {
			//伪代码,这就是在执行方法之前横向切入的执行内容
			System.out.println("准备~~开始记录日志...");
			result = method.invoke(this.targetObject, args);//执行被代理类的方法
			//伪代码:这是在方法执行之后横向切入的内容
			System.out.println("日志记录完毕...");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
创建被代理类UserDao.java

被代理的类必须要提供接口

package com.dz.aop_jdk;

public interface UserDao {	
	// 假装添加一个用户
	void addUser();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

实现接口

package com.dz.aop_jdk;

public class UserDaoImpl implements UserDao{

	@Override
	public void addUser() {
		//伪代码
		System.out.println("执行一个添加用户的操作...");
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
测试代理类
package com.dz.aop_jdk;

public class TestProxy {

	public static void main(String[] args) {
		UserDao userDao = new UserDaoImpl();
		
		LogHandler log=new LogHandler();
		//根据被代理的对象,生成代理的对象--根据指定的接口返回对象
		UserDao proxyUser = (UserDao) log.creatProxy(userDao);
		//让代理对象来执行方法
		proxyUser.addUser(); 
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
jdk实现代理结果

在这里插入图片描述

SpringAOP的实现

流行的AOP框架目前有两个:SpringAOP和AspectJ。后来,从Spring2.0开始SpringAOP引入了对AspectJ的支持。并且SpringAOP建议使用AspectJ开发AOP。
开始AOP的配置之前,先来认识一下AOP中的一些术语:

  1. Aspect:切面。代理类就是切面。
  2. JoinPoint:连接点。方法的调用。
  3. PointCut:切入点。切面与程序流程的交叉点(比如执行到某一个类或者方法开始切入,这个切入的点就叫切入点)。
  4. Advince:通知。切入点处要执行的代码,可以理解为切面类中的方法。
  5. Target Object:目标对象。指被通知的对象,也就是被代理的对象。
  6. Proxy:代理。被动态创建的对象。
  7. Weaving:织入。将切面的代码插入到目标对象上。

通知又分为几个类型,下面在代码里面可以看到通知的类型。(也就是这个通知在方法的什么位置执行)。

xml配置方式实现SpringAOP

定义一个切面

package com.dz.aop_spring;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 
 * 配置一个切面类
 */
public class AspectXML {

	public void beforeAdvice() {
		System.out.println("前置通知..执行的方法,比如说:开启事务???");
	}

	public void afterAdvice() {
		System.out.println("最终通知..执行的方法,比如说:关闭事务??");
	}

	public void afterReturnAdvice() {
		System.out.println("后置通知");
	}

	public void afterThrowingAdvice() {
		System.out.println("异常通知");
	}

	public Object aroundAdvice(ProceedingJoinPoint pjp) {
		
		Object object = null;
		try {
			System.out.println("环绕通知开始...");
			object = pjp.proceed();// 执行切点,切中的方法
			System.out.println("环绕通知结束...");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return object;
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

定义切点切入的类

package com.dz.aop_spring;

public interface UserService {
	
	public void insertUser();

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
package com.dz.aop_spring;

public class UserServiceImpl implements UserService {
	
	//假装插入一条数据
	@Override
	public void insertUser() {
		System.out.println("执行一个插入用户的操作");
	}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

配置切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd ">

	<bean id="userService" class="com.dz.aop_spring.UserServiceImpl"></bean>
	<!--配置切面 -->
	<bean id="aspectXmL" class="com.dz.aop_spring.AspectXML"></bean>
	<!-- 配置切面 -->
	<aop:config>
		<aop:aspect id="myAspect" ref="aspectXmL">
			<!-- 配置切入点 :根据表达式找切入点,满足切入点执行通知-->
			<aop:pointcut
				expression="execution(* com.dz.aop_spring.*.*(..))" id="myCut" />
			<!-- 根据切入点,执行一些类型的通知方法 -->
			<aop:before method="beforeAdvice" pointcut-ref="myCut" />
			<aop:after method="afterAdvice"  pointcut-ref="myCut"/>
			<aop:after-returning method="afterReturnAdvice" pointcut-ref="myCut" />
			<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="myCut" />
			<aop:around method="aroundAdvice"  pointcut-ref="myCut"/>
		</aop:aspect>
	</aop:config>

</beans>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

测试类

package com.dz.aop_spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		//满足切入点的类,和类下面所有的方法
		UserService userService = ac.getBean("userService", UserService.class);
		userService.insertUser();
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结果
在这里插入图片描述
异常通知在方法出现异常的时候才会执行。

注解配置SpringAOP

注解配置AOP也是要声明切面的!!!

package com.dz.aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 
 * 配置一个切面类
 */
@Aspect // 注解声明切面
//@Component  //注解声明需要一个扫描
public class AspectAnno {
	// 定义切入点:任何返回类型,aop_anno包下的任何类下的任何方法包括任意参数都满足切入点
	@Pointcut("execution(* com.dz.aop_anno.*.*(..))")
	public void pointCut() {
	}

	//这里也可以直接将表达式放进去
	@Before(value = "execution(* com.dz.aop_anno.*.*(..))")
	//@Before("pointCut()") // 根据切点切入通知
	public void beforeAdvice() {
		System.out.println("注解:前置通知..执行的方法,比如说:开启事务???");
	}

	@After("pointCut()")
	public void afterAdvice() {
		System.out.println("最终通知..执行的方法,比如说:关闭事务??");
	}

	@AfterReturning("pointCut()")
	public void afterReturnAdvice() {
		System.out.println("后置通知");
	}

	@AfterThrowing("pointCut()")
	public void afterThrowingAdvice() {
		System.out.println("异常通知");
	}

	@Around("pointCut()")
	public Object aroundAdvice(ProceedingJoinPoint pjp) {

		Object object = null;
		try {
			System.out.println("环绕通知开始...");
			object = pjp.proceed();// 执行切点,切中的方法
			System.out.println("环绕通知结束...");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return object;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

定义一个UserService.java接口

package com.dz.aop_anno;

public interface UserService {
	
	public void updatetUser();

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

并实现它

package com.dz.aop_anno;

public class UserServiceImpl implements UserService {
	
	//假装修改一条数据
	@Override
	public void updatetUser() {
		System.out.println("执行一个修改用户的操作");
	}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

配置文件开启注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/context ">

	<!-- 这里没有使用扫描,而是直接装配的bean -->
	<bean id="userService" class="com.dz.aop_anno.UserServiceImpl"></bean>
	<bean id="aspectAnno" class="com.dz.aop_anno.AspectAnno"></bean>
	
	<!--启动注解配置切入方式  -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

测试注解配置的AOP

package com.dz.aop_anno;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) ac.getBean("userService");
		userService.updatetUser();
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结果
注解配置结果

咦???仔细对比了两张结果图发现不一样?

总结

  • 两张结果图可以看出:

基于XML与注解的方式执行的结果是相同的,只是目标方法前后的通知顺序发生了改变。

  • 需要注意的是:

如果同一个连接点有多个通知执行,那么前置通知与环绕通知的执行顺序未知,后置通知与环绕通知的执行顺序未知。

  • xml配置与注解配置的相同之处

无论使用的哪种方法的配置,都需要定义:切面、切点、通知。(除非有人帮你定义好了,你只需要引用。比如:事务管理。)

附上我的目录结构
在这里插入图片描述
依赖

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/876628
推荐阅读
相关标签
  

闽ICP备14008679号