目 录CONTENT

文章目录

代理模式

在水一方
2022-02-01 / 0 评论 / 0 点赞 / 274 阅读 / 3,527 字 / 正在检测是否收录...

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能

生活的例子

在租房的时候,有的人会通过房东直租,有的人会通过中介租房。这两种情况哪种比较方便呢?当然是通过中介更加方便。这里的中介就相当于代理,用户通过中介完成租房的一系列操作(看房、交押金、租房、清扫卫生)代理模式可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码完全将具体的实现隐藏在内部。代理分为静态代理和动态代理

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实例对象传为参数传入;
  • 通过代理对象调用目标方法

image.png

📣Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。📣

JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

image.png

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);
    }
}

image.png

如果是用jdk动态代理,那么切面类的方法由JdkDynamicAopProxy来调用

image.png

问题

AOP-如何实现拦截器链式调用(责任链模式)过程是怎样的?
Tomcat中的Filter就是使用了责任链模式这个过程怎么看?

参考地址:https://mp.weixin.qq.com/s/GT1-yrxJ5KF0xeMydbJDCQ

0

评论区