代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,扩展目标对象的功能。
在 Java 中代理模式有静态代理和动态代理两种实现方式:
- 静态代理:代理类在程序编译时生成
- 动态代理:代理类在程序运行时生成
无论是静态代理还是动态代理,都可以基于 JDK 自带的类库实现,也可以基于第三方类库实现。
基于 JDK 实现的静态代理
public interface IUserDao {
public void save();
}
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("保存数据");
}
}
public class UserDaoProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务");
target.save();
System.out.println("提交事务");
}
}
import org.junit.Test;
public class StaticUserProxy {
@Test
public void testStaticProxy(){
IUserDao target = new UserDao();
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();
}
}
基于 JDK 实现的动态代理
public interface IUserDao {
public void save();
}
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("保存数据");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return null;
}
});
}
}
import org.junit.Test;
public class TestProxy {
@Test
public void testDynamicProxy (){
IUserDao target = new UserDao();
System.out.println(target.getClass());
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}
基于 JDK 实现的静态代理其实跟平常写代码没什么不同,无非就是在目标类的基础上再加一层封装,将额外的代码逻辑加上去。JDK 静态代理无法自动化批量修改目标类,需要更多的人工介入。
基于 JDK 实现的动态代理是运用了 Java 的反射,对目标类的字节码进行修改后生成一个新的类。JDK 动态代理可以运用反射来解析方法名或注解,从而能够自动化批量修改目标类,更智能更便利。可以说, JDK 静态代理相当于静态网页,而 JDK 动态代理相当于动态网页,哪一个功能更强大我想大家都很清楚。
AOP
面向切面编程(Aspect-oriented programming,AOP)是计算机科学中的一种程序设计思想,是面向对象编程(OOP)的延伸。AOP 可以让程序员在不影响原有功能的前提下扩展功能,也可以将一些功能代码从业务逻辑代码中分离出来。也就是说,AOP 可以提高代码的模块化程度,降低代码之间的耦合度。AOP 的应用场景有日志记录,性能统计,安全控制,事务处理,异常处理等等。
AOP 在 Java 中的实现方式有 JDK 静态代理、JDK 动态代理、AspectJ 静态代理、Cglib 动态代理等。AspectJ 意思就是 Java的 Aspect,它是一个代码编译器,在 Java 编译器的基础上增加了一些它自己的关键字识别和编译方法。Cglib 是一个可以修改字节码的 Java 类库,JDK 动态代理必须在目标类实现了接口的情况下才能进行动态代理,Cglib 则没有这个限制。
Spring AOP
Spring AOP 采用了 JDK 动态代理和 Cglib 动态代理两种方式实现 AOP,在初始化 IoC 容器的过程中对业务方法进行增强,具体使用哪种方式,Spring 会对做如下判断:
- 如果目标对象实现了接口,默认情况下会采用 JDK 动态代理实现 AOP
- 如果目标对象实现了接口,程序员可以配置强制使用 Cglib 实现 AOP
- 如果目标对象没有实现了接口,程序员也没有配置使用 Cglib,Spring 会自动在 JDK 动态代理和 Cglib 之间转换
Spring AOP 在基于注解配置的情况下,需要依赖于 AspectJ 提供的标准注解,而基于 XML 配置 的情况下则不需要依赖 AspectJ。因此 Spring AOP 只是复用了AspectJ 的注解,并没有其他依赖 AspectJ 的地方。