动态代理是什么
AOP(面向切面编程)是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。
动态代理有两种:一是JDK动态代理,二是CGLib动态代理。
怎么设置动态代理
- 我们需要先写服务层代码:
@Service
public class MathService implements IMathService {
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public int div(int a, int b) {
return a/b;
}
}
- 再写一个切面类:
@Component
@Aspect
public class MathAspect {
@Before("execution(public int live.sunhao.math.service.MathService.*(..))")
public void before(JoinPoint jp) {
Signature signature = jp.getSignature();
System.out.println("The "+signature.getClass()+" method begins");
}
}
- 接着,我们配置xml文件:
- a.JDK动态代理的配置
<context:component-scan base-package="live.sunhao"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- b.GCLib动态代理的配置(Spring3.2之后开始自身支持CGLib)
首先我们需要引入一个jar包:
然后配置xml文件,将标签属性proxy-target-class设置为true即可
<context:component-scan base-package="live.sunhao"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
JDK与CGLib动态代理的区别
这里我们通过测试类来介绍两种动态代理的区别:
JDK动态代理
首先按照JDK动态代理的配置方法配置好xml文件,同时写好测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
IMathService mathService = applicationContext.getBean(IMathService.class);//这里参数为何必须是接口,下面会讲到
System.out.println(mathService.getClass());
Class[] clazz = mathService.getClass().getInterfaces();
for (Class c : clazz) {
System.out.println(c);
}
applicationContext.close();
}
}
控制台输出结果如下
我们可以发现,JDK动态代理的代理类,也实现了IMathService接口,该代理类与MathService类没有继承关系。所以我们通过 getBean() 方法在IOC容器中获取该代理对象时,参数必须是接入点匹配的类的接口,而不是接入点匹配的类。
CGLib动态代理(Spring3.2之后开始自身支持CGLib)
先按照CGLib动态代理的配置方法配置好xml文件,同时写测试类:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
IMathService mathService = applicationContext.getBean(IMathService.class);//这里参数可以是接口,也可是实现类
System.out.println(mathService.getClass());
//我们这里控制台输出代理对象所在类的父类
System.out.println(mathService.getClass().getSuperclass());
applicationContext.close();
}
}
控制台输出如下:
我们可以看到CGLib动态代理的 代理对象所在类的父类是MathService类,所以我们所以我们通过 getBean() 方法在IOC容器中获取该代理对象时,参数既可以是接入点匹配的类,也可以是该类实现的接口。
AOP中隐含的动态代理
一个类中的方法被@Transactional注解修饰,则Spring自动为该类创建代理类及其代理对象
懂了这个原理便可以帮助我们理解Spring中的事务是什么原理。
首先我们把切面类删掉
然后我们来配置xml文件
该引的jar包如下:
我们给MathService类中的任意方法加上@Transactional注解即可,
Test类如下:
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
IMathService mathService = applicationContext.getBean(IMathService.class);
System.out.println(mathService.getClass());
System.out.println(mathService.getClass().getSuperclass());
applicationContext.close();
}
我们会发现控制台输出:
是JDK自动代理