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类图:

posted @ 2019-10-28 22:20  发挥哥  阅读(414)  评论(0编辑  收藏  举报