Spring(四):SpringAOP原理和源码解析

AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码。

面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑。

通过定义和前面代码我们可以发现3点:

    1.AOP是基于动态代理模式。

    2.AOP是方法级别的(要测试的方法不能为static修饰,因为接口中不能存在静态方法,编译就会报错)。

    3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。

 

AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging  调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence  持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

Spring(四):SpringAOP原理和源码解析

 

AOP联盟将AOP体系分为三层,从三层结构可以看出,AOP实现方式有很多种,包括反射、元数据处理、程序处理、拦截器处理等,通过本节学习,你就会看到Spring AOP的实现使用的是Java语言本身的特性,即Java Proxy代理类、拦截器技术实现。

Spring AOP何时调用

有很多朋友不愿意去看AOP源码的一个很大原因是因为找不到AOP源码实现的入口在哪里,这个确实是。不过我们可以看一下上面的测试代码,就普通Bean也好、AOP也好,最终都是通过getBean方法获取到Bean并调用方法的,getBean之后的对象已经前后都打印了TimeHandler类printTime()方法里面的内容,可以想见它们已经是被Spring容器处理过了。

既然如此,那无非就两个地方处理:

  1. 加载Bean定义的时候应该有过特殊的处理
  2. getBean的时候应该有过特殊的处理

方法主要做了三件事:

  1. 根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义
  2. 将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义
  3. 将advisorDefinition注册到DefaultListableBeanFactory中
  4. AOP Bean定义加载----AopNamespaceHandler处理<aop:pointcut>流程

Spring AOP代理对象的生成

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。

动态代理JdkDynamicAopProxy实现方式

/** 
    * <ol> 
    * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false) 
    * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口 
    * <li>调用Proxy.newProxyInstance创建代理对象 
    * </ol> 
    */  
   public Object getProxy(ClassLoader classLoader) {  
       if (logger.isDebugEnabled()) {  
           logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());  
       }  
       Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);  
       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
}  

如何织入

我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {  
       MethodInvocation invocation = null;  
       Object oldProxy = null;  
       boolean setProxyContext = false;  
   
       TargetSource targetSource = this.advised.targetSource;  
       Class targetClass = null;  
       Object target = null;  
   
       try {  
           //eqauls()方法,具目标对象未实现此方法  
           if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){  
                return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);  
           }  
   
           //hashCode()方法,具目标对象未实现此方法  
           if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){  
                return newInteger(hashCode());  
           }  
   
           //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知  
           if (!this.advised.opaque &&method.getDeclaringClass().isInterface()  
                    &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
                // Service invocations onProxyConfig with the proxy config...  
                return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);  
           }  
   
           Object retVal = null;  
   
           if (this.advised.exposeProxy) {  
                // Make invocation available ifnecessary.  
                oldProxy = AopContext.setCurrentProxy(proxy);  
                setProxyContext = true;  
           }  
   
           //获得目标对象的类  
           target = targetSource.getTarget();  
           if (target != null) {  
                targetClass = target.getClass();  
           }  
   
           //获取可以应用到此方法上的Interceptor列表  
           List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);  
   
           //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)  
           if (chain.isEmpty()) {  
                retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
           } else {  
                //创建MethodInvocation  
                invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
                retVal = invocation.proceed();  
           }  
   
           // Massage return value if necessary.  
           if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)  
                    &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
                // Special case: it returned"this" and the return type of the method  
                // is type-compatible. Notethat we can't help if the target sets  
                // a reference to itself inanother returned object.  
                retVal = proxy;  
           }  
           return retVal;  
       } finally {  
           if (target != null && !targetSource.isStatic()) {  
                // Must have come fromTargetSource.  
               targetSource.releaseTarget(target);  
           }  
           if (setProxyContext) {  
                // Restore old proxy.  
                AopContext.setCurrentProxy(oldProxy);  
           }  
       }  
    }  

主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。

AOP的使用

XML方式实现AOP

1 public interface Dao {
2     
3     public void select();
4 
5     public void insert();
6     
7 }

Dao接口的实现类DaoImpl:

 1 public class DaoImpl implements Dao {
 2 
 3     @Override
 4     public void select() {
 5         System.out.println("Enter DaoImpl.select()");
 6     }
 7 
 8     @Override
 9     public void insert() {
10         System.out.println("Enter DaoImpl.insert()");
11     }
12     
13 }

