代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能
生活的例子
在租房的时候,有的人会通过房东直租,有的人会通过中介租房。这两种情况哪种比较方便呢?当然是通过中介更加方便。这里的中介就相当于代理,用户通过中介完成租房的一系列操作(看房、交押金、租房、清扫卫生)代理模式可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码完全将具体的实现隐藏在内部。代理分为静态代理和动态代理
JDK静态代理
静态代理:在编译时就已经实现,编译完成后代理类是一个实际的class文件
示例:
定义一个接口
public interface UserDao {
void save();
}
定义一个接口的实现类(目标对象)
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("目标方法的执行");
}
}
定义一个代理对象
public class UserDaoProxy implements UserDao {
// 这里需要考虑的一点是代理对象需要执行目标对象的方法,需要把目标对象注入进来,通过构造函数来赋值
private UserDao userDao;
public UserDaoProxy(UserDao target){
this.userDao = target;
}
@Override
public void save() {
// 这里可以开启事务控制
System.out.println("开始事务");
userDao.save();
System.out.println("结束事务");
}
}
测试
@Test
public void proxyTest(){
UserDaoImpl dao = new UserDaoImpl();
UserDaoProxy proxy = new UserDaoProxy(dao);
proxy.save();
}
}
打印的结果:
开始事务
目标方法的执行
结束事务
由于JDK静态代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐
动态代理:
在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
说到动态代理不得不提java.lang.reflect包下Proxy这个类Proxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
JDK动态代理是利用反射机制在运行时创建代理类,在动态代理中,核心是InvocationHandler(在java.lang.reflect包下)
使用JDK动态代理的步骤
- 通过实现InvocationHandler接口来自定义自己的InvocationHandler(可参照JdkDynamicAopProxy类);
- 通过Proxy.getProxyClass获得动态代理类;
- 通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class);
- 通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入;
- 通过代理对象调用目标方法
📣Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。📣
JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
Aop中通过注解来构建日志切面的案例
@Order(1)
@Aspect
@Component
public class SysLogAspect {
private Logger log=Logger.getLogger(SysLogAspect.class);
@Autowired
private SysLogDao sysLogDao;
@Pointcut("@annotation(com.jt.common.annotation.RequestLog)")
public void logPointCut(){
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint jointPoint) throws Throwable{
long startTime=System.currentTimeMillis();
//执行目标方法(result为目标方法的执行结果)
Object result=jointPoint.proceed();
long endTime=System.currentTimeMillis();
long totalTime=endTime-startTime;
log.info("方法执行的总时长为:"+totalTime);
saveSysLog(jointPoint,totalTime);
return result;
}
private void saveSysLog(ProceedingJoinPoint point,
long totleTime) throws NoSuchMethodException, SecurityException, JsonProcessingException{
//1.获取日志信息
MethodSignature ms=
(MethodSignature)point.getSignature();
Class<?> targetClass=
point.getTarget().getClass();
String className=targetClass.getName();
//获取接口声明的方法
String methodName=ms.getMethod().getName();
Class<?>[] parameterTypes=ms.getMethod().getParameterTypes();
//获取目标对象方法
Method targetMethod=
targetClass.getDeclaredMethod(methodName,parameterTypes);
//获取登陆用户
String username=
ShiroUtils.getPrincipal().getUsername();
//获取方法参数
Object[] paramsObj=point.getArgs();
System.out.println("paramsObj="+paramsObj);
//将参数转换为字符串
String params=new ObjectMapper()
.writeValueAsString(paramsObj);
//2.封装日志信息
SysLog log=new SysLog();
log.setUsername(username);//登陆的用户
//假如目标方法对象上有注解,我们获取注解定义的操作值
RequestLog requestLog=
targetMethod.getDeclaredAnnotation(RequestLog.class);
log.setOperation(requestLog.value());
log.setMethod(className+"."+methodName);//className.methodName()
log.setParams(params);//method params
log.setIp(IPUtils.getIpAddr());//ip 地址
log.setTime(totleTime);//
log.setCreatedTime(new Date());
//3.保存日志信息
sysLogDao.insertObject(log);
}
}
如果是用jdk动态代理,那么切面类的方法由JdkDynamicAopProxy来调用
问题
AOP-如何实现拦截器链式调用(责任链模式)过程是怎样的?
Tomcat中的Filter就是使用了责任链模式这个过程怎么看?
评论区