• 005-搭建框架-实现AOP机制【二】AOP技术


    一、什么是AOP

      aspect-oriented  programming,面向切面编程,对oop的一种补充。

      著名示例,aspectj,spring+aspectj

    二、aop之代码重构

    2.1、代理重构

    示例代码

    public interface Greeting {
        void sayHello(String name);
    }

    实现

    public class GreetingImpl implements Greeting {
        @Override
        public void sayHello(String name) {
            before();
            System.out.println("hello " + name);
            after();
        }
    
        private void before() {
            System.out.println("before");
        }
    
        private void after() {
            System.out.println("after");
        }
    }

    以上便是标准的死代码,不宜扩展。

    代码重构:

    1、静态代理

    2、JDK动态代理

    3、CGLib动态代理

    2.1.1、静态代理

    实现

    package com.lhx.chapter4.aop.staticproxy;
    
    import com.lhx.chapter4.aop.Greeting;
    import com.lhx.chapter4.aop.GreetingImpl;
    
    public class GreetingProxy implements Greeting {
        private GreetingImpl greetingImpl;
        public GreetingProxy(GreetingImpl greetingImpl){
            this.greetingImpl=greetingImpl;
        }
    
        @Override
        public void sayHello(String name) {
            before();
            greetingImpl.sayHello(name);
            after();
        }
    
        private void before() {
            System.out.println("before");
        }
    
        private void after() {
            System.out.println("after");
        }
    }
    View Code

    调用

    package com.lhx.chapter4.aop.staticproxy;
    
    import com.lhx.chapter4.aop.Greeting;
    import com.lhx.chapter4.aop.GreetingImpl;
    
    public class Client {
        public static void main(String[] args) {
            Greeting proxy = new GreetingProxy(new GreetingImpl());
            proxy.sayHello("木子旭");
        }
    }
    View Code

    会导致XxxProxy越来越多。

    2.1.2、JDK动态代理

    实现

    package com.lhx.chapter4.aop.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKDynamicProxy implements InvocationHandler {
        private Object target;
    
        public JDKDynamicProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
    
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    this);
        }
    
        private void before() {
            System.out.println("Before");
        }
    
        private void after() {
            System.out.println("After");
        }
    }
    View Code

    调用

    package com.lhx.chapter4.aop.dynamicproxy;
    
    import com.lhx.chapter4.aop.Greeting;
    import com.lhx.chapter4.aop.GreetingImpl;
    
    public class JDKDynamicProxyClient {
        public static void main(String[] args) {
            JDKDynamicProxy dynamicProxy = new JDKDynamicProxy(new GreetingImpl());
            Greeting proxy = dynamicProxy.getProxy();
            proxy.sayHello("muzixu");
        }
    }
    View Code

    所有的动态代理都合并到代理类了,JDK动态代理只能代理接口,不能代理没有接口的类。

    2.1.3、CGLib动态代理

    实现

    package com.lhx.chapter4.aop.dynamicproxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class CGLibDynamicProxy implements MethodInterceptor {
        private static CGLibDynamicProxy instance = new CGLibDynamicProxy();
        private CGLibDynamicProxy(){
        }
        public static CGLibDynamicProxy getInstance(){
            return instance;
        }
        public <T> T getProxy(Class<T> cls) {
            return (T) Enhancer.create(cls,this);
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            before();
            Object result = methodProxy.invokeSuper(o,objects);
            after();
            return result;
        }
        private void before() {
            System.out.println("Before");
        }
    
        private void after() {
            System.out.println("After");
        }
    }
    View Code

    调用

    package com.lhx.chapter4.aop.dynamicproxy;
    
    import com.lhx.chapter4.aop.Greeting;
    import com.lhx.chapter4.aop.GreetingImpl;
    
    public class CGLibDynamicProxyClient {
        public static void main(String[] args) {
            Greeting proxy = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
            proxy.sayHello("muzixu");
        }
    }
    View Code

    2.2、spring aop

    1、spring aop(编程式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】

      可以理解为增强类

    前置增强

    package com.lhx.chapter4.aop.springaop;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class GreetingBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("Before");
        }
    }
    View Code 

    后置增强

    package com.lhx.chapter4.aop.springaop;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class GreetingAfterAdvice implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("after");
        }
    }
    View Code

    调用

    package com.lhx.chapter4.aop.springaop;
    
    import com.lhx.chapter4.aop.GreetingImpl;
    import org.springframework.aop.framework.ProxyFactory;
    
    public class GreetingAdviceClient {
        public static void main(String[] args) {
            ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
            proxyFactory.setTarget(new GreetingImpl());//射入目标类对象
            proxyFactory.addAdvice(new GreetingBeforeAdvice());//前置通知
            proxyFactory.addAdvice(new GreetingAfterAdvice());//后置
            //proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice());//前后结合
            proxyFactory.addAdvice(new GreetingAroundAdvice());//环绕
    
            GreetingImpl greeting = (GreetingImpl) proxyFactory.getProxy();
            greeting.sayHello("木子旭");
        }
    }
    View Code

     前置后置合并一个

    package com.lhx.chapter4.aop.springaop;
    
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("After");
        }
    
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("Before");
        }
    }
    View Code

    环绕通知

    package com.lhx.chapter4.aop.springaop;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.stereotype.Component;
    
    @Component
    public class GreetingAroundAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            before();
            Object proceed = methodInvocation.proceed();
            after();
            return proceed;
        }
        private void before(){
            System.out.println("Before");
        }
        private void after(){
            System.out.println("after");
        }
    }
    View Code

      环绕通知不是spring提供是由org.aopalliance.intercept.MethodInterceptor提供

    2、spring aop(声明式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】

    spring的application-aop.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
        <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
        <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
        <!--配置一个代理-->
        <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!--需要代理的接口-->
            <property name="interfaces" value="com.lhx.chapter4.aop.Greeting"></property>
            <!--接口实现类-->
            <property name="target" ref="greetingImpl"></property>
            <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
            <property name="interceptorNames">
                <list>
                    <value>greetingAroundAdvice</value>
                </list>
            </property>
        </bean>
    </beans>
    View Code

      ProxyFactoryBean相当于ProxyFactory

    在部分实现类增加@Component注解

    客户端调用

    package com.lhx.chapter4.aop.springaopdeclare;
    
    import com.lhx.chapter4.aop.Greeting;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Client {
        public static void main(String[] args) {
            //获取spring content
            ApplicationContext context =new ClassPathXmlApplicationContext("application-aop.xml");
            //从Content中根据id获取Bean对象,其实就是一个代理
            Greeting greetingProxy = (Greeting) context.getBean("greetingProxy");
            greetingProxy.sayHello("muzixu ");
    
        }
    }
    View Code

    3、Spring AOP:Throws Advice【抛出增强】

      程序报错,抛出异常,一般做法打印到控制台或者日志文件。一劳永逸办法使用,Throws Advice

     异常接口

    package com.lhx.chapter4.aop;
    
    public interface Greeting {
        void sayHello(String name);
        void sayHelloThrows(String name);
    }
    View Code

    抛出异常增强类

    package com.lhx.chapter4.aop.springaopthrows;
    
    import org.springframework.aop.ThrowsAdvice;
    
    import java.lang.reflect.Method;
    
    public class GreetingThrowsAdvice implements ThrowsAdvice {
        //public void afterThrowing(Exception ex)
        //public void afterThrowing(RemoteException)
        //public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
        //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
        public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
            System.out.println("--------Throw Exception-------");
            System.out.println("Target class:"+target.getClass().getName());
            System.out.println("Method name:"+method.getName());
            System.out.println("Exception:"+ex.getMessage());
            System.out.println("------------------------------");
    
        }
    }
    View Code

      接口中没有任何抽象方法,但是自定义又会出异常,Spirng内部是用反射来实现方法匹配的,需要实现下列接口中的其中1个

        //public void afterThrowing(Exception ex)
        //public void afterThrowing(RemoteException)
        //public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
        //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)

     4、Spring AOP:Introduction Advice【引入增强】

      在AOP中对方法的增强叫Weaving【织入】,对类的增强叫Introduction【引用】,Introduction advice【引用增强】就是对类功能的增强。

      示例:类C实现了A接口,那么使类C可以调用B接口

    新增一个接口

    package com.lhx.chapter4.aop.springaopintroductionadvice;
    
    public interface Apology {
        void saySorry(String name);
    }
    View Code

    不想改变原有类的实现,需要使用引用增强

    package com.lhx.chapter4.aop.springaopintroductionadvice;
    
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.support.DelegatingIntroductionInterceptor;
    
    public class GreetingIntroductionAdvice extends DelegatingIntroductionInterceptor implements Apology {
        @Override
        public void saySorry(String name) {
            System.out.println("sorry " + name);
        }
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            return super.invoke(mi);
        }
    }
    View Code

      引用增强类扩展了org.springframework.aop.support.DelegatingIntroductionInterceptor,同时实现了需要的接口

    需要的Spring配置application-aop-intro.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
        <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
        <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
        <!--配置一个代理-->
        <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!--需要代理的接口-->
            <property name="interfaces" value="com.lhx.chapter4.aop.springaopintroductionadvice.Apology"></property>
            <!--接口实现类-->
            <property name="target" ref="greetingImpl"></property>
            <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
            <property name="interceptorNames" value="greetingIntroductionAdvice">
    
            </property>
            <!--代理目标 false 接口 true 类 默认是false-->
            <property name="proxyTargetClass" value="true"></property>
        </bean>
    </beans>
    View Code

      proxyTargetClass属性,默认false,代理接口,此时Spring使用JDK动态代理

          如果是true,此时Spring使用CGLib动态代理。

    客户端调用

    package com.lhx.chapter4.aop.springaopintroductionadvice;
    
    import com.lhx.chapter4.aop.Greeting;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Client {
        public static void main(String[] args) {
            //获取spring content
            ApplicationContext context =new ClassPathXmlApplicationContext("application-aop-intro.xml");
            //从Content中根据id获取Bean对象,其实就是一个代理
            Greeting greetingProxy = (Greeting) context.getBean("greetingProxy");
            greetingProxy.sayHello("muzixu ");
            Apology apology = (Apology) greetingProxy;
            //将目标类强制向上转型为Apology接口类型【引入增强的特性,即“动态接口实现”功能】
            apology.saySorry("Jack");
    
        }
    }
    View Code

    5、Spring AOP:切面

      之前说的AOP框架其实可以理解为一个拦截器框架,他拦截了一个类,其实就是拦截了所有方法。在使用动态代理时,也需要在代码中对所拦截的方法名加以判断,才能过滤出需要拦截的方法。

      Advisor【切面】封装了Advice【增强】与Pointcut【切面】。

    增加两个以good开头的方法

    package com.lhx.chapter4.aop.aoppointcut;
    
    import com.lhx.chapter4.aop.Greeting;
    import org.springframework.stereotype.Component;
    
    @Component
    public class GreetingGood implements Greeting {
        @Override
        public void sayHello(String name) {
            System.out.println("hello " + name);
        }
    
        public void goodMorning(String name) {
            System.out.println("goodMorning " + name);
        }
    
        public void goodNight(String name) {
            System.out.println("goodNight " + name);
        }
    
        @Override
        public void sayHelloThrows(String name) {
    
        }
    }
    View Code

    基于正则表达式的切面类

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
        <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)-->
        <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan>
        <!--配置一个切面-->
        <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="greetingAroundAdvice"></property>
            <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property>
        </bean>
        <!--配置一个代理-->
        <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!--接口实现类-->
            <property name="target" ref="greetingGood"></property>
            <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
            <property name="interceptorNames" value="pointcutAdvisor"></property>
            <!--代理目标 false 接口 true 类 默认是false-->
            <property name="proxyTargetClass" value="true"></property>
        </bean>
    </beans>
    View Code

      interceptorNames不在是一个增强,而是一个切面。

      表达式中的".*",标示匹配所有字符。

    Spring AOP提供的几个切面类

      RegexpMethodPointcutAdvisor

      DefaultPointcutAdvisor

      NameMatchMethodPointcutAdvisor

      AspectJExpressionPointcutAdvisor

     6、Spring AOP:自动代理【扫面Bean名称】

     增加spring xml配置

        <!--spring 自动代理-->
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
            <!--只为后缀为Impl的Bean生成代理-->
            <property name="beanNames" value="*Impl"></property>
            <!--拦截器名称(也是增强类名称,Spring Bean的id)-->
            <property name="interceptorNames" value="greetingAroundAdvice"></property>
            <!--是否对代理生成策略进行优化-->
            <property name="optimize" value="true"></property>
        </bean>

      此处使用BeanNameAutoProxyCreator只为后缀为Impl的Bean生成代理类。

        这里提供了optimize配置项,默认为false, true含义:对生成策略进行优化,如果该类有接口,就代理接口(JDK动态代理),如果没有接口,就代理类(CGLib动态代理)。

      不像之前proxyTargetClass属性,强制代理类,而不考虑接口方式

     既然有CGLib动态代理代理类,为什么还需要JDK动态代理?

      CGLib创建动态代理的速度较慢,但创建代理后运行速度却非常快。而JDK动态代理正好相反。如果在运行的时候不断的用CGLib创建代理, 系统性能会降低。

      所以建议一般在系统初始化的时候用CGLib去创建代理,并放入Spring的ApplicationContext中。

    7、Spring AOP:自动代理【扫面切面配置】

      Spring的xml配置

        <!--配置一个切面-->
        <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="greetingAroundAdvice"></property>
            <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property>
        </bean>
        <!--spring 自动代理 扫描切面-->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
            <!--是否对代理生成策略进行优化-->
            <property name="optimize" value="true"></property>
        </bean>

      这里无需在配置代理,因为代理将由DefaultAdvisorAutoProxyCreator自动生成,这个类可以扫描所有的切面类,并未其自动生成代理。

    2.3、spring+aspectj

      见下一节

      

  • 相关阅读:
    day14(xml 编写及解析)
    day11(多线程,唤醒机制,生产消费者模式,多线程的生命周期)
    day13(反射,BeanUtils包)
    day10(IO流汇总)
    day08(File类 ,字节流)
    day08(异常处理,创建异常,finally,throws和throw的区别)
    [SPOJ-PT07J] Query on tree III (主席树)
    [ZJOI2008] 树的统计(树链剖分)
    长链剖分学习笔记
    [BZOJ4260] Codechef REBXOR (01字典树,异或前缀和)
  • 原文地址:https://www.cnblogs.com/bjlhx/p/7695823.html
Copyright © 2020-2023  润新知