定义一个TimeHandler,用于方法调用前后打印时间,在AOP中,这扮演的是横切关注点的角色:

1 public class TimeHandler {
2 
3     public void printTime() {
4         System.out.println("CurrentTime:" + System.currentTimeMillis());
5     }
6     
7 }

定义一个XML文件aop.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/aop
 9         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
10 
11     <bean id="daoImpl" class="org.xrq.action.aop.DaoImpl" />
12     <bean id="timeHandler" class="org.xrq.action.aop.TimeHandler" />
13 
14     <aop:config proxy-target-class="true">
15         <aop:aspect id="time" ref="timeHandler">
16             <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.action.aop.Dao.*(..))" />
17             <aop:before method="printTime" pointcut-ref="addAllMethod" />
18             <aop:after method="printTime" pointcut-ref="addAllMethod" />
19         </aop:aspect>
20     </aop:config>
21     
22 </beans>

写一段测试代码TestAop.java:

 1 public class TestAop {
 2 
 3     @Test
 4     public void testAop() {
 5         ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");
 6         
 7         Dao dao = (Dao)ac.getBean("daoImpl");
 8         dao.select();
 9     }
10     
11 }

代码运行结果就不看了,有了以上的内容,我们就可以根据这些跟一下代码,看看Spring到底是如何实现AOP的。

 

自定义注解实现AOP
最近项目中需要记录日志,跟大家想的一样 ,利用spring aop去实现,之前也参考了一些代码,自己实现了一套设计,供大家参考。

之前看到网上很多是基于切面类Aspect去实现了,在切面类中定义before after around等逻辑以及要拦截等方法。本文利用注解实现了一套可以扩展等日志记录模块。

1. 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public abstract @interface RequiredLogInterceptor {
    boolean required() default true;
 
    String targetGenerator() default  "";
 
    OperateType  operateType() default  OperateType.GET;
}
requried:注解是否生效

targetGenerator: 每个模块记录等内容不同,入口参数不同,所以需要个性化定制日志等记录内容,每个模块的日志生成有自己定义的generator类,并且重写generateContent方法。

operateType:当前方法是增加,删除,还是修改

public abstract class ContentGerator {
 
    public   static String SPLIT="/";
 
    public  static String CONTENT_SPLIT=",";
 
    public  static String VALUE_SPLIT=":";
 
    abstract  List<UserOperateLog> generateContent(Object returnValue, Object[] args, OperateType operateType);
}

2. 定义拦截器
本模块主要是后置通知,主要逻辑如下:

1.拦截方法,判断是否有注解loginterceptor

2. 如果有判断是否执行成功,成功则记录log,失败不记录

3. 获取注解中配置的generator类,利用反射调用generateContent方法,生成个性化日志内容

5.在日志中添加其他公共属性,比如用户id,创建时间等等。所有个性化定制的日志信息都是在generator类中产生。



public class LogAfterInterceptor implements AfterReturningAdvice {
 
    @Autowired
    private LogService logService;
 
 
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        RequiredLogInterceptor requiredLogInterceptor = AnnotationUtils.findAnnotation(method, RequiredLogInterceptor.class);
        if (requiredLogInterceptor != null) {
            if(returnValue!=null&&returnValue instanceof  Response){
                Response response=(Response)returnValue;
                String code=response.getCode();
                String code200= MegCodeEnums.ResponseCodeEnum.C200.getCode();
                String code201= MegCodeEnums.ResponseCodeEnum.C201.getCode();
                if (!Strings.isNullOrEmpty(code)&&!code.equalsIgnoreCase(code200)&&!code.equalsIgnoreCase(code201)){
                    return;
                }
 
            }
            String targetGeneratorName=requiredLogInterceptor.targetGenerator();
            OperateType operateType=requiredLogInterceptor.operateType();
            Class targetGeneratorclass=Class.forName("com.puhui.flowplatform.manage.log."+targetGeneratorName);
            Method executeMethod=targetGeneratorclass.getMethod("generateContent",Object.class,Object[].class,OperateType.class);
            ContentGerator targetGeneratorBean=(ContentGerator)SpringContextHolder.getBean(targetGeneratorclass);
            List<UserOperateLog> userOperateLogList=(List<UserOperateLog>)executeMethod.invoke(targetGeneratorBean,returnValue,args,operateType);
            if(CollectionUtils.isNotEmpty(userOperateLogList)){
                userOperateLogList.forEach(userOperateLog -> {
                    userOperateLog.setCreateTime(new Date());
                    //token
                    long userId=0L;
                    if (args.length>0&&args[0] instanceof String){
                        userId = CommonUtils.getManageCurUserId(args[0].toString());
                    }
                    userOperateLog.setUserId(userId);
                });
                logService.batchInsertLog(userOperateLogList);
            }
 
        }
    }
 
}
3 Generator类
继承统一的ContentGenerator类,便于共享一些常量。根据当前操作类型,生成对应的日志内容就可以了。如果需要新增模块, 先定义自己的日志generator类,然后添加注解到对应模块就可以。

