文章目录
AOP概述
AOP是基于代理模式实现的。
AOP=面向切面编程
现在主流是OOP。面向对象编程。
AOP是OOP的补充。
一般使用OOP的条件
- 多用于功能性代码而不是业务性代码
- 多用于公用功能方面
- 关注点(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>