Spring框架-04-02-AOP-三种实现方式


AOP概述

AOP是基于代理模式实现的。
AOP=面向切面编程

在这里插入图片描述

现在主流是OOP。面向对象编程。
AOP是OOP的补充。

一般使用OOP的条件

  1. 多用于功能性代码而不是业务性代码
  2. 多用于公用功能方面
  • 关注点(concern):
    一个关注点可以是一个特定的问题、概念或者程序要达到的一个目标。比如日志记录等都是关注点。如果一个关注点的代码被多个类或方法引用,这个关注点就被称为横切关注点

可以是问题,可以是目标

  • 切面(aspect):
    一个切面是对一个横切关注点的模块化
    是上面关注点的模块化

  • 连接点(joinpoint):
    程序执行过程中的某个点,如方法调用或者抛出异常等
    从空间的角度看,什么地方可以加点

  • 通知(advice):
    在特定的连接点应该执行的动作,在多数AOP框架中,通知都是由拦截器实现的,Spring AOP也是如此(定义何时)
    从时间层面

  • 切入点(pointcut):
    定义在哪些连接点处使用通知,在应用中一般通过指定类名、方法名或者匹配类名、方法名的正则表达式来指定切入点(何地)

  • 目标对象(target):
    =被切面所通知的对象
    =被代理对象

  • 织入(weaving):
    将切面应用到目标对象,从而创建新的代理对象

  • 前置通知(Before advice):
    在某连接点之前执行的通知

  • 后置通知(After returning advice):
    在某连接点正常完成后执行的通知

  • 异常通知(After throwing advice):
    在方法抛出异常退出时执行的通知

  • 最终通知(After finally advice):
    当某连接点退出的时候执行的通知

  • 环绕通知(Around advice):
    包围一个连接点的通知,这是最强大的一种通知类型

Spring Api流程

在这里插入图片描述

黑线是流程执行过程
蓝色面与黑线点是连接点


Spring AOP

Spring AOP是由纯Java语言实现

Spring AOP和其他AOP框架不同,目的并不是提供最完整的AOP,目前仅支持方法执行上的连接点;但是他和Spring的IoC集成紧密,可以帮助解决企业应用中的常见问题

功能不是很强大,有局限性。

当Spring AOP不能满足我们的需求时,可以采用其他AOP框架,比如AspectJ,Spring框架可以很好的集成诸如AspectJ等框架,他们是互补的

在这里插入图片描述
紫色就是通知,时间角度确定通知顺序
切入点=在哪里植入
通知者会告诉代理工厂

SpringAPI传统方式

在这里插入图片描述

前置通知

项目结构

在这里插入图片描述

Md5Advice.java 前置通知

package adivce;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

import util.Md5Encode;


//前置通知
//需要实现接口
public class Md5Advice implements MethodBeforeAdvice{
    
    

	
	//需要实现抽象方法
	//参数1:被调用方法,参数2;被调用方法的参数,参数3:被代理的对象
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
    
    
		System.out.println("被调用的方法"+arg0.getName());
		System.out.println("被代理的对象"+arg2.getClass().getName());
		//下面才是密码加密、
		//密码是第二个参数
		//arg1[1] = Md5Encode.getMD5(arg1[1].toString().getBytes());
		System.out.println("================================");
	}

}

Test.java

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

import com.UserService;

public class Test {
    
    

	public static void main(String[] args) {
    
    
		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		//获取想要的目标对象,(代理对象)
		UserService us = (UserService)ctx.getBean("UserServiceProxy");
		boolean result = us.login("zs", "123");
		System.out.println("main最终结果"+result);
	}

}

UserService.java 代理模式接口

package com;


//代理模式的接口
public interface UserService {
    
    
	public boolean login(String loginName, String pwd);
	//public boolean regist(String loginName, String pwd);
}

UserServiceImpl.java 被代理类

package com;

import adivce.Md5Advice;
import util.Md5Encode;