@Service
public class ContentGeneratorForRoleMgt extends   ContentGerator {
 
 
    @Autowired
    private MenuService menuService;
 
    private  String generateMenus(VoRole voRole){
        List<Menus> menusList=voRole.getMenusList();
        StringBuffer stringBuffer=new StringBuffer();
        if (CollectionUtils.isNotEmpty(menusList)){
            menusList.forEach(menus -> {
                Long menuId=menus.getId();
                Menus menusTemp=menuService.queryMenuByMenuId(menuId);
                stringBuffer.append(menusTemp.getDisplayTitle()+CONTENT_SPLIT);
            });
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        }
        return  stringBuffer.toString();
 
    }
    @Override
    public    List<UserOperateLog> generateContent(Object returnValue, Object[] args, OperateType operateType) {
        {
            List<UserOperateLog> userOperateLogList=new ArrayList<>();
            UserOperateLog userOperateLog=new UserOperateLog();
            if (operateType==OperateType.ADD||operateType==OperateType.UPDATE){
                VoRole voRole=(VoRole)args[1];
                String menus=generateMenus(voRole);
                userOperateLog.setOperateContent("角色名称"+VALUE_SPLIT+voRole.getDisplayName()+SPLIT+"权限"+VALUE_SPLIT+menus);
                userOperateLog.setOperateType(operateType==OperateType.ADD?LogOperateTypeEnum.ADD_ROLE.getCode():LogOperateTypeEnum.UPDATE_ROLE.getCode());
            }
            if (operateType==OperateType.DELETE){
                if(returnValue!=null){
                    Response response=(Response) returnValue;
                    String roleName=response.getData().toString();
                    userOperateLog.setOperateContent(roleName);
                    userOperateLog.setOperateType(LogOperateTypeEnum.DELETE_ROLE.getCode());
                }
 
            }
            userOperateLogList.add(userOperateLog);
            return  userOperateLogList;
 
        }
 
    }
}
4. 注解应用
 @PutMapping(value = "roles/{roleId}")
    @RequiredLogInterceptor(targetGenerator = "ContentGeneratorForRoleMgt",operateType= OperateType.UPDATE)
    @ApiOperation(value = "修改角色", httpMethod = "PUT", response = Response.class, notes = "修改角色")
    public Response<Object> updateRole(@RequestHeader String token,@RequestBody VoRole voRole, @PathVariable("roleId") String roleId) {
        LOGGER.info("updateRole入参:{}", JSONObject.toJSONString(voRole));
5. Configuration
public class SpringMvcConfig extends WebMvcConfigurerAdapter {
 
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        // 初始化转换器
        FastJsonHttpMessageConverter fastConvert = new FastJsonHttpMessageConverter();
        // 初始化一个转换器配置
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        // 将配置设置给转换器并添加到HttpMessageConverter转换器列表中
        fastConvert.setFastJsonConfig(fastJsonConfig);
        converters.add(fastConvert);
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations(
                ResourceUtils.CLASSPATH_URL_PREFIX + "/META-INF/resources/");
        registry.addResourceHandler("/static/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/static/",
                ResourceUtils.CLASSPATH_URL_PREFIX + "/dist/static/");
        registry.addResourceHandler("/page/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/dist/");
        super.addResourceHandlers(registry);
    }
 
    @Bean
    public ViewResolver viewResolver() {
        FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
        resolver.setCache(true);
        resolver.setPrefix(ResourceUtils.CLASSPATH_URL_PREFIX + "templates/");
        resolver.setSuffix(".ftl");
        resolver.setContentType("text/html; charset=UTF-8");
        return resolver;
    }
 
    // 创建Advice或Advisor
    @Bean
    public BeforeAdvice beforeControllerInterceptor() {
        return new BeforeControllerInterceptor();
    }
    @Bean
    public AfterAdvice logAfterInterceptor() {
        return new LogAfterInterceptor();
    }
    // 创建Advice或Advisor
    @Bean
    public BeforeAdvice logBeforeInterceptor() {
        return new LogBeforeInterceptor();
    }
 
    // 使用BeanNameAutoProxyCreator来创建代理
 
    @Bean
    public BeanNameAutoProxyCreator beanAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setProxyTargetClass(true); // 设置要创建代理的那些Bean的名字
        beanNameAutoProxyCreator.setBeanNames("*Controller"); //
        // 设置拦截链名字(这些拦截器是有先后顺序的)
        beanNameAutoProxyCreator.setInterceptorNames("logAfterInterceptor");
        return beanNameAutoProxyCreator;
    }
 
    @Bean
    public BeanNameAutoProxyCreator beanBeforeAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setProxyTargetClass(true);
        // 设置要创建代理的那些Bean的名字
        beanNameAutoProxyCreator.setBeanNames("*Controller");
        // 设置拦截链名字(这些拦截器是有先后顺序的)
        beanNameAutoProxyCreator.setInterceptorNames("beforeControllerInterceptor");
        beanNameAutoProxyCreator.setInterceptorNames("logBeforeInterceptor");
