【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

时间:2023-02-17 23:15:48

Spring AOP详解 、 JDK动态代理、CGLib动态代理

 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html

AOP是Aspect Oriented Programing的简称,面向切面编程。AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录。AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。

一、AOP术语

1.连接点(Joinpoint)

程序执行的某个特定位置:如类开始初始化之前、类初始化之后、类某个方法调用前、调用后等;一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就成为“连接点”,Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后以及方法调用前后的这些程序执行点织入增强。比如:黑客攻击系统需要找到突破口,没有突破口就没有办法攻击,从某种程度上来说,AOP就是一个黑客,连接点就是AOP向目标类攻击的候选点。

连接点有两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位;如在Test.foo()方法执行前的连接点,执行点为Test.foo,方位为该方法执行前的位置。Spring使用切点对执行点进行定位,而方位则在增强类型中定义。

2.切点(Pointcut)

每个程序类都拥有许多连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。但在为数众多的连接点钟,如何定位到某个连接点上呢?AOP通过切点定位特定连接点。通过数据库查询的概念来理解切点和连接点:连接点相当于数据库表中的记录,而切点相当于查询条件。连接点和切点不是一一对应的关系,一个切点可以匹配多一个连接点。

在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点;其实确切的说应该是执行点而非连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。

3.增强(Advice)

增强是织入到目标类连接点上的一段程序代码(好比AOP以黑客的身份往业务类中装入木马),增强还拥有一个和连接点相关的信息,这边是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点了,所以Spring提供的增强接口都是带方位名的:BefortAdvice、AfterReturningAdvice、ThrowsAdvice等。(有些将Advice翻译为通知,但通知就是把某个消息传达给被通知者,并没有为被通知者做任何事情,而Spring的Advice必须嵌入到某个类的连接点上,并完成了一段附加的应用逻辑;)

4.目标对象(Target)

增强逻辑的织入目标类,如果没有AOP,目标业务类需要自己实现所有逻辑,在AOP的帮助下,目标类只实现那些非横切逻辑的程序逻辑,而其他监测代码则可以使用AOP动态织入到特定的连接点上。

5.引介(Introduction)

引介是一种特殊的增强,它为类添加一些属性和方法,这样即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态的为该业务类添加接口的实现逻辑,让这个业务类成为这个接口的实现类。

6.织入(Weaving)

织入是将增强添加到目标类具体连接点上的过程,AOP就像一台织布机,将目标类、增强或者引介编织到一起,AOP有三种织入的方式:

a.编译期间织入,这要求使用特殊的java编译器;

b.类装载期织入,这要求使用特殊的类装载器;

c.动态代理织入,在运行期为目标类添加增强生成子类的方式。

Spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。

7.代理(Proxy)

一个类被AOP织入增强后,就产生出了一个结果类,它是融合了原类和增强逻辑的代理类。

8.切面(Aspect)

切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

总结:AOP的工作重点就是如何将增强应用于目标对象的连接点上,这里首先包括两个工作:第一,如何通过切点和增强定位到连接点;第二,如何在增强中编写切面的代码。

二、AOP实例(通过Proxy代理模式)

Spring AOP使用纯java实现,不需要专门的编译过程和类装载器,它在运行期间通过代理方式向目标类织入增强代码,它更侧重于提供一种和Spring IoC容器整合的AOP实现,在Spring中,我们可以无缝的将AOP,IoC,AspectJ整合在一起。

Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,一种是基于CGLib的动态代理;

JDK1.3以后,java提供了动态代理技术,允许开发者在运行期间动态的创建接口的代理实例,JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler,其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。

下面我们来看一个JDK动态代理的例子:

1.业务接口UserService.java

package spring.aop.demo1;

public interface UserService {
void removeUser(int userId);
}

2.横切逻辑代理监视代码PerformanceMonitor.java

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo1;

public class MethodPerformance {

    private long begin;

    private long end;

    private String serviceMethod;

