一、什么是 AOP
AOP Aspect Oriented Programing 面向切面编程 , 人们说AOP 是对 OOP (面向对象编程)思想一个延伸。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(主流的AOP的应用方向:性能监视、事务管理、安全检查、缓存)
如果把共用的方法写在类里边,就无法复用,而用继承可以解决这个问题,如果不用继承采用AOP(其实AOP横向抽取机制就是代理机制),也就是说如果想让很多DAO都 实现这个共用的方法,可以为具体的类创建一个代理类,
如果有了代理类,我们访问该类的任何方法,都会经过代理类
在传统的代码结构中:我们会通过类的继承来完成一些代码的复用,这是一种纵向结构代码复用。
☆☆☆:AOP 面向切面编程 底层原理 代理!!!
相关术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
二、 AOP 的底层实现
AOP 底层使用的代理技术 : JDK动态代理 和 CGlib的动态代理
1、 JDK动态代理
原理: 针对内存中Class对象,使用类加载器 动态为目标对象的实现接口创建代理类
* 代理类 是动态创建的, 代理类 和 被代理对象 实现相同接口
* 被代理对象 必须要实现 接口 (JDK代理 只能针对接口 进行代理 )
package lsq.spring.aop.jdkproxy;
/**
* 用户数据库操作
*
* @author lishanquan
*
*/
public interface UserDao {
public void add();
public void search();
}
package lsq.spring.aop.jdkproxy;
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加用户……");
}
@Override
public void search() {
System.out.println("查询用户……");
}
}
package lsq.spring.aop.jdkproxy;
import org.junit.Test;
public class JDKProxyTest {
@Test
//代理前操作
public void demo1(){
//没有代理
UserDao userDao = new UserDaoImpl();
userDao.add();
userDao.search();
}
效果:
使用JDK动态代理:
package lsq.spring.aop.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK代理
*
* @author lishanquan
*
*/
public class MyJDKProxy implements InvocationHandler{
//把代理对象作为成员变量
private UserDao userDao;
//通过被代理对象构造代理对象
public MyJDKProxy(UserDao userDao){
this.userDao = userDao;
}
/**
* 使用JDK进行动态代理
* @return
*/
public UserDao createJDKProxy(){
return (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
}
@Override
//访问被代理对象的任何方法,都会执行invoke
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//针对add方法进行增强,记录日志……
if(method.getName().equals("add")){
//如果访问的是add方法
System.out.println("记录日志……");
return method.invoke(userDao, args);
}else{
//其它方法
return method.invoke(userDao, args);
}
}
}
package lsq.spring.aop.jdkproxy;
import org.junit.Test;
public class JDKProxyTest {
@Test
//测试JDK动态代理
public void demo2(){
//创建被代理对象
UserDao userDao = new UserDaoImpl();
//根据被代理对象创建代理对象
MyJDKProxy jdkProxy = new MyJDKProxy(userDao);
UserDao proxy = jdkProxy.createJDKProxy();
//执行代理对象方法
proxy.add();
proxy.search();
}
}
使用JDK动态代理的效果:
2、 使用CGlib 完成动态代理
* JDK 动态代理原理, 为目标对象 接口生成代理对象 ,对于不使用接口的业务类,无法使用JDK动态代理
CGLIB(Code Generation Library)是一个开源项目!
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持CGlib 来实现PO字节码的动态生成。
* Hibernate 默认PO 字节码生成技术 javassist
CGLIB 是一个第三方技术,使用时 ,需要下载 jar 包
http://sourceforge.net/projects/cglib/
* Spring3.2 版本, spring-core jar包 已经集成 cglib 开发类
☆原理 : CGlib采用非常底层字节码技术,可以为一个类创建子类,解决无接口代理问题
package lsq.spring.aop.cglibproxy;
/**
* 商品操作类(被代理对象)
*
* @author lishanquan
*
*/
public class ProductDao {
public void addProduct(){
System.out.println("添加商品……");
}
public void deleteProduct(){
System.out.println("删除商品……");
}
}
package lsq.spring.aop.cglibproxy;
import org.junit.Test;
public class CGLibProxyTest {
@Test
//未使用代理
public void demo1(){
ProductDao productDao = new ProductDao();
productDao.addProduct();
productDao.deleteProduct();
}
}
未使用代理效果:
使用CGLIB动态代理:
package lsq.spring.aop.cglibproxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* CGLIB代理
*
* @author lishanquan
*
*/
public class MyCGLIBProxy implements MethodInterceptor {
//目标对象
private ProductDao productDao;
//通过构造器传入被代理对象
public MyCGLIBProxy(ProductDao productDao){
this.productDao = productDao;
}
//创建代理
public ProductDao createCglibProxy(){
//创建代理的核心对象
Enhancer enhancer = new Enhancer();
//设置被代理对象(为类创建子类)
enhancer.setSuperclass(productDao.getClass());
//设置回调函数
enhancer.setCallback(this);
//返回代理(返回代理子类对象)
return (ProductDao) enhancer.create();
}
@Override
// 被代理对象所有方法执行 ,都会调用 intercept 方法
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
// 为 addProduct 计算运行时间
if(method.getName().equals("addProduct")){
//当前执行方法
long start = System.currentTimeMillis();
Object result = methodProxy.invokeSuper(proxy, args);
long end = System.currentTimeMillis();
System.out.println("addProduct方法运行时间 : " + (end - start));
return result;
}else{
// 不进行增强
return methodProxy.invokeSuper(proxy, args);
}
}
}
package lsq.spring.aop.cglibproxy;
import org.junit.Test;
public class CGLibProxyTest {
@Test
//使用CGLIB代理
public void demo2(){
//创建被代理对象
ProductDao productDao = new ProductDao();
//创建代理
MyCGLIBProxy cglibProxy = new MyCGLIBProxy(productDao);
ProductDao proxy = cglibProxy.createCglibProxy();
//执行代理对象方法
proxy.addProduct();
proxy.deleteProduct();
}
}
使用代理效果:
结论:
1).若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2).若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类程序中应优先对接口创建代理,便于程序解耦维护