(转)java动态代理与aop

Posted PacosonSWJTU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(转)java动态代理与aop相关的知识,希望对你有一定的参考价值。

转自: Java 动态代理与AOP - 如果的事 - 博客园动态代理与AOP 代理模式 代理模式给某一个目标对象(target)提供代理对象(proxy),并由代理对象控制对target对象的引用。 模式图: 代理模式中的角色有: 抽象对象角色(Abstrachttps://www.cnblogs.com/chenny7/p/11201010.html


【1】代理模式

/**
 * 抽象对象角色
 */
abstract class AbstractObject {
    public abstract void operation();
}
/**
 * 目标对象
 */
class TargetObject extends AbstractObject {
    public void operation() {
        System.out.println("Do Something!");
    }
}
/**
 * 代理对象
 */
public class ProxyObject extends AbstractObject {
    TargetObject targetObject = new TargetObject();

    @Override
    public void operation() {
        System.out.println("do sth before");
        targetObject.operation();
        System.out.println("do sth after");
    }

    public static void main(String[] args) {
        new ProxyObject().operation();
    }
}

代理模式中的角色有:

  • 抽象对象角色(AbstractObject):声明了目标对象和代理对象的共同接口,这样依赖在任何可以使用目标对象的地方都可以使用代理对象。
  • 目标对象角色(RealObject):定义了代理对象所代表的目标对象。   
  • 代理对象角色(ProxyObject):代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或者之后,执行某个操作,而不是单纯的将调用传递给目标对象。

【2】静态代理与动态代理

按照代理类的创建时期,可分为静态代理和动态代理:

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时运用反射机制动态创建而成。

【2.1】静态代理

/**
 * 会飞接口
 */
interface Flyable {
    void fly(long ms);
}
/**
 * 会飞的鸟
 */
class Bird implements Flyable {
    @Override
    public void fly(long ms) {
        System.out.println("bird is flying.");
        try {
            Thread.sleep(ms);
        } catch (Exception e ) {
            System.out.println("bird睡眠异常");
        }
    }
}
/**
 * 会飞的风筝
 */
class Kite implements Flyable {
    @Override
    public void fly(long ms) {
        System.out.println("kite is flying.");
        try {
            Thread.sleep(ms);
        } catch (Exception e ) {
            System.out.println("kite 睡眠异常");
        }
    }
}
/**
 * 静态代理
 */
public class StaticProxy implements Flyable {
    private Flyable flyable;
    public StaticProxy(Flyable flyable) {
        this.flyable = flyable;
    }
    @Override
    public void fly(long ms) {
        System.out.println("before fly");
        flyable.fly(ms);
        System.out.println("after fly");
    }

    public static void main(String[] args) {
        new StaticProxy(new Kite()).fly(1000);
        new StaticProxy(new Bird()).fly(1000);
    }
}

静态代理缺点:

  • 若接口  Flyable 增加一个方法,则目标对象(接口实现类)与代理类全都需要增加方法实现,全都需要修改代码;

【2.2】动态代理

/**
 * 动态代理
 */
public class DynamicProxy implements InvocationHandler {
    private Object targetObject;
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke");
        proxy = method.invoke(targetObject, args);
        System.out.println("before after");
        return proxy;
    }
    public static void main(String[] args) {
        DynamicProxy dProxy = new DynamicProxy();
        //  会飞的鸟
        Flyable bird = (Flyable)dProxy.newProxyInstance(new Bird());
        bird.fly(1000);
        // 风筝
        Flyable kite = (Flyable)dProxy.newProxyInstance(new Kite());
        kite.fly(1000);
    }
}

显然, 动态代理对象不需要实现目标对象接口,但目标对象一定要实现接口,否则不能使用代理;


【3】cglib 代理

1)应用场景:

  • 有的时候,目标对象可能只是一个单独的对象,并没有实现任何的接口,这个时候,我们就可以使用目标对象子类的方式实现代理,这种代理方式就是:Cglib代理,也叫做子类代理,它是在内存中构件一个子类对象,从而实现对目标对象的功能拓展。

2)cglib 介绍

  • Cglib是强大的高性能的代码生成包,它可以在运行期间拓展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。
  • Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类,不鼓励直接只使用ASM,因为它要求你必须对JVM内部结构,包括class文件的格式和指令集都很熟悉。
class Plane {
    public void fly(long ms) {
        System.out.println("plane is flying");
        try {
            Thread.sleep(ms);
        } catch(Exception e) {
            System.out.println("plane睡眠异常");
        }
    }
}
public class CglibProxy implements MethodInterceptor {
    private Object target;
    public CglibProxy(Object target) {
        this.target = target;
    }
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer(); // 1 实例化工具类
        enhancer.setSuperclass(this.target.getClass()); // 设置父类对象
        enhancer.setCallback(this); // 设置回调函数
        return enhancer.create(); // 创建子类,也就是代理对象
    }
    // 拦截器方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before invoke");
        Object returnValue = method.invoke(target, objects);
        System.out.println("after invoke");
        return returnValue;
    }
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy(new Plane());
        Plane plane = (Plane)cglibProxy.getProxyInstance();
        plane.fly(1000);
    }
}

