SpringAOP原理分析

SpringAOP使得代码关注点分离,不同的问题交给不同的部分去解决。将业务功能代码与切面代码分开,架构也变得高内聚,低耦合。

一.AOP的三种织入方式

  • 编译时织入:需要特殊的Java编译器,如AspectJ
  • 类加载时织入:需要特殊的Java编译器,如AspectJ和AspectWerkz
  • 运行时织入:Spring采用的方式,通过动态代理方式,实现简单

二.理解三个W

  • 理解如下代码
@Aspect
@Component
public class RequestLogAspect {
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    private static final Logger logger =
            LoggerFactory.getLogger(RequestLogAspect.class);

    @Pointcut("execution(public * com.whz.framework.web..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint){
        startTime.set(System.currentTimeMillis());
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("请求URL:" + request.getRequestURL().toString());
        logger.info("请求HTTP_METHOD:" + request.getMethod());
        logger.info("请求IP:" + request.getRemoteAddr());
        logger.info("请求CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName()
        								+"."+joinPoint.getSignature());
        logger.info("请求参数值:"+ Arrays.toString(joinPoint.getArgs()));
    }
    public void doAfterReturning(Object ret){
        logger.info("响应RESPONSE:"+ret);
        logger.info("响应时间SPEND TIME" + (System.currentTimeMillis()
         - startTime.get()));
    }
}
  • 以上代码实现的功能是,当访问com.whz.framework.web下的controller时,打印日志。试想,假如没有AOP进行统一配置,而是将该代码复制到每个方法中,那么方法将会变得庞大而冗余,不便于维护。

  • 三个W,即what,where和when。

  • what既是切面要处理的内容,使用注解@Aspect标注

  • where既是想要增强的方法,可以是切点,使用@Pointcut标注

  • when既是指想要在方法执行前、后或环绕进行织入。使用@Before @After @Around @AfterReturning等注解进行标注。

三.AOP主要名称概念

  • Aspect:通用功能代码实现
  • Target:被织入Aspect的对象
  • Join Point:可以作为切入点的机会,所有方法都可以作为切入点
  • PointCut:Aspect实际被应用在Join Point,支持正则
  • Advice:类里的方法以及这个方法如何织入到目标方法的方式
  • Weaving:AOP的实现过程

四.AOP实现

  • 观察DefaultAopProxyFactory 默认为AopProxyFactory的实现,创建CGLIB代理或JDK动态代理。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() 
		|| hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
  • 默认策略如果目标类是接口,则用JDKProxy来实现,否则用后者来实现
  • Cglib以继承的方式动态生成目标类的代理,借助ASM实现。JDKProxy通过Java内部反射机制实现。
  • 反射机制在生成类的过程中比较高效。ASM在生成类之后的执行过程中比较高效。若类使用final修饰,则无法进行Cglib代理。
  • JDKProxy实现动态代理可以参考动态代理模式。以下代码为实际代理实现过程。
//  每个代理实例都有一个关联的调用处理程序。
//  在代理实例上调用方法时
//该方法调用被编码并分派到{@code invoke}其调用处理程序的方法。
public class OrderServiceDynamicProxy 
								implements InvocationHandler {
    private Object target;
    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }

    public Object bind(){
        Class targetClass = target.getClass();
        return Proxy.newProxyInstance(targetClass.getClassLoader()
        								,targetClass.getInterfaces(),this);
    }
    /*
     * @Description //TODO 处理代理实例上的方法调用并返回
     * @Date 2019/4/4
     * @Param [proxy 调用该方法的代理实例
     *         method 在代理实例上调用的接口方法
     *         args 包含值的对象数组
     *         ]
     * @return java.lang.Object 从方法调用返回的值
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
    												throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        // target 从中调用基础方法的对象 是OrderServiceImpl对象
        // args 用于方法调用的参数  是Order对象
        // return 调度表示的方法的结果
        Object object = method.invoke(target, args);
        afterMethod();
        return object;
    }

    private void beforeMethod(Object object){
        int userId = 0;
        System.out.println("动态代理 before code ");
        if(object instanceof Order){
            Order order = (Order) object;
            userId = order.getUserId();
        }
        int dbRouter = userId % 2;
        System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");
        //todo 设置dataSource
        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));
    }
    private void afterMethod(){
        System.out.println("动态代理 after code");
    }
}
public static void main(String[] args) {
        Order order = new Order();
        order.setUserId(1);
        //创建代理类,并注入目标类
        //注意这里需要将被代理对象注入代理类,并对代理类进行强制类型转换
        IOrderService orderServiceDynamicProxy =
                (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
        orderServiceDynamicProxy.saveOrder(order);
    }

五.Cglib实现

  • Cglib针对类来代理实现,它的原理是对制定目标类生成一个子类,并覆盖其中方法实现增强。使用cglib实现动态代理,完全不受代理类必须实现接口的限制,而且Cglib底层采用了ASM字节码生成框架,使用字节码。
  • 需要引入两个jar包:cglib.jar asm.jar,若是已有Sping环境,则无需另外引入jar包。
  • 请看以下实例
public class UserDaoImpl {
    public void save(){
        System.out.println("已保存...");
    }
}
public class CglibProxyFactory {
    private Object obj;
    public CglibProxyFactory(Object obj) {
        super();
        this.obj = obj;
    }

    public Object getProxyFactory(){
    	//生成动态子类以启用方法拦截
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        //钩子回调用户定义的拦截器
        enhancer.setCallback(new MethodInterceptor() {
            //通过实现MethodInterceptor实现方法回调
            @Override
            public Object intercept(Object o, Method method, Object[] objects
                    , MethodProxy methodProxy) throws Throwable {
                System.out.println("代理开始...");
                method.invoke(obj,objects);
                System.out.println("代理结束...");
                return o;
            }
        });
        //生成目标对象并返回
        return enhancer.create();
    }
}
public class TestCglibProxy {
    public static void main(String[] args) {
        UserDaoImpl userDao = new UserDaoImpl();
        UserDaoImpl userDaoProxy = (UserDaoImpl) new CglibProxyFactory(userDao).getProxyFactory();
        System.out.println("目标对象类型:" +userDao.getClass());
        System.out.println("代理对象类型:" + userDaoProxy.getClass());
    }
}

代码运行结果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值