Github源码下载地址:https://github.com/chenxingxing6/sourcecode/tree/master/code-springaop
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
Aop相关专业术语:
- Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的Advice。
- Jointpoint(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它joint point。
- Pointcut(切点):表示一组 joint point,这些 joint point或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
- Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通before、after 和around 来区别是在每个 joint point 之前、之后还是代替执行的代码。 Target(目标对象):织入 Advice的目标对象.。
- Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
AOP底层使用动态代理实现。包括两种方式:
使用JDK动态代理实现。
使用cglib来实现
通知类型
前置通知:在方法之前执行
后置通知:在方法之后执行
异常通知:方法出现异常执行
最终通知:在后置之后执行
环绕通知:在方法之前和之后执行
1.前置通知,后置返回通知,环绕通知,后置通知,异常通知
2.自己实现一个简单版IOC容器
3.通过动态代理 + 通知注解类实现
4.定义切入点,正则表达式 @pointcut
服务启动时,将项目中的bean注入到容器里面,对应有切面进行代理的bean,重新生成代理对象,替换原理的对象。
①被代理类;
②被代理类要实现的接口;
③代理类;
④动态创建“代理类的对象”的类;
⑤注解类:
a. 切面注解类,注解在类上:@Aspect
b. 各种通知注解,注解在方法上:
__@Before
__@AfterReturning
__@After
__@AfterThrowing
__@Around
⑥IOC容器:BeanFactory
本项目配置切面,只支持@MyPointcut方式,支持正则表达式;然后将pointcut放到对应切面通知上。
1.编写Aspect切面
2.测试
只支持jdk动态代理
package com.demo1;
import com.aop.annotation.*;
import com.aop.aspect.AbstractAspect;
import com.aop.aspect.JoinPoint;
import com.ioc.annotation.MyComponent;
/**
* @Author: cxx
* @Date: 2019/10/3 11:25
* 日志切面
*/
@MyAspect
@MyComponent
public class LogAspect extends AbstractAspect{
@MyPointcut("com.demo1.*")
private void myPointcut(){
}
@MyAround(value = "myPointcut()")
public Object around(JoinPoint joinPoint){
Long start = System.currentTimeMillis();
System.out.println("环绕通知start....");
Object obj= joinPoint.proceed();
System.out.println("环绕通知end....");
Long end = System.currentTimeMillis();
System.out.println("执行方法耗时:" + String.valueOf(end - start));
return obj;
}
}
package com.aop.core;
import com.aop.aspect.AspectHandler;
import com.aop.aspect.CheckResult;
import com.aop.aspect.IAspectHandler;
import com.ioc.core.MyIoc;
import javafx.util.Pair;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: cxx
* @Date: 2019/10/3 13:45
*/
public class AopBeanContext {
private static IAspectHandler aspectHandler = null;
static {
init();
}
public static Object getObject(Class clz){
Object target = MyIoc.getObject(clz);
if (target == null){
throw new RuntimeException("容器中获取不到实例");
}
Pair<Object, CheckResult> aspectResult = aspectHandler.getAspectInstance(clz.getTypeName());
// 没有切面
if (aspectResult == null){
return target;
}
// 创建代理类
return ProxyBeanFactory.newProxyBean(target, aspectResult);
}
private static void init(){
createProxyBeanContext(MyIoc.getBeanFactory());
}
// 根据切点,创建有代理类的bean容器
public static void createProxyBeanContext(Map<String, Object> iocMap){
aspectHandler = new AspectHandler(iocMap);
}
}
package com.aop.core;
import com.aop.annotation.AdviceEnum;
import com.aop.aspect.AbstractAspect;
import com.aop.aspect.CheckResult;
import com.aop.aspect.JoinPoint;
import javafx.util.Pair;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author: cxx
* @Date: 2019/10/3 15:35
* @Desc: 环绕通知先不实现
*/
public class BeanInvocationHandler implements InvocationHandler {
// 目标类
private Object target;
// 切面相关信息
private Pair<Object, CheckResult> acpectResult;
public BeanInvocationHandler(Object target, Pair<Object, CheckResult> acpectResult) {
this.target = target;
this.acpectResult = acpectResult;
}
/**
* 内部类实现环绕通知
*/
class MyJoinPoint implements JoinPoint{
private Method method;
private Object[] args;
public MyJoinPoint(Method method, Object[] args) {
this.method = method;
this.args = args;
}
@Override
public Object proceed() {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 切面类
AbstractAspect aspcet = (AbstractAspect) acpectResult.getKey();
// 通知方式
CheckResult runType = acpectResult.getValue();
boolean isAround = runType.isRunAround();
JoinPoint joinPoint = null;
if (isAround){
joinPoint = new MyJoinPoint(method, args);
}
Object result = null;
try {
// 1.前置通知
if (runType.isRunBefore()){
runAspectInstance(AdviceEnum.BEFORE, aspcet, args);
}
// 2.环绕通知
if (isAround){
result = aspcet.around(joinPoint);
}else {
result = method.invoke(target, args);
}
// 3.返回通知
if (runType.isRunAfterReturning()){
runAspectInstance(AdviceEnum.AFTER_RETURNING, aspcet, args, result);
}
return result;
}catch (Exception e){
// 4.异常通知
if (runType.isRunAfterThrowing()){
runAspectInstance(AdviceEnum.AFTER_THROWING, aspcet, args, e);
}
}finally {
// 5.后置通知
if (runType.isRunAfter()){
runAspectInstance(AdviceEnum.AFTER, aspcet, args);
}
}
return result;
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args){
this.runAspectInstance(adviceEnum, aspect, args, null, null);
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Object result){
this.runAspectInstance(adviceEnum, aspect, args, null, result);
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Throwable e){
this.runAspectInstance(adviceEnum, aspect, args, e, null);
}
/**
* 执行切面实例
* @param adviceEnum
* @param aspect
* @param args
*/
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Throwable e, Object result){
try {
switch (adviceEnum){
case BEFORE:{
aspect.before();
break;
}
case AFTER_RETURNING:{
aspect.afterReturning(result);
break;
}
case AFTER_THROWING:{
aspect.afterThrowable(e);
break;
}
case AFTER:{
aspect.after();
break;
}
default:{
break;
}
}
}catch (Exception ee){
ee.printStackTrace();
}
}
}
package com.aop;
import com.aop.core.AopBeanContext;
import com.demo1.ILogService;
import com.demo1.LogService;
/**
* @Author: cxx
* @Date: 2019/10/3 11:34
*/
public class MainTest {
public static void main(String[] args) {
// 01.测试 修改ioc.properties scan.package=com.demo
//IUserService userService = (IUserService) AopBeanContext.getObject(UserService.class);
//userService.delete("100");
// 02.环绕通知测试 修改ioc.properties scan.package=com.demo1
ILogService logService = (ILogService) AopBeanContext.getObject(LogService.class);
logService.printLog("test....");
}
}