• 代理模式(CGLib动态代理实现aop切面)——用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 {
        
    //   如果是SpringMvc 配置 在applicationContext.xml中添加配置  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    // 如果是SpringBoot 在启动类中加注解 @EnableAspectJAutoProxy//开启AspectJ注解
    @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);//com.ggq.service.impl.UserServiceImpl@7c82a93e 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的切面来轻松实现。

    个人意见,欢迎评论。

  • 相关阅读:
    cmd修改系统时间
    军校退学
    What's New for Visual C# 6.0
    分批次获取git for windows的源代码
    Interpolated Strings
    Java写程序猿专访String2
    堆栈溢出分析
    javascript于&quot;return obj === void 0&quot;这样的书面理由和优势
    Java 内部类分析
    Android使用开发WebView战斗技能
  • 原文地址:https://www.cnblogs.com/Dylan_G/p/10524625.html
Copyright © 2020-2023  润新知