通过注解实现AOP
package com.puhui.flowplatform.manage.interceptor;
 
import com.puhui.flowplatform.common.model.manage.UserOperateLog;
import com.puhui.flowplatform.manage.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
 
import java.lang.reflect.Method;
import java.util.Date;
 
@Aspect
public class LogAspect {
 
	public Long id=null;
 
	@Autowired
	LogService logService;
 
	/**
	 * 添加业务逻辑方法切入点
	 */
	@Pointcut("execution(*  com.puhui.flowplatform.manage.service.*.add*(..))")
	public void insertCell() {
	}
 
	/**
	 * 修改业务逻辑方法切入点
	 */
	@Pointcut("execution(*  com.puhui.flowplatform.manage.service.*.update*(..))")
	public void updateCell() {
	}
 
	/**
	 * 删除业务逻辑方法切入点
	 */
	@Pointcut("execution(*  com.puhui.flowplatform.manage.service.*.delete*(..))")
	public void deleteCell() {
	}
 
 
	/**
	 * 添加操作日志(后置通知)
	 *
	 * @param joinPoint
	 * @param object
	 */
	@AfterReturning(value = "insertCell()", argNames = "object", returning = "object")
	public void insertLog(JoinPoint joinPoint, Object object) throws Throwable {
		// Admin admin=(Admin)
		// request.getSession().getAttribute("businessAdmin");
		// 判断参数
		if (joinPoint.getArgs() == null) {// 没有参数
			return;
		}
		// 获取方法名
		String methodName = joinPoint.getSignature().getName();
		// 获取操作内容
		String opContent = optionContent(joinPoint.getArgs(), methodName);
 
		UserOperateLog log = new UserOperateLog();
		log.setOperateContent(opContent);
		log.setUserId(id);;
		log.setOperateType(1);//enum 增加
		log.setCreateTime(new Date());
		logService.insertLog(log);
	}
 
	/**
	 * 管理员修改操作日志(后置通知)
	 *
	 * @param joinPoint
	 * @param object
	 * @throws Throwable
	 */
	@AfterReturning(value = "updateCell()", argNames = "object", returning = "object")
	public void updateLog(JoinPoint joinPoint, Object object) throws Throwable {
		// Admin admin=(Admin)
		// request.getSession().getAttribute("businessAdmin");
 
		// 判断参数
		if (joinPoint.getArgs() == null) {// 没有参数
			return;
		}
		// 获取方法名
		String methodName = joinPoint.getSignature().getName();
		// 获取操作内容
		String opContent = optionContent(joinPoint.getArgs(), methodName);
 
		// 创建日志对象
		UserOperateLog log = new UserOperateLog();
		log.setOperateContent(opContent);
		log.setUserId(id);;
		log.setOperateType(2);//enum 修改
		log.setCreateTime(new Date());
		logService.insertLog(log);
	}
 
