代理模式
对代理模式的理解,通过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); } }
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的切面来轻松实现。
个人意见,欢迎评论。