优点:解耦
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要功能
日志记录,性能统计,安全控制,事务处理,异常处理等等。
主要意图
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AOP相关概念
1.方面(Aspect)
2.连接点(Joinpoint)
3.通知(Advice): 各种类型的通知包括“around”、“before”和“throws”通知。
许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
Spring中定义了四个advice:
BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
4.切入点(Pointcut)
5.引入(Introduction): 添加方法或字段到被通知的类。
6.目标对象(Target Object): 被通知或被代理对象。
7.AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。
8.织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
Spring AOP代理对象的生成
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
静态代理
JDK动态代理
CGlib动态代理
静态代理
静态代理很简单,咱们自己在写代码的时候都会写到这种类似静态代理的代码。简单来说,就是把被代理类作为参数传给代理类的构造方法,让代理类替被代理类实现更强大的功能。
public class StaticProxyTest {
public static void main(String[] args) {
UserService userService = new UserService();
LogProxy logProxy = new LogProxy(userService);
logProxy.addUser();
logProxy.deleteUser();
}
}
interface IUserService{
void addUser();
void deleteUser();
}
class UserService implements IUserService{
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
}
//日志代理
class LogProxy implements IUserService{
//目标类
private UserService target;
public LogProxy(UserService target){
this.target = target;
}
@Override
public void addUser() {
System.out.println("记录日志开始");
target.addUser();
System.out.println("记录日志结束");
}
@Override
public void deleteUser() {
System.out.println("记录日志开始");
target.deleteUser();
System.out.println("记录日志结束");
}
}
动态代理在Spring源码中,用到的动态代理主要有两种,JDK动态代理以及CGLib动态代理。
两者主要区别是:
JDK动态代理一般针对实现了接口的类生成代理。
目标对象没有实现接口,则默认会采用CGLIB代理。如果目标对象实现了接口,可以强制使用CGLIB实现代理。
相同点:两种动态代理本质上都是字节码组装
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标对象实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
CGLIB代理模式下每一个目标对象创建一个子类。每一个代理实例会生成两个对象:实际代理对象和它的一个实现了通知的子类实例。
且CGLib的效率没有使用JDK代理机制高,速度平均要慢8倍左右。
JDK动态代理的代理类一般需要实现接口
public class JdkProxyTest {
public static void main(String[] args) {
IPersonService personService = JdkDynamicProxy.getProxy();
personService.addPerson();
personService.deletePerson();
}
}
interface IPersonService{
void addPerson();
void deletePerson();
}
class PersonService implements IPersonService{
@Override
public void addPerson() {
System.out.println("添加人物");
}
@Override
public void deletePerson() {
System.out.println("删除人物");
}
}
/**
* newProxyInstance方法参数说明:
* ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
* Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
* InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
*/
class JdkDynamicProxy{
public static IPersonService getProxy(){
IPersonService personService = new PersonService();
IPersonService proxy = (IPersonService) Proxy.newProxyInstance(IPersonService.class.getClassLoader(), new Class<?>[]{IPersonService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志开始");
Object obj = method.invoke(personService, args);
System.out.println("记录日志结束");
return obj;
}
});
return proxy;
}
}
CGLib动态代理
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Train t = (Train)proxy.getProxy(Train.class);
t.move();
}
}
class Train {
public void move(){
System.out.println("火车行驶中...");
}
}
class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* obj 目标类的实例
* m 目标方法的反射对象
* args 方法的参数
* proxy代理类的实例
*/
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("日志开始...");
//代理类调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("日志结束...");
return null;
}
}
原理分析
下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,假设我们要对下面这个用户管理进行代理:
public interface UserMgr {
void addUser();
void de