maven pom.xml 

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

【4】 spring aop

1)Spring在新版本中对AOP功能进行了增强,体现在这么几个方面:

  •     在XML配置文件中为AOP提供了aop命名空间
  •     增加了AspectJ切点表达式语言的支持
  •     可以无缝地集成AspectJ

2)如何使用 引介切面(Introduction Advisor)为一个现有对象添加任何接口的实现:

2.1)定义两个接口及其实现类,包括 服务员与售货员;

public class SpringAopDef {
}
interface Waiter { // 服务员接口
    void greetTo(String client);
    void serveTo(String client);
}
class NaiveWaiter implements Waiter { // 服务员实现类
    public void greetTo(String client) {
        System.out.println("NaiveWaiter greet to " + client);
    }
    public void serveTo(String client) {
        System.out.println("NaiveWaiter serve to " + client);
    }
}
interface Seller {// 售货员接口
    int sell(String goods, String client);
}
class SmartSeller implements Seller { // 售货员实现类
    public int sell(String goods, String client) {
        System.out.println("a smart seller sells " + goods + " to " + client);
        return 100;
    }
}

2.2)下一步,我们想让 服务员充当售货员角色,可以卖东西;

引入切面:

@Aspect
public class EnableSellerAspect {
    @DeclareParents(value="com.swjtu.mybatis.proxy.springaop.NaiveWaiter" // 切点-目标类
                            , defaultImpl = SmartSeller.class)  // 增强类
    public Seller seller; // 增强类接口
}

maven pom 引入spring依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.swjtu.mybatis</groupId>
  <artifactId>MybatisHello2</artifactId>
  <version>0.0.1-SNAPSHOT</version>

	<properties>
		<spring.version>5.2.0.RELEASE</spring.version>
	</properties>
.................
<dependencies>

<dependency>
		  <groupId>org.aspectj</groupId>
		  <artifactId>aspectjrt</artifactId>
		  <version>1.9.4</version>
	  </dependency>
	  <dependency>
		  <groupId>org.aspectj</groupId>
		  <artifactId>aspectjweaver</artifactId>
		  <version>1.9.4</version>
	  </dependency>

	  <!-- Spring Dependencies -->
	  <dependency>
		  <groupId>org.springframework</groupId>
		  <artifactId>spring-core</artifactId>
		  <version>${spring.version}</version>
	  </dependency>
	  <dependency>
		  <groupId>org.springframework</groupId>
		  <artifactId>spring-beans</artifactId>
		  <version>${spring.version}</version>
	  </dependency>
	  <dependency>
		  <groupId>org.springframework</groupId>
		  <artifactId>spring-context</artifactId>
		  <version>${spring.version}</version>
	  </dependency>

  </dependencies>

beans.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"
       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/aop
                            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                            http://www.springframework.org/schema/context/
                            http://www.springframework.org/schema/context/spring-context.xsd">

    <aop:aspectj-autoproxy/>
    <bean id="waiter" class="com.swjtu.mybatis.proxy.springaop.NaiveWaiter"/>
    <bean class="com.swjtu.mybatis.proxy.springaop.EnableSellerAspect"/>

    <!-- 定义切面 -->
    <bean id="testBeforeAdvice" class="com.swjtu.mybatis.proxy.springaop.advice.TestBeforeAdvice"/>
    <aop:config proxy-target-class="true">
        <aop:advisor advice-ref="testBeforeAdvice" pointcut="execution(* com..*.Waiter.greetTo(..))"/>
    </aop:config>

</beans>

切面如下:

public class TestBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("before invoke");
        System.out.println("do busi");
        System.out.println("after invoke");
    }
}

当调用 Waiter.greetTo() 方法会调用 before 通知;

springaop 测试用例入口:

public class SpringAopMain {
    public static void main(String[] args) {
        // 获取上下文环境
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Waiter waiter = (Waiter)context.getBean("waiter"); // 从上下文中获取bean
        // 调用服务员原有方法
        waiter.greetTo("zhangsan");
        waiter.serveTo("zhangsan");
        // 通过切面已经将 Waiter 实现了 Seller 接口,所以可以强制转换
        Seller seller = (Seller) waiter;
        seller.sell("apple", "zhangsan");
    }
}
// spring aop 打印日志 
before invoke
do busi
after invoke
NaiveWaiter greet to zhangsan
NaiveWaiter serve to zhangsan
a smart seller sells apple to zhangsan

以上是关于(转)java动态代理与aop的主要内容,如果未能解决你的问题,请参考以下文章

(转)java的动态代理机制详解

AOP与JAVA动态代理

转:JDK动态代理为什么必须用接口以及与CGLIB的对比

Spring AOP实现原理与CGLIB应用(转)

什么是反射技术?什么是静态代理?什么是动态代理?什么是aop

[JAVA]动态代理与AOP的千丝万缕