	/**
	 * 删除操作
	 *
	 * @param joinPoint
	 * @param object
	 */
	@AfterReturning(value = "deleteCell()", argNames = "object", returning = "object")
	public void deleteLog(JoinPoint joinPoint, Object object) throws Throwable {
		// Admin admin=(Admin)
		// request.getSession().getAttribute("businessAdmin");
		// 判断参数
		if (joinPoint.getArgs() == null) {// 没有参数
			return;
		}
		// 获取方法名
		String methodName = joinPoint.getSignature().getName();
 
		StringBuffer rs = new StringBuffer();
		rs.append(methodName);
		String className = null;
		for (Object info : joinPoint.getArgs()) {
			// 获取对象类型
			className = info.getClass().getName();
			className = className.substring(className.lastIndexOf(".") + 1);
			rs.append("[参数,类型:" + className + ",值:(id:"
					+ joinPoint.getArgs()[0] + ")");
		}
 
		// 创建日志对象
		UserOperateLog log = new UserOperateLog();
		log.setOperateContent(rs.toString());
		log.setUserId(id);;
		log.setOperateType(3);//删除
		log.setCreateTime(new Date());
		logService.insertLog(log);
	}
 
	/**
	 * 使用Java反射来获取被拦截方法(insert、update)的参数值, 将参数值拼接为操作内容
	 *
	 * @param args
	 * @param mName
	 * @return
	 */
	public String optionContent(Object[] args, String mName) {
		if (args == null) {
			return null;
		}
		StringBuffer rs = new StringBuffer();
		rs.append(mName);
		String className = null;
		int index = 1;
		// 遍历参数对象
		for (Object info : args) {
			// 获取对象类型
			className = info.getClass().getName();
			className = className.substring(className.lastIndexOf(".") + 1);
			rs.append("[参数" + index + ",类型:" + className + ",值:");
			// 获取对象的所有方法
			Method[] methods = info.getClass().getDeclaredMethods();
			// 遍历方法,判断get方法
			for (Method method : methods) {
				String methodName = method.getName();
				// 判断是不是get方法
				if (methodName.indexOf("get") == -1) {// 不是get方法
					continue;// 不处理
				}
				Object rsValue = null;
				try {
					// 调用get方法,获取返回值
					rsValue = method.invoke(info);
				} catch (Exception e) {
					continue;
				}
				// 将值加入内容中
				rs.append("(" + methodName + ":" + rsValue + ")");
			}
			rs.append("]");
			index++;
		}
		return rs.toString();
	}}

 

 总结

根据我们常用的XML配置

 <?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"
      xmlns:tx="http://www.springframework.org/schema/tx"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
     <bean id="daoImpl" class="org.xrq.action.aop.DaoImpl" />
     <bean id="timeHandler" class="org.xrq.action.aop.TimeHandler" />
 
     <aop:config proxy-target-class="true">
         <aop:aspect id="time" ref="timeHandler">
             <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.action.aop.Dao.*(..))" />
             <aop:before method="printTime" pointcut-ref="addAllMethod" />
             <aop:after method="printTime" pointcut-ref="addAllMethod" />
         </aop:aspect>
     </aop:config>
 </beans>

AOP初始化流程

1.读取配置文件XML解析为了org.w3c.dom.Document,org.w3c.dom.Document以树的形式表示整个XML,具体到每一个节点就是一个Node。

2.解析<aop:config>标签  通过Node对应Namespace="http://www.springframework.org/schema/aop"获取对应的NamespaceHandler即Namespace处理器

3.根据织入方式将<aop:before>、<aop:after>转换成名为adviceDef的RootBeanDefinition将advisorDefinition注册到DefaultListableBeanFactory中

4.加载AopNamespaceHandler处理<aop:pointcut>流程获取<aop:pointcut>标签下的"id"属性与"expression"属性。

<aop:pointcut>标签对应解析出来的BeanDefinition是RootBeanDefinition中的Class是org.springframework.aop.aspectj.AspectJExpressionPointcut<aop:pointcut>标签对应的Bean是prototype即原型的

5.代理对象实例化判断是否为<bean>生成代理,目标类必须满足expression的匹配规则目标类中有一个方法必须满足expression的匹配规则<aop:config>这个节点中proxy-target-class="false"或者proxy-target-class不配置,即不使用CGLIB生成代理

6.创建AopProxy接口实现类

创建AopProxy接口实现类

通过AopProxy接口的实现类的getProxy方法获取<bean>对应的代理

平时我们说AOP原理三句话就能概括:

  1. 对类生成代理使用CGLIB
  2. 对接口生成代理使用JDK原生的Proxy
  3. 可以通过配置文件指定(proxy-target-class="true")对接口使用CGLIB生成代理

 

本片文章为总结而来,如有错误敬请指出