    public MethodPerformance(String serviceMethod) {
this.serviceMethod = serviceMethod;
this.begin = System.currentTimeMillis();
} public void printPerformance() {
this.end = System.currentTimeMillis();
long elapse = end - begin; System.out.println(serviceMethod + "花费" + elapse + "毫秒");
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo1;

public class PerformanceMonitor {

    // 通过一个ThreadLocal保存调用线程相关的性能监视信息
private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>(); // 启动对一目标方法的性能监视
public static void begin(String method) {
System.out.println("begin monitor...");
MethodPerformance mp = new MethodPerformance(method);
performanceRecord.set(mp);
} public static void end() {
System.out.println("end monitor...");
MethodPerformance mp = performanceRecord.get();
mp.printPerformance();
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

3.横切逻辑代理代码PerformanceHandler.java

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class PerformanceHandler implements InvocationHandler { private Object target; public PerformanceHandler(Object target) {
this.target = target;
} @Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
PerformanceMonitor.begin(target.getClass().getName() + "."
+ arg1.getName());
Object obj = arg1.invoke(target, arg2);// 通过反射机制调用目标对象的方法
PerformanceMonitor.end();
return obj;
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

首先,我们实现InvocationHandler接口,该接口定义了一个invoke方法,proxy最是最终生成的一个代理实例,一般不会用到,参数arg1是被代理目标实例的某个具体的方法,通过它可以发起目标实例方法的反射调用;参数arg2是通过被代理实例某一个方法的入参,在方法反射调用时候使用,通过代理将横切逻辑代码和业务类的代码编织到了一起。

我们在构造函数里通过target传入希望被代理的目标对象,将目标实例产地个method.inoke(),调用目标实例的方法。

4.通过Proxy结合PerformanceHandler创建UserService接口的代理实例:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo1;

import java.lang.reflect.Proxy;

public class UserServiceImpl implements UserService {

    @Override
public void removeUser(int userId) {
System.out.println("模拟删除用户:" + userId);
try {
Thread.currentThread().sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// 将目标业务类和横切代码编织到一起
PerformanceHandler handler = new PerformanceHandler(userService); // 根据编织了目标业务类逻辑和性能监视横切逻辑的InvocationHandler实例创建代理实例
UserService proxy = (UserService) Proxy.newProxyInstance(userService
.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), handler); proxy.removeUser(3); }
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

begin monitor...
模拟删除用户:3
end monitor...
spring.aop.demo1.UserServiceImpl.removeUser花费203毫秒

说明:上面的代码完成业务类代码和横切代码的编制工作,并生成了代理实例,newProxyInstance方法的第一个参数为类加载器,第二个参数为目标类所实现的一组接口,第三个参数是整合了业务逻辑和横切逻辑的编织器对象。使用JDK代理模式有一个限制,即它只能为接口创建代理实例,这一点我们可以从Proxy.newProxyInstance的方法签名中就可以看的很清楚,第二个参数interfaces就是需要代理实例实现的接口列表。

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的结束拦截所有父类方法的调用,并顺势织入横切逻辑。我们采用CGLib技术可以编写一个可以为任何类创建织入横切逻辑代理对象的代理创建器,下面看一个使用CGLib代理技术实现横切的一个例子:

1.CglibProxy.java

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo2;

import java.lang.reflect.Method;

import spring.aop.demo1.PerformanceMonitor;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { // private static CglibProxy proxy = new CglibProxy();
private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);// 设置需要创建子类的类
enhancer.setCallback(this);
return enhancer.create();// 通过字节码技术动态创建子类实例
} @Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
PerformanceMonitor.begin(arg0.getClass().getName() + "."
+ arg1.getName());
Object result = arg3.invokeSuper(arg0, arg2);
PerformanceMonitor.end();
return result;
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

2.UserServiceImpl.java

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.demo2;

public class UserServiceImpl{

    public void removeUser(int userId) {
System.out.println("模拟删除用户:" + userId);
} public void addUser(int userId) {
// TODO Auto-generated method stub
} public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
UserServiceImpl userService =(UserServiceImpl)proxy.getProxy(UserServiceImpl.class);
userService.removeUser(7);
}
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

begin monitor...
模拟删除用户:7
end monitor...
spring.aop.demo2.UserServiceImpl$$EnhancerByCGLIB$$4d9bdf63.removeUser花费15毫秒

总结:用户通过getProxy(Class clazz)为一个类创建动态代理对象,该代理对象通过扩展clazz创建代理对象,在这个代理对象中,我们织入横切逻辑代码。intercept是CGLib定义的Interceptor接口的方法,它拦截所有目标方法的调用,参数arg0表示目标类的实例;参数arg1表示目标类方法的反射对象;arg2表示目标类方法的参数的反射对象;arg3表示代理类实例;

我们看到输出spring.aop.demo2.UserServiceImpl$$EnhancerByCGLIB$$4d9bdf63.removeUser,这个特殊的类就是CGLib为UserService动态创建的子类。

Spring AOP的底层就是通过代理(JDK动态代理或CGlib代理)来实现AOP的,但是这种实现方式存在三个明显需要改进的地方:

a.目标类的所有方法都添加了横切逻辑,而有时,这并不是我们所期望的,我们可能只希望对业务类中的某些特定的方法添加横切逻辑;

b.我们通过硬编码的方式制定了织入横切逻辑的织入点,即在目标业务方法的开始和结束前织入代码;

c.我们手工编写代理实例的创建过程,为不同类创建代理时,需要分别编写相应的创建代码,无法做到通用;

CGLib所创建的动态代理对象的性能比JDK的高大概10倍,但CGLib在创建代理对象的时间比JDK大概多8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需重复的创建代理对象,所以比较适合CGLib动态代理技术,反之选择JDK代理。值得一提的是由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中final的方法进行代理。

三、创建增强类

Spring使用增强定义横切逻辑,同时由于Spring只支持方法连接点,增强还包括了在方法的哪一点加入横切代码的方位信息,所以增强既包含横切逻辑,还包含部分连接点的信息。

前置增强:org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定义的,下面我们看一个前置通知的小例子:

1.Waiter.java

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.beforeadvicedemo;

public interface Waiter {
void greetTo(String name);
void serverTo(String name);
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

2.GreetingBeforeAdvice.java 实现前置增强接口的横切逻辑

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.beforeadvicedemo;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice implements MethodBeforeAdvice {

    //arg0是目标类的方法,arg1是目标类方法的参数,arg2是目标类的实例
@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
String clientName = (String)arg1[0];
System.out.println("How are you! Mr." + clientName);
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

3.目标类NaiveWaiter.java和测试代码

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.beforeadvicedemo;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory; public class NaiveWaiter implements Waiter { @Override
public void greetTo(String name) {
System.out.println("great to " + name);
} @Override
public void serverTo(String name) {
System.out.println("serving "+ name);
} public static void main(String[] args) { BeforeAdvice advice = new GreetingBeforeAdvice();
Waiter waiter = new NaiveWaiter(); //Spring提供的代理工厂
ProxyFactory pf = new ProxyFactory(); //设置代理目标
pf.setTarget(waiter); //为代理目标添加增强
pf.addAdvice(advice); //生成代理实例
Waiter waiterProxy = (Waiter)pf.getProxy();
waiterProxy.greetTo("nicholaslee");
waiterProxy.serverTo("nicholaslee");
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

How are you! Mr.nicholaslee
great to nicholaslee
How are you! Mr.nicholaslee
serving nicholaslee

说明:在测试代码中,我们用到了org.springframework.aop.framework.ProxyFactory,这个内部就是使用了我们之前的JDK代理或者CGLib代理的技术,将增强应用到目标类中。Spring定义了org.springframework.aop.framework.AopProxy接口,并提供了两个final的实现类,其中:

Cglib2AopProxy使用CGLib代理技术创建代理,而JdkDynamicAopProxy使用JDK代理技术创建代理;

如果通过ProxyFactory的setInterfaces(Class[] interfaces)指定针对接口进行代理,ProxyFactory就使用JdkDynamicAopProxy;

如果是通过类的代理则使用Cglib2AopProxy,另外也可以通过ProxyFactory的setOptimize(true)方法,让ProxyFactory启动优化代理模式,这样针对接口的代理也会使用Cglib2AopProxy。

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
        BeforeAdvice advice = new GreetingBeforeAdvice();
Waiter waiter = new NaiveWaiter(); //Spring提供的代理工厂
ProxyFactory pf = new ProxyFactory();
pf.setInterfaces(waiter.getClass().getInterfaces());
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

  以上代码就指定了JdkDynamicAopProxy进行代理;

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
BeforeAdvice advice = new GreetingBeforeAdvice();
Waiter waiter = new NaiveWaiter(); //Spring提供的代理工厂
ProxyFactory pf = new ProxyFactory();
pf.setInterfaces(waiter.getClass().getInterfaces());
pf.setOptimize(true);
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

以上代码虽然指定了代理的接口,但由于setOptimize(true),所以还是使用了Cglib2AopProxy代理;

我们使用了addAdvice来添加一个增强,用户可以用该方法添加多个增强,形成一个增强链,调用顺序和添加顺序一致,下标从0开始:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.beforeadvicedemo;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice2 implements MethodBeforeAdvice {

    @Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println( "我是第一个横切逻辑");
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
public static void main(String[] args) {

        BeforeAdvice advice = new GreetingBeforeAdvice();
BeforeAdvice advice2 = new GreetingBeforeAdvice2();
Waiter waiter = new NaiveWaiter(); //Spring提供的代理工厂
ProxyFactory pf = new ProxyFactory();
pf.setInterfaces(waiter.getClass().getInterfaces());
pf.setOptimize(true); //设置代理目标
pf.setTarget(waiter); //为代理目标添加增强
pf.addAdvice(0,advice2);
pf.addAdvice(1,advice); //生成代理实例
Waiter waiterProxy = (Waiter)pf.getProxy();
waiterProxy.greetTo("nicholaslee");
waiterProxy.serverTo("nicholaslee");
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

我是第一个横切逻辑
How are you! Mr.nicholaslee
great to nicholaslee
我是第一个横切逻辑
How are you! Mr.nicholaslee
serving nicholaslee

我们还可以将以上代码更加优化一下,可以通过依赖注入来实例化:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="targetWaiter"
p:proxyTargetClass="true">
<property name="interceptorNames">
<list>
<value>greetingAdvice</value>
<value>greetingAdvice2</value>
</list>
</property>
</bean>
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Waiter waiter = (Waiter)ctx.getBean("waiter",Waiter.class);
waiter.greetTo("nicholaslee");
}

输出:

How are you! Mr.nicholaslee
我是第一个横切逻辑
great to nicholaslee

参数说明:

target:代理的目标对象;

proxyInterfaces:代理索要实现的接口,可以是多个接口,另一个别名属性是interfaces;

interceptorNames:需要织入目标对象的Bean列表,必须是实现了MethodInterceptor或者aop.Advisor的Bean,配置的顺序对应调用顺序;

singleton:返回的代理是否单实例,默认为单实例;

optimize:当设置为true的时候,强制使用CGLib代理;

proxyTargetClass:是否对类进行代理(而不是针对接口进行代理),设置为true后,使用CGLib代理;

这个时候我们使用了JDK代理技术,如果我们想使用CGLib代理,则可以更改参数:

p:proxyTargetClass="true"

因为CGLib代理创建代理慢,但是创建的代理对象效率非常高,所以比较适合singleton的代理;

下面我们看一个后置增强,org.springframework.aop.AfterReturningAdvice,表示在目标方法执行后试试增强:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.afteradvicedemo;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class GreetingAfterAdvice implements AfterReturningAdvice {

    @Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("Please enjoy youself~");
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

下面看一下环绕增强,org.aopalliance.intercept.MethodInterceptor,表示在目标方法执行前后实施增强:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.interceptoradvicedemo;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; public class GreetingInterceptor implements MethodInterceptor { @Override
public Object invoke(MethodInvocation arg0) throws Throwable {
Object[] args = arg0.getArguments();// 获取目标方法参数
String clientName = (String) args[0];
System.out.println("How are you:" + clientName);
Object obj = arg0.proceed();
System.out.println("just enjoy yourself");
return obj;
}
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

在环绕增强时,arg0.proceed()通过proceed反射调用目标实例相应的方法;

下面是异常抛出增强:org.springframework.aop.ThrowsAdvice,表示在目标方法抛出异常后实施增强;异常抛出增强最适合的应用场景是事务管理,当参与事务的某个Dao发生异常时,事务管理器就必须去回滚事务,下面看一个模拟的例子:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.throwsadvicedemo;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;

public class TransactionManager implements ThrowsAdvice {

    //ThrowsAdvice异常抛出增强接口没有定义任何方法,它只是一个标示接口
//在运行期Spring使用反射的机制自行判断,我们必须采用以下签名的增强方法
public void afterThrowing(Method method, Object[] args, Object target,Exception ex) throws Throwable{
System.out.println("---------------");
System.out.println("method:" + method.getName());
System.out.println("抛出异常:" + ex.getMessage());
System.out.println("成功回滚事务。");
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.throwsadvicedemo;

import java.sql.SQLException;

import org.springframework.aop.framework.ProxyFactory;

public class UserServiceImpl {

    public void removeUser(int userId) {
System.out.println("模拟删除用户:" + userId);
throw new RuntimeException("运行异常。");
} public void addUser(int userId) {
System.out.println("添加用户" + userId);
throw new RuntimeException("数据库插入异常。");
} public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
TransactionManager tran = new TransactionManager();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(userService);
pf.addAdvice(tran);
UserServiceImpl user = (UserServiceImpl)pf.getProxy();
user.removeUser(0);
user.addUser(1);
}
}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

模拟删除用户:0
---------------
method:removeUser
抛出异常:运行异常。
成功回滚事务。

添加用户1
---------------
method:addUser
抛出异常:数据库插入异常。
成功回滚事务。

也可以配置注入方式:

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
<bean id="throwsManager" class="spring.aop.throwsadvicedemo.TransactionManager" />
<bean id="throwsTarget" class="spring.aop.throwsadvicedemo.UserServiceImpl" />
<bean id="throwsAdvice" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="throwsTarget"
p:proxyTargetClass="true"
p:singleton="false" >
<property name="interceptorNames">
<list>
<value>throwsManager</value>
</list>
</property>
</bean>
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

最后来看一下引介增强:org.springframework.aop.IntroductionInterceptor,表示在目标类中添加一些新的方法和属性;引介增强是一种比较特殊的增强类型,它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,我们可以为目标类添加一个接口的实现,即原来目标类未实现某个接口,通过引介可以为目标类创建实现某接口的代理。

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.introductionadvicedemo;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor; import spring.aop.demo1.PerformanceMonitor; public class ControllablePerformanceMonitor extends
DelegatingIntroductionInterceptor implements Monitorable { /**
*
*/
private static final long serialVersionUID = -5983845636084465442L; private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>(); public Object invoke(MethodInvocation mi) throws Throwable {
Object obj = null;
// 通过判断其状态决定是否开启性能监控功能
if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {
PerformanceMonitor.begin(mi.getClass().getName() + "."
+ mi.getMethod().getName());
obj = super.invoke(mi);
PerformanceMonitor.end();
} else {
obj = super.invoke(mi);
}
return obj;
} @Override
public void setMonitorActive(boolean active) {
MonitorStatusMap.set(active);
} }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.introductionadvicedemo;

public interface Monitorable {

    void setMonitorActive(boolean active);

}
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
package spring.aop.introductionadvicedemo;

import org.springframework.aop.framework.ProxyFactory;

public class ForumService {

    public void removeUser(int userId) {
System.out.println("模拟删除用户:" + userId);
} public void addUser(int userId) {
System.out.println("添加用户" + userId);
} public static void main(String[] args) {
ForumService forumService = new ForumService();
ControllablePerformanceMonitor advice = new ControllablePerformanceMonitor();
ProxyFactory pf = new ProxyFactory();
pf.setInterfaces(Monitorable.class.getInterfaces());
pf.setTarget(forumService);
pf.setOptimize(true);
pf.addAdvice(advice);
ForumService forum = (ForumService)pf.getProxy();
Monitorable monitorAble =(Monitorable)forum;
monitorAble.setMonitorActive(true);
forum.removeUser(1); } }
【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

输出:

begin monitor...
模拟删除用户:1
end monitor...
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeUser花费32毫秒

说明:

1. Monitorable monitorAble =(Monitorable)forum; 我们可以这么转换,说明返回的代理实例确实引入了Monitorable接口方法的实现;

2. pf.setInterfaces(Monitorable.class.getInterfaces()); 引介增强需要制定所实现的接口;

3. pf.setOptimize(true); 由于只能通过为目标类创建子类的方式生成音节增强的代理,所以必须选择CGLib代理;

部分博文为网络资料摘录,如有侵犯到您的权利,请尽快与我联系,以便修正。

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理的更多相关文章

  1. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

  2. Spring AOP详解及简单应用

    Spring AOP详解   一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址: ...

  3. 转:Spring AOP详解

    转:Spring AOP详解 一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址: ...

  4. Spring Aop 详解二

    这是Spring Aop的第二篇,案例代码很详解,可以查看https://gitee.com/haimama/java-study/tree/master/spring-aop-demo. 阅读前,建 ...

  5. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  6. &lbrack;Spring学习笔记 5 &rsqb; Spring AOP 详解1

    知识点回顾:一.IOC容器---DI依赖注入:setter注入(属性注入)/构造子注入/字段注入(注解 )/接口注入 out Spring IOC容器的使用: A.完全使用XML文件来配置容器所要管理 ...

  7. spring AOP详解四

    AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存.对象池管理等.AOP 实现的关键就在于 ...

  8. spring AOP详解三

    CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的结束拦截所有父类方法的调用,并顺势织入横切逻辑.我们采用CGLib技术可以编写一个可以为任何类创建织入横切逻辑代理对象 ...

  9. &lbrack;转载&rsqb;Spring配置文件详解一:

    原文地址:与base-package="com.xx">Spring配置文件详解一:<context:annotation-config/>与<contex ...

随机推荐

  1. JS魔法堂:精确判断IE的文档模式by特征嗅探

    一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...

  2. J2EE面试题

    J2EE面试题 J2EE相关基础知识 1.面向对象的特征有哪些方面  1.  抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只 ...

  3. ASP&period;NET Core MVC 2&period;1 *参数验证

    本文讨论ASP.NET Core 2.1中与ASP.NET Core MVC / Web API控制器中的模型绑定相关的功能.虽说这是一个功能,但从我的角度来看,它更像是一个错误修复! 请注意,我使用 ...

  4. JavaScript push&lpar;&rpar; 方法

    定义和用法: push() :可向数组的末尾添加一个或多个元素,并返回新的长度. 语法 arrayObject.push(newelement1,newelement2,....,newelement ...

  5. easyui-tab标签

    一. 加载方式 //class 加载方式<div id="box" class="easyui-tabs" style="width:500px ...

  6. 20175204 张湲祯 2018-2019-2《Java程序设计》第三周学习总结

    20175204 张湲祯 2018-2019-2<Java程序设计>第三周学习总结 教材学习内容总结 -第四章类与对象要点: -面向对象语言三个特性:封装性:继承:多态: -类:1.类是组 ...

  7. javascript获取值

    <div id='name'>张三</div> $('#name').val() $(name).val() 以上两个都可以得到值,第一种用的比较多.

  8. laravel 使用 php artisan make&colon;model到指定目录(controller同理)

    在 \app\Models 目录下创建一个BusinessProduct模型文件 D:\htdocs\PHPTutorial\WWW\gf>php artisan make:model /Mod ...

  9. 如何解决JSP页面顶端报错 The superclass &quot&semi;javax&period;servlet&period;http&period;HttpServlet&quot&semi; was not found on the Java Build Path

    题目有点长,昨天刚接触jsp,按照网上的教程安装完 tomcat 和 eclipse EE 之后,新建jsp文件却出现了如下报错: The superclass "javax.servlet ...

  10. SQL——查询一段时间内每天的数据,按天将数据封装进行封存

    DROP TABLE IF EXISTS `T_ROTA_RECORD`; CREATE TABLE `T_ROTA_RECORD` ( `id` ) NOT NULL AUTO_INCREMENT, ...