zoukankan      html  css  js  c++  java
  • JDK动态代理和CGLIB动态代理编码

    JDK动态代理【接口】:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * JDK动态代理接口
     */
    public class DynamicProxy implements InvocationHandler {
        private Object target;
        public DynamicProxy(Object target) {
            this.target = target;
        }
    
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
    
        private void after() {
            System.out.println("after");
        }
    
        private void before() {
            System.out.println("before");
        }
    
        public static void main(String[] args) {
            DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
            ((Hello)dynamicProxy.getProxy()).say();
        }
    }
    
    interface Hello {
        String say();
    }
    
    class HelloImpl implements Hello {
        @Override
        public String say() {
            System.out.println("say");
            return "say";
        }
    }
    

    CGLIB动态代理【类或接口】:
    依赖:

            <!-- https://mvnrepository.com/artifact/cglib/cglib -->
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
    

    编码:

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * cglib动态代理类+单例模式
     */
    public class CGlibProxy implements MethodInterceptor {
        private static CGlibProxy instance = new CGlibProxy();
    
        private CGlibProxy() {}
    
        public static CGlibProxy getInstance() {
            return instance;
        }
    
        public <T> T getProxy(Class<T> cls) {
            return (T) Enhancer.create(cls, this);  //Enhancer类,增强类
        }
        @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 after() {
            System.out.println("after");
        }
    
        private void before() {
            System.out.println("before");
        }
    
        public static void main(String[] args) {
            CGlibProxy.getInstance().getProxy(HelloImpl.class).say();
        }
    }
    

    Spring AOP:
    增强类:

    package smart;
    
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 前置增强类,放入增强代码
     */
    @Component
    public class GreetingBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("Before");
        }
    }
    
    package smart;
    
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 后置增强类
     */
    @Component
    public class GreetingAfterAdvice implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("After");
        }
    }
    
    package smart;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    /**
     * 环绕增强org.aopalliance.intercept.MethodInterceptor该接口不是Spring提供的,借用AOP联盟提供的
     */
    @Component
    public class GreetingAroundAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            before();
            Object result = methodInvocation.proceed();
            after();
            return result;
        }
    
        private void after() {
            System.out.println("After...");
        }
    
        private void before() {
            System.out.println("Before...");
        }
    }
    
    package smart;
    
    import org.springframework.aop.ThrowsAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * 抛出增强
     */
    @Component
    public class GreetingThrowAdvice implements ThrowsAdvice {
        /**
         * 当被代理的方法内部抛出异常,会被该增强拦截
         * 按理ThrowsAdvice接口有afterThrowing方法,参数是方法,参数,目标对象,异常对象等信息,可以获取记录日志或插入数据库
         * 但点开接口看没有任何方法
         */
        public void afterThrowing(Method method, Object[] args, Object target, Exception e) {}
    }
    
    package smart;
    
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.support.DelegatingIntroductionInterceptor;
    import org.springframework.stereotype.Component;
    
    /**
     * 用这个增强类让被代理类动态实现Apology接口,XML配置如下:
     *     <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
     *         <property name="interfaces" value="smart.Apology"/> //需要动态实现的接口
     *         <property name="target" value="greetingImpl"/> //目标类
     *         <property name="interceptorNames" value="greetingIntroductionAdvice"/> //引入增强
     *         <property name="proxyTargetClass" value="true"/> //代理目标类,默认false代理接口,此时spring使用jdk动态代理,设置为true,spring用cglib动态代理
     *     </bean>
     */
    @Component
    public class GreetingIntroductionAdvice extends DelegatingIntroductionInterceptor implements Apology {
        public Object invoke(MethodInvocation mi) throws Throwable {
            return super.invoke(mi);
        }
        @Override
        public void saySorry(String name) {
            System.out.println("Sorry! " + name);
        }
    }
    

    编程式:

    package smart;
    
    import org.springframework.aop.framework.ProxyFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * Spring AOP:前置增强(Before Advice)、后置增强(After Advice)、环绕增强(Around Advice)、抛出增强(Throws Advice)、引入增强(Introduction Advice)
     * AOP术语:对方法的增强叫Weaving(织入),对类的增强叫Introduction(引入)
     * 当某个类实现类A接口,并没有实现B接口,想要调用B接口的方法,即实现对类的功能增强,使用引入增强(参考《Spring Reference》),不用改写被代理类,在程序运行时动态实现接口
     *
     * 分编程式和声明式
     * 增强类同时实现MethodBeforeAdvice和AfterReturningAdvice接口,执行一次addAdvice即可
     *
     * 声明式配置代理:
     * <bean id="代理工厂bean名称" class="org.springframework.aop.framework.ProxyFactoryBean">
     *      <property name="interfaces" value="代理的接口全限定名"/>
     *      <property name="target" ref="接口实现类的bean名称"/>
     *      <property name="interceptorNames">
     *          <list>
     *              <value>拦截器名称(即增强类的bean名称)</value> (若只有一个增强类,可简化为<property name="interceptorNames" value="拦截器名称(即增强类的bean名称)"/>)
     *          </list>
     *      </property>
     * </bean>
     */
    public class GreetingAdviceTest {
        public static void main(String[] args) {
            /**
             * 编程式配置代理
             */
            ProxyFactory proxyFactory = new ProxyFactory(); //创建代理工厂,使用ProxyFactoryBean也可以,一回事
            proxyFactory.setTarget(new GreetingImpl()); //注入目标类对象
            proxyFactory.addAdvice(new GreetingBeforeAdvice()); //添加前置增强
            proxyFactory.addAdvice(new GreetingAfterAdvice()); //添加后置增强
    //        proxyFactory.addAdvice(new GreetingAroundAdvice()); //添加环绕增强,将前置增强与后置增强功能结合起来
            Greeting greeting = (Greeting)proxyFactory.getProxy(); //从代理工厂中获取代理
            greeting.sayHello("Jack"); //调用代理的方法
        }
    }
    interface Greeting {
        void sayHello(String name);
    }
    @Component
    class GreetingImpl implements Greeting {
        @Override
        public void sayHello(String name) {
            System.out.println("hello, " + name);
        }
    }
    
    interface Apology {
        void saySorry(String name);
    }
    
    //Apology接口默认实现类
    class ApologyImpl implements Apology {
        @Override
        public void saySorry(String name) {
            System.out.println("Sorry!" + name);
        }
    }
    

    声明式:

    XML配置:
        <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="smart.Greeting"/>
            <property name="target" value="greetingImpl"/>
            <property name="interceptorNames">
                <list>
                    <value>greetingBeforeAdvice</value>
                    <value>greetingAfterAdvice</value>
                </list>
            </property>
        </bean>
    
    package smart;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class GreetingAdviceTestForXML {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring_aop.xml");
            Greeting greeting = (Greeting) context.getBean("greetingProxy");
            greeting.sayHello("JACK");
            //若使用引入增强,动态实现了Apology接口,将bean强转为Apology类型调用其方法即可
        }
    }
    

    切面:
    动态代理拦截了类中所有方法,需要在代码中对所拦截的方法名加以判断,不太优雅的做法。
    Spring AOP也拦截了类中所有方法,可通过切面Advisor(增强类Advice+拦截匹配条件(Pointcut,切点,基于表达式的拦截条件)),将切面配置到ProxyFactory中生成代理。

        <!-- 配置切面:增强+切点 -->
        <!-- Spring AOP提供的其它切面类:
            DefaultPointcutAdvisor:默认切面,可扩展它来自定义切面
            NameMatchMethodPointcutAdvisor:根据方法名称进行匹配的切面
            StaticMethodMatcherPointcutAdvisor:用于匹配静态方法的切面
         -->
        <bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="greetingAroundAdvice"/> <!-- 增强 -->
            <property name="pattern" value="smart.GreetingImpl.good.*"/> <!-- 切点,正则表达式(对满足切点匹配条件的方法进行拦截),匹配类中所有方法名以字符串good开头的方法,.*表示匹配所有字符 -->
        </bean>
        <!-- 配置代理,注入切面 -->
        <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" value="greetingImpl"/> <!-- 目标类 -->
            <property name="interceptorNames" value="greetingAdvisor"/> <!-- 这里不再配置增强,而改成切面,切面中封装了增强和切点 -->
            <property name="proxyTargetClass" value="true"/> <!-- 代理目标类 -->
        </bean>
    

    上面的方式随着项目扩大,代理配置会越来越多,改用Spring AOP提供的自动代理(扫描bean名称):

        <!-- 配置自动代理,不能定义代理接口即interfaces属性,因为不知道这些bean实现了多少接口,所以只能代理类 -->
        <!-- optimize属性默认false,为true对代理生成策略进行优化:类有接口就用JDK动态代理代理接口,没有接口就用cglib动态代理代理类
            而proxyTargetClass属性为true强制代理类,不考虑代理接口的方式
            cglib可以代理任何类,但cglib创建代理速度慢,创建代理后运行速度快,jdk相反
            运行时采用cglib创建代理拖慢系统性能,建议系统初始化时用cglib创建代理放入spring的applicationContext中备用
            BeanNameAutoProxyCreator只能匹配目标类,不能匹配指定方法,要想匹配方法,需要使用切面与切点
            Spring AOP基于切面的自动代理生成器:DefaultAdvisorAutoProxyCreator
        -->
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
            <property name="beanNames" value="*Impl"/> <!-- 只为后缀是Impl的bean生成代理 -->
            <property name="interceptorNames" value=""/> <!-- 增强 -->
            <property name="optimize" value="true"/> <!-- 是否对代理生成策略进行优化 -->
        </bean>
    

    进一步,Spring AOP基于切面的自动代理生成器:DefaultAdvisorAutoProxyCreator:

        <!-- 配置切面:增强+切点 -->
        <bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="pattern" value="smart.GreetingImpl.good.*"/>
            <property name="advice" ref="greetingAroundAdvice"/>
        </bean>
        <!-- 这里无需再配置代理,代理由DefaultAdvisorAutoProxyCreator自动生成,它扫描所有切面类并为其自动生成代理 -->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
            <property name="optimize" value="true"/>
        </bean>
    

    以上的方式依然存在大量切面配置,若要拦截指定注解的方法,必须扩展DefaultPointcutAdvisor类自定义切面类,在配置文件中进行切面配置,繁琐,这种切面+代理的配置方式保留,但引入Spring+Aspectj的优秀方案,常用:
    Spring集成的AspectJ与直接使用AspectJ不同,不用定义AspectJ类(它扩展了Java语法的一种新语言,需要特点编译器),而只需要使用AspectJ切点表达式(比正则表达式更友好):
    注解的方式:
    配置:

    <?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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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
                                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- spring 2.5+提供的bean扫描特性,扫描指定包 -->
        <context:component-scan base-package="smart"/>
        <!-- proxy-target-class属性默认false只能使用JDK动态代理代理接口,为true使用CGLib动态代理代理目标类 -->
        <!-- 不在需要大量配置切面和代理了 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>
    

    编程:

    package smart;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.DeclareParents;
    import org.springframework.stereotype.Component;
    
    /**
     * Spring+AspectJ基于注解,通过AspectJ execution表达式拦截方法实现环绕增强
     * @Aspect注解表名该类是Aspect切面类(Advisor)无需实现接口,定义任意名称方法即可,Around注解+execution表达式(AspectJ切点表达式)
     * 方法参数ProceedingJoinPoint对象在AOP中叫做JoinPoint连接点,可以通过它获取如方法名、参数等方法的任何信息
     * 在xml配置:
     *
     */
    @Aspect
    @Component
    public class GreetingAspect {
        /**
         * 切点表达式,execution()表示拦截方法,括号中定义需要匹配的规则
         * 第一个*表示方法的返回值是任意的
         * 第二个*表示匹配该类中所有的方法,改为指定方法名称可以匹配指定方法
         * (..)表示方法的参数是任意的
         * @param pjp
         * @return
         * @throws Throwable
         */
        /**
         * 除了@Around外,其它增强注解:@Before前置增强,@After后置增强,@Around环绕增强,@AfterThrowing抛出增强
         * DeclareParents引入增强,AfterReturning返回后增强,可立即为Finally增强,相当于finally语句,在方法结束后执行,比After执行时机晚
         *
         * 若目标方法有指定注解如自定义一个注解@Log,使用AspectJ @annotation表达式拦截方法:
         * 切点表达式改为@Around("@annotation(Log)")即可,括号里是要拦截的注解全限定名
         *
         * @param pjp
         * @return
         * @throws Throwable
         */
        @Around("execution(* smart.GreetingImpl.*(..))")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            before();
            Object result = pjp.proceed();
            after();
            return result;
        }
    
        private void after() {
            System.out.println("after");
        }
    
        private void before() {
            System.out.println("before");
        }
    
        /**
         * 在Aspect类中定义需要引入增强的接口,也就是运行时需要动态实现的接口,在接口是标注@DeclareParents注解,属性:
         * value目标类
         * defaultImpl引入的接口的默认实现类,默认实现会在运行时自动增强到目标类中
         */
        @DeclareParents(value = "smart.GreetingImpl", defaultImpl = ApologyImpl.class)
        private Apology apology;
    }
    

    JDK1.4没有注解,使用基于配置的Spring+AspectJ,基于配置的方式定义切面类:

        <aop:config>
            <aop:aspect ref="aspect类的的bean名称">
                <aop:around method="around" pointcut="execution(* smart.GreetingImpl.*(..))"/>
            </aop:aspect>
        </aop:config>
    

    AOP思维导图:

    各类增强类型对应的解决方案:

    Spring AOP整体架构UML类图:

  • 相关阅读:
    noi.ac NOI挑战营模拟赛1-5
    TJOI2015 弦论
    CQOI2018 破解D-H协议
    NOI2013 矩阵游戏
    NOI2015 荷马史诗
    NOI2015 寿司晚宴
    SDOI2014 重建
    NOI1999 生日蛋糕
    NOI2015 程序自动分析
    ZJOI2008 泡泡堂
  • 原文地址:https://www.cnblogs.com/kibana/p/11755971.html
Copyright © 2011-2022 走看看