您的位置:首页 > 编程语言 > Java开发

Spring基础知识(5)-AOP

2016-05-12 11:02 585 查看
一、什么是 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库生成目标对象的子类程序中应优先对接口创建代理,便于程序解耦维护
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: