SpringAOP

AOP是在容器级BeanPostProcessor接口的postProcessAfterInitialization方法中监控每一个Bean初始化完成后,为其生成代理对象,并用代理对象替换原容器的Bean。

 

与拦截器不同,拦截器通过preHandle返回的boolean值判断是否继续,且拦截器永远早于AOP执行

AOP可以通过异常终止代码,并且@ControllerAdvice注解可以捕获AOP中抛出的异常做统一处理。

 

失效

https://blog.csdn.net/u012373815/article/details/77345655

https://blog.csdn.net/shanchahua123456/article/details/89766116

保证做到一下几点:被包裹的方法是public,在对象内部的方法中调用该对象的其他使用aop机制的方法,被调用方法的aop注解失效。避免在类内部方法直接调用。

缓存失效用例:

同一个类中的方法直接调用 

public class TService{

//在同一个类中的方法,调用 aop注解(@Cacheable 注解也是aop 注解) 的方法,会使aop 注解失效  
public User getUser(Integer id){
    //此时注解失效,getUserById 方法不会去缓存中查询数据,会直接查询数据库。
    return getUserById(id);
}


//使用缓存,查询时先查询缓存,缓存中查询不到时,调用数据库。
@Cacheable(value = "User")
public User getUserById(Integer id){
        System.out.println("查询数据库");
    return UserDao.getUserById(id);
}

}

解决方案

1 避免避免类内部方法直接调用。最简单彻底,但是要修改代码逻辑

2 注入当前ApplicationContext,注入BeanName,通过Spring上下文获取当前bean,用当前bean调用方法,因为容器中的Bean是已经被替换过的代理对象,所以包含AOP。


@Service
public class TicketService  implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext){
            applicationContext = applicationContext;
    }


    //买火车票
    public void buyTrainTicket(Ticket ticket){
        System.out.println("买到了火车票");
        try {
           //通过本类Bean    
           applicationContext.getBean(this.getClass()).sendMessage();

        } catch (Exception  e) {
            logger.warn("发送消息异常");
        }
    }

    @Transactional
    public void sendMessage(){
        System.out.println("消息存入数据库");
        System.out.println("执行发送消息动作");
    }
}

拿到代理类对象,再调用本类中B方法。(T)AopContext.currentProxy().B()

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
//XML 新增如下语句;先开启cglib代理,开启 exposeProxy = true,暴露代理对象

public class TicketService{

    //买火车票
    public void buyTrainTicket(Ticket ticket){
        System.out.println("买到了火车票");
        try {

           //通过代理对象去调用sendMessage()方法          
           (TicketService)AopContext.currentProxy().sendMessage();

        } catch (Exception  e) {
            logger.warn("发送消息异常");
        }
    }

    @Transactional
    public void sendMessage(){
        System.out.println("消息存入数据库");
        System.out.println("执行发送消息动作");
    }
}

 

1 JoinPoint 

JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据:

package org.aspectj.lang;  
import org.aspectj.lang.reflect.SourceLocation;  
public interface JoinPoint {  
    String toString();         //连接点所在位置的相关信息  
    String toShortString();     //连接点所在位置的简短相关信息  
    String toLongString();     //连接点所在位置的全部相关信息  
    Object getThis();         //返回AOP代理对象  
    Object getTarget();       //返回目标对象  
    Object[] getArgs();       //返回被通知方法参数列表  
    Signature getSignature():MethodSignature;  //返回当前连接点签名  
    methodSignature.getMethod():Method;   //返回被通知方法 
    SourceLocation getSourceLocation();   //返回连接点方法所在类文件中的位置  
    String getKind();        //连接点类型  
    StaticPart getStaticPart();    //返回连接点静态部分  
}  

ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法:

public interface ProceedingJoinPoint extends JoinPoint {  
    public Object proceed() throws Throwable;  
    public Object proceed(Object[] args) throws Throwable;  //传入修改后参数,执行目标方法
}  
@Around("execution(* com.abc.service.*.many*(..))")
    public Object process(ProceedingJoinPoint point) throws Throwable {
        //访问目标方法的参数:
        Object[] args = point.getArgs();
        if (args != null && args.length > 0  && args[0].getClass() == String.class) {
            args[0] = "改变后的参数1";
        }
        //用改变后的参数执行目标方法
        Object returnValue = point.proceed(args);
        System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
        return "原返回值:" + returnValue + ",这是返回结果的后缀";
    }

/*
判断参数类型
arg[0].getClass().getTypeName().equals("java.lang.String");
arg[0].getClass().getTypeName().equals("int");
args[0].getClass() == String.class
*/

2 AOP中获取request和response对象

RequestContextHolder是SpringMVC环境中的请求线程上下文,适用于SpringMVC中的任意方法中。

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

3 取得执行目标方法


Signature signature = proceedingJoinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;    
Method targetMethod = methodSignature.getMethod();
Method realMethod=proceedingJoinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes()); 
       

4 Round AOP切面包裹整个方法执行过程,且整个过程在同一线程下完成

 AOP前面可以做日志、计时、鉴权、验证、后处理等等操作。

@Around("@annotation(sysLog)")
	public Object around(ProceedingJoinPoint point, SysLog sysLog) throws Throwable {
		String strClassName = point.getTarget().getClass().getName();
		String strMethodName = point.getSignature().getName();
		log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);

		SysLog logVo = SysLogUtils.getSysLog();
		logVo.setTitle(sysLog.value());
		Long startTime = System.currentTimeMillis();
		Object obj = point.proceed(); //方法执行
		Long endTime = System.currentTimeMillis();
		logVo.setTime(endTime - startTime);
		// 发送异步日志事件
		applicationContext.publishEvent(new SysLogEvent(logVo));
		return obj;
	}

5 执行顺序

https://blog.csdn.net/qq_32331073/article/details/80596084#commentBox

在同一个切面类中

  
        
  在不同切面类中:@Order的值越小越优先。先入后出,后入先出。@Order 默认为最低优先级Integer.MAX_VALUE

事务切面优先级:默认为最低优先级 Integer.MAX_VALUE

posted @ 2018-11-23 23:56  sw008  阅读(143)  评论(0编辑  收藏  举报