//这是被代理类
public class UserServiceImpl implements UserService{
    
    
	//通过前置通知实现操作前密码的加密
	
	
	public boolean login(String loginName, String pwd) {
    
    	
		//登录的业务逻辑
		System.out.println("传过来的判断的名字是"+loginName);
		System.out.println("传过来的判断的密码是"+pwd);
		boolean result = "zs".equals(loginName) && "123".equals(pwd);
		System.out.println("登录的业务逻辑");
		return result;
	}
	
	public boolean regist(String loginName, String pwd) {
    
    
		//插入数据库之前数据需要加密
		System.out.println("注册成功");
		System.out.println("登陆成功,密码是:"+pwd);
		return true;
	}

}

Md5Encoding.java Md5工具类

package util;

public class Md5Encode {
    
    
	public static String getMD5(byte[] source) {
    
    
		String s = null;
		char hexDigits[] = {
    
     // 用来将字节转换成 16 进制表示的字符
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
		try {
    
    
			java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
			md.update(source);
			byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数,
										// 用字节表示就是 16 个字节
			char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
											// 所以表示成 16 进制需要 32 个字符
			int k = 0; // 表示转换结果中对应的字符位置
			for (int i = 0; i < 16; i++) {
    
     // 从第一个字节开始,对 MD5 的每一个字节
											// 转换成 16 进制字符的转换
				byte byte0 = tmp[i]; // 取第 i 个字节
				str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
															// >>> 为逻辑右移,将符号位一起右移
				str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
			}
			s = new String(str); // 换后的结果转换为字符串

		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
		return s;
	}
}

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <!-- 业务类配置 -->
    <bean id="UserServiceImpl" class="com.UserServiceImpl"/>
    <!-- 前置通知配置 -->
    <bean id="Md5Advice" class="adivce.Md5Advice"></bean>
	<!-- 代理工厂,class属性是固定的 ,.ProxyFactoryBean是代理对象-->
	<bean id="UserServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	   <!-- 注入接口属性,value=被代理对象接口 -->
	   <property name="proxyInterfaces" value="com.UserService"></property>
	   <!-- 注入目标属性,创建被代理对象 -->
	   <property name="target" ref="UserServiceImpl"></property>
	   <!-- 配置可以加入的通知 -->
	   <property name="interceptorNames">
	       <list>
	           <value>Md5Advice</value>
	       </list>
	   </property>
	</bean>
</beans>


后置通知

在前置通知的基础上加了个通知文件,修改了Bean。xml文件

项目结构

在这里插入图片描述

ScoreAdvice.java

package adivce;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class ScoreAdvice implements AfterReturningAdvice{
    
    

	
	//参数1=业务逻辑方法返回值,参数2,参数3=目标对象,参数4
	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
    
    
		System.out.println(arg0);
		System.out.println(arg3);
		System.out.println("增加100己分");
		System.out.println("==========================");
		
	}

}

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <!-- 业务类配置 -->
    <bean id="UserServiceImpl" class="com.UserServiceImpl"/>
    <!-- 前置通知配置 -->
    <bean id="Md5Advice" class="adivce.Md5Advice"></bean>
	<!-- 后通知设置 -->
	<bean id="ScoreAdvice" class="adivce.ScoreAdvice"></bean>
	
	
	
	<!-- 代理工厂,class属性是固定的 ,.ProxyFactoryBean是代理对象-->
	<bean id="UserServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	   <!-- 注入接口属性,value=被代理对象接口 -->
	   <property name="proxyInterfaces" value="com.UserService"></property>
	   <!-- 注入目标属性,创建被代理对象 -->
	   <property name="target" ref="UserServiceImpl"></property>
	   <!-- 配置可以加入的通知 -->
	   <property name="interceptorNames">
	       <list>
	           <value>Md5Advice</value>
	           <value>ScoreAdvice</value>
	       </list>
	   </property>
	</bean>
</beans>


POJO方式,基于XML

在这里插入图片描述

POJO方式,基于annotation

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44627608/article/details/115291195