代理模式——用AOP测试业务层方法的执行时间

Posted ggq-insist-qiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式——用AOP测试业务层方法的执行时间相关的知识,希望对你有一定的参考价值。

代理模式

对代理模式的理解,通过http://www.runoob.com/design-pattern/proxy-pattern.html

对AOP的代理模式,参考https://www.cnblogs.com/xujiming/p/5737531.html

目标:测试service层每一个方法的执行时间;

代理模式的本质:将被代理对象注入给代理对象,当调用代理对象时,实际上,是在调用被注入的被代理对象

理解:

代理对象 被代理对象
代理类 被代理对象
商家 厂商
房屋中介 房东

静态代理:代理类只有代理某一种类,不能代理任何一个类。

如何规范代理类和被代理类处理同样的业务?

让两个类实现相同的接口,这样同一个接口中的抽象方法就可以规范两个类实现相同的方法。

代码展示:

技术图片
//该类是被代理类,实现了LoginServiceI 接口
@Service
public class LoginServiceImpl implements LoginServiceI {
    
    @Autowired
    private LoginDaoI dao;
    
    @Override
    public Boolean checkLogin(User user, HttpServletRequest request) {
        Boolean flag = false;
        User u = dao.checkLogin(user);
        
        if(u!=null){
            request.getSession().setAttribute("userInfo", u);
            flag = true;
        }
        return flag;
    }

    @Override
    public void getPower(HttpServletRequest request) throws IOException {
        User u = (User) request.getSession().getAttribute("userInfo");
        List<Power> list = dao.getPowerByUid(u);
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(list);
        request.setAttribute("json", json);
    }
}
被代理类
技术图片
//该类是代理类,实现了LoginServiceI 接口
@Service
public class LoginServiceProxy implements LoginServiceI {
    
    @Autowired
    @Qualifier("loginServiceImpl")//防止注入失败报错
    private LoginServiceI loginService;

    @Override
    public Boolean checkLogin(User user, HttpServletRequest request) {
        long t1 = System.currentTimeMillis();
        Boolean flag = loginService.checkLogin(user, request);
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1+"ms");//计算出执行完成后的时间
        return flag;
    }

    @Override
    public void getPower(HttpServletRequest request) throws IOException {
        long t1 = System.currentTimeMillis();
        loginService.getPower(request);
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1+"ms");//计算出执行完成后的时间
    }
}
代理类

 

动态代理:(核心思想通过反射获取方法)代理类可以代理任何一个类。

JDK动态代理 :实现接口 InvocationHandler(java.lang.reflect.InvocationHanler反射包)  中的方法 

技术图片
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TimeProxy implements InvocationHandler {
    
    //被代理对象
    private Object obj;
    
    public TimeProxy(Object obj) {
        this.obj = obj;
    }

    //根据被代理对象生成代理对象
    public Object createProxy() {
        //Proxy.newProxyInstance(ClassLoader loader获取被代理对象的类加载器, Class<?>[] interfaces被代理对象所实现的接口, InvocationHandler h对象);
        return Proxy.newProxyInstance(this.obj.getClass().getClassLoader(), this.obj.getClass().getInterfaces(), this);
    }
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long t1 = System.currentTimeMillis();
        Object result = method.invoke(this.obj, args);
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1);
        return result;
    }

}
被代理类

在controller中调用

技术图片
@Controller
public class LoginController {
    
    @Autowired
    private LoginServiceI service;
    
    @RequestMapping("login")
    @ResponseBody
    public Boolean login(User user,HttpServletRequest request){
        TimeProxy proxy = new TimeProxy(service);
        LoginServiceI proxySerivce = (LoginServiceI) proxy.createProxy();
        return proxySerivce.checkLogin(user,request);
    }
}
在controller中调用被代理类

CGLib动态代理

 

通过AOP的切面实现——创建单独的类来进行管理。

package com.ggq.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//注解形式实现
@Component
@Aspect
public class TimeAspect {
    
//    在applicationContext.xml中添加配置  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    @Around("execution(* com.ggq.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint jp) throws Throwable {
        long t1 = System.currentTimeMillis();
        Object proceed = jp.proceed();
        Signature signature = jp.getSignature();
        System.out.println(signature);//User com.ggq.service.UserService.login(User)
        StaticPart staticPart = jp.getStaticPart();
        System.out.println(staticPart);//execution(User com.ggq.service.UserService.login(User))
        Object target = jp.getTarget();
        System.out.println(target);//[email protected]
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1);
        return proceed;
    }
    
    @Before("execution(* com.ggq.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("this is 前置 advice");
    }
    
    @After("execution(* com.ggq.service.*.*(..))")
    public void afterAdvice() {
        System.out.println("this is 最终  advice");
    }
    
    @AfterReturning("execution(* com.ggq.service.*.*(..))")
    public void afterReturingAdvice() {
        System.out.println("this is 后置 advice");
    }
    
    @AfterThrowing("execution(* com.ggq.service.*.*(..))")
    public void afterThrowingAdvice() {
        System.out.println("this is 异常 advice");
    }

}

如果不用注解@Component @Aspect @Around.......,可以再applicationContext.xml中配置标签

 

    <bean id="timeAspect" class="com.ggq.aspect.TimeAspect"></bean>
    
    <aop:config>
        <aop:aspect ref="timeAspect">
            <aop:pointcut expression="execution(* com.ggq.service.*.*(..))" id="pointcut"/>
            <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

 

工作中遇到测试执行时间的处理,如果再service中直接去计算,这样会造成不必要的输出,会影响日志,如果有计算,还会影响性能。
每个方法中有重复的代码,提高了代码的耦合度,不便于维护,所以可以通过AOP的切面来轻松实现。

个人意见,欢迎评论。

 

以上是关于代理模式——用AOP测试业务层方法的执行时间的主要内容,如果未能解决你的问题,请参考以下文章

Spring AOP入门基础-继承装饰者,代理的选择

Spring AOP JDK动态代理与CGLib动态代理区别

springAOP之代理模式

java动态代理中的invoke方法是如何被自动调用的

Spring中的Aop

Spring中的Aop