(转)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的主要内容,如果未能解决你的问题,请参考以下文章