## **模式的定义**
代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
## **静态代理**
静态代理就是需要手动编写代理类,再编译成源码,在代码里是真实存在的片段。
代码模拟简单的事物提交
1. 创建dao接口以及实现dao
```
public interface UserDao {
void add();
}
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("add");
}
}
```
2. 创建代理方法
```
public class UserDaoProxy implements UserDao {
private UserDao userDao;
public UserDaoProxy(UserDao target) {
this.userDao = target;
}
@Override
public void add() {
System.out.println("开始事物");
userDao.add();
System.out.println("提交事物");
}
}
```
3. 客户端条用
```
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserDaoProxy proxy = new UserDaoProxy(userDao);
proxy.add();
}
```
很简单的实现了静态代理处理事物的功能,但是假设我们100个dao就意味着需要创建100个proxy,很显然代码非常的冗余,JDK给我们提供了一种更好的技术,叫做JDK动态代理。
## **JDK动态代理**
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的`java.lang.reflect.Proxy`,通过固定的规则生成
其步骤如下:
1. 编写一个委托类的接口,即静态代理的(UserDao接口)
2. 实现一个真正的委托类,即静态代理的(UserDaoImpl类)
3. 创建一个动态代理类,实现`InvocationHandler`接口,并重写该`invoke`方法
4. 在测试类中,生成动态代理的对象。
步骤1和2和静态代理的写法一致
步骤3创建动态代理类:
```
public class TransHandler implements InvocationHandler {
private Object target;
public TransHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事物");
Object invoke = method.invoke(target, args);
System.out.println("提交事务");
return invoke;
}
}
```
步骤4生成代理对象:
```
public static void main(String[] args) {
// 代理对象
UserDao userDao = new UserDaoImpl();
TransHandler transHandler = new TransHandler(userDao);
// 获取代理对象的类加载器
ClassLoader loader = userDao.getClass().getClassLoader();
// 获取代理对象的实现接口
Class<?>[] interfaces = userDao.getClass().getInterfaces();
UserDao userDaoImpl = (UserDao) Proxy.newProxyInstance(loader, interfaces, transHandler);
userDaoImpl.add();
}
```
## **CGLIB动态代理**
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
使用CGLIB动态代理实现事物功能
1. maven中添加CGLIB依赖
```
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
```
2. 创建动态代理类
```
public class CglibProxy implements MethodInterceptor {
// 代理的对象
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 指定cglib生成的子类继承的父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) {
System.out.println("开启事物");
Object invoke = proxy.invoke(target, args);
System.out.println("提交事物");
return invoke;
}
}
```
3. 客户端生成创建代理对象
```
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
CglibProxy cglibProxy = new CglibProxy();
UserDao userDaoImpl = (UserDao) cglibProxy.getInstance(userDao);
userDaoImpl.add();
}
```
cglib使用底层字节码技术,动态创建代理对象(UserDao)的子类,并重写父类的方法。
## **JDK动态代理和CGLIB动态代理的区别**
| JDK动态代理 | CGLIB动态代理 |
| --- | --- |
| 利用反射机制生成一个实现代理接口的匿名类 | 通过动态字节码技术创建虚拟子类实现 |
| 代理对象类需要实现接口 | 无需实现接口 |
Spring AOP采用哪种动态代理模式呢?
1. 代理对象实现了接口,默认情况下会采用JDK动态代理来实现AOP,例如Service层,Dao层等
2. 代理对象没实现接口,会采用cglib动态代理模式实现AOP,例如Controller层