Spring基础知识(5)-AOP

一、什么是 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库生成目标对象的子类程序中应优先对接口创建代理,便于程序解耦维护







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值