什么是AOP?

  • AOP Aspect Oriented Programing 面向切面编程
  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
  • Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
  • AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

AOP底层原理

就是代理机制:

  • 动态代理:(JDK中使用)
  • JDK的动态代理,对实现了接口的类生成代理.

Spring的AOP代理

  • JDK动态代理:对实现了接口的类生成代理
  • CGLib代理机制:对类生成代理

AOP的术语

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  • Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
  • Target(目标对象):代理的目标对象
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
  • spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
  • Aspect(切面): 是切入点和通知(引介)的结合
解释以上名词:
  • joinpoit:连接点,指的是那些方法【可以】被拦截

  • pointcut:切入点,对哪些joinpoint进行拦截

  • advice:通知,指的是 【要 】增强的代码,(方法级别的增强)

  • introduction:引介,指的是一种特殊方式的advice(类级别的增强,在原有类上添加一个属性或者方法)

  • target:目标对象,呗增强的对象。

  • weaving(织入):是指增强(advice)应用到目标对象(targe)来创建新的代理对象的过程。

  • proxy:代理对象

  • Aspect(切面):是切入点和通知(引介)的结合(允许有多个切点和多个通知的结合)


以下是底层实现demo演示

AOP的底层实现

  • JDK动态代理(对实现接口的类进行代理):

接口类:

package cn.spring3.demo1;
/**
 * @author NOP
 * DAO接口
 */
public interface UserDao {
	public void add();
	public void update();
}

实现类:

package cn.spring3.demo1;
public class UserDaoImpl implements UserDao {
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("添加用户");
	}
	public void update() {
		// TODO Auto-generated method stub
		System.out.println("修改用户");
	}
}

传统方式调用:

package cn.spring3.demo1;
import org.junit.Test;
public class SpringTest1 {
	@Test
	public void demo1() {
		UserDao userDao = new UserDaoImpl();
		userDao.add();
		userDao.update();
	}
}

如果增强add方法,怎么做呢? 方法一:

package cn.spring3.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * @author NOP JDK动态类里的机制
 * 直接实现Handler,所以JDKProxy自身就是Handler
 */
public class JDKProxy implements InvocationHandler {
	private UserDao userDao;
	public JDKProxy(UserDao userDao) {
		super();
		this.userDao = userDao;
	}
	public UserDao createProxy() {
		/**
		 * invocationHandler两种方式,一种匿名内部类的方式见JDKProxy1.java
		 */
		UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass()
				.getClassLoader(), userDao.getClass().getInterfaces(), this);
		return proxy;
	}
	//调用目标对象的任何一个方法都相当于调用invoke();
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		if("add".equals(method.getName())){
			System.out.println("===日志记录===");
			Object result = method.invoke(userDao, args);
			return result;
		}
		return method.invoke(userDao, args);
	}
}

方法二:

package cn.spring3.demo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author NOP JDK动态类里的机制
 */
public class JDKProxy1 {
	private UserDao userDao;

	public JDKProxy1(UserDao userDao) {
		super();
		this.userDao = userDao;
	}

	public UserDao createProxy() {
		/**
		 * invocationHandler两种方式
		 */
		UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass()
				.getClassLoader(), userDao.getClass().getInterfaces(),
				new InvocationHandler() {
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// TODO Auto-generated method stub
						if (method.getName().equals("add")) {
							System.out.println("===匿名内部类方式记录logs日志====");
							Object result = method.invoke(userDao, args);
							return result;
						}
						return method.invoke(userDao, args);
					}
				});
		return proxy;
	}
}

编写测试类 对应JDKProxy.java

@Test
	public void demo2() {
		//被代理对象
		UserDao userDao = new UserDaoImpl();
		//创建代理对象的时候传入被代理对象
		UserDao proxy = new JDKProxy(userDao).createProxy();
		proxy.add();
		proxy.update();
	}
	测试结果:
===日志记录===
添加用户
修改用户

对应JDKProxy1.java

@Test
	public void demo3() {
		UserDao userDao = new UserDaoImpl();
		UserDao proxy = new JDKProxy1(userDao).createProxy();
		proxy.add();
		proxy.update();
	}
	测试结果:
===匿名内部类方式记录logs日志====
添加用户
修改用户

  • 使用CGLib生成代理对象JDK动态代理(对实现接口的类进行代理):
    • CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。 Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成

    • Hibernate生成持久化类的javassist.

    • CGLIB生成代理机制:其实生成了一个真实对象的子类.

    • 下载cglib的jar包.

      • 现在做cglib的开发,可以不用直接引入cglib的包.已经在spring的核心中集成cglib.

创建一个产品类(目标对象):

package cn.spring3.demo2;
public class ProductDao {
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("添加商品");
	}
	public void update() {
		// TODO Auto-generated method stub
		System.out.println("修改商品");
	}
}

//传统方式

	@Test
	public void demo1(){
		ProductDao productDao = new ProductDao();
		productDao.add();
		productDao.update(); 
	}

如果用CGLib的话,创建CGLib类

package cn.spring3.demo2;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * @author NOP 使用CGLib生成代理对象
 */
public class CGLibProxy implements MethodInterceptor {
	private ProductDao productDao;
	// 构造方法
	public CGLibProxy(ProductDao productDao) {
		super();
		this.productDao = productDao;
	}
	public ProductDao createProxy() {
		// 使用cglib生成代理
		// 1、创建核心类
		Enhancer enhancer = new Enhancer();
		// 2、为其设置父类
		enhancer.setSuperclass(productDao.getClass());
		// 3、设置回调:
		enhancer.setCallback(this);
		// 4、创建代理
		return (ProductDao) enhancer.create();
	}
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodproxy) throws Throwable {
		// TODO Auto-generated method stub
		if("add".equals(method.getName())){
			System.out.println("===cglib日志记录===");
			Object result =	methodproxy.invokeSuper(proxy, args);
		//	Object result = method.invoke(productDao, args);
			return result;
		}
		return methodproxy.invokeSuper(proxy   , args);
	}
}
//cglib方式动态代理
	@Test
	public void demo2(){
		ProductDao productDao = new ProductDao();
		ProductDao proxy= new CGLibProxy(productDao).createProxy();
		proxy.add();
		proxy.update();
	}
	测试结果:
===cglib日志记录===
添加商品
修改商品

=========================华丽的分割线===========================

结论:

Spring框架,如果类实现了接口,就使用JDK的动态代理生成代理对象,如果这个类没有实现任何接口,使用CGLIB生成代理对象.