那些年我们一起着迷的Spring:AOP(八)

什么是AOP
AOP 的概念

  • AOP : Aspect Oriented Programming 的缩写,意为:面向切面编程,通过
    预编译的方式运行期动态代理实现程序功能的统一维护的一种技术
  • 主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等。

切面是和功能垂直的。
这里写图片描述
以事务处理为例,我们希望任何数据库操作的方法,像update,insert,delete,我们不希望在每一个方法上自己手动地去开启事务,得到连接,提交事务再关闭连接。这样做成本大,开发效率低,所以希望有一种方式对于开发人员是透明的。执行到某一个方法,或者说某一类型具有某种特点的方法,或者是到某一层的类,比如说Service或者是以Service结尾的这种类的时候,我们就会开启这种事务控制,这就是一种实现统一维护的技术。

技术实现的方式:

  • 预编译
    • AspectJ
  • 运行期动态代理(JDK 动态代理、CGLib 动态代理)
    • SpringAOP、JbossAOP
      相关概念
      切面:比如说我们关注事务,那这个事务就是我们的关注点,它横切多个对象。这个对象如何理解?事务可以横切多个Service实现类的对象,也就是说在多个对象执行的过程中都被事务控制。
      连接点:特定的点,有可能是一个类中的某个方法执行的开始等。
      通知:按照刚才对于连接点的理解,通知就是就是这个方法执行的时候额外执行的我们的切面的动作。
      切入点:就是我们如何在切面中去匹配一个具体的连接点。
      引入:就像编译器动态修改class文件来为一个类增加新的属性和方法。
      目标对象:比如说有一个订单的Service和一个商品的Service。切面会通知到所有的Service在执行到数据库操作的时候加上事务的控制,这两个Service就是目标对象。
      AOP代理:它的最终目的就是用来执行切面的契约(包括通知方法执行等功能)。它是由AOP框架创建的对象,在我们开发的时候不知道它的存在,也不知道它究竟会被创建成什么样。
      织入:就是把切面和对象关联起来,然后创建一个被通知的对象。

1.advice:通知,或者成为增强,定义了-做什么
2.pointCut:切点,定义了-在哪里做
3.aspect:切面,advice和pointCut的结合,定义了-什么时候,在哪里,做什么
Advice的类型

Spring 中的 AOP用途
提供声明是的企业服务,EJB 的替代服务
允许用户定制自己的方面,完成 OOP 和 AOP 的互补使用
Spring 的 AOP 实现
纯 java 实现,不需控制类加载器层次
目前只支持方法执行连接点(通知 Spring Bean 的方法执行)
不是为了提供最完整的 AOP 的实现,侧重 AOP 和 Spring IoC 容器之间的整合
Spring AOP (动态织入)不与 AspectJ(静态织入,依赖特殊编辑器 ajc) 竞争,从而提供综合全面的 AOP 解决方案。
有接口与无接口AOP 实现区别
有接口的 Spring AOP 默认使用 JavaSE 动态代理。(使得任何接口(或接口集)都可以被代理)
无接口使用 CGLIB 代理

补充

AOP技术其实是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术。

字节码增强技术

AOP技术其实是字节码增强技术,JVM提供的动态代理本质就是字节码增强技术。

动态代理

Java 动态代理机制详解
InvocationHandler(Interface)
实现java.lang.reflect.InvocationHandler接口,实现其invoke方法

/**
	 * proxy:  所代理的那个真实对象
	 * method: 所要调用真实对象的某个方法的Method对象
	  * args:    调用真实对象某个方法时接受的参数
	 */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    }

Proxy(Class)

//Proxy的作用是用来动态创建一个代理对象的类
	/**
	 * @param loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
	 * @param interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
	 * @param h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
	 */
	public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  
			throws IllegalArgumentException{
			//查看其源码 创建一个类(会实现所有传入实例的类的所有接口)return cons.newInstance(new Object[]{h});
	}

类的实例化通过Proxy.newProxyInstance()返回

示例

1.定义一个接口

public interface Subject{    
		public void rent();    
		public void hello(String str);
	}

2.实现接口

	public class RealSubject implements Subject{    
		@Override
	    public void rent()
	    {
	        System.out.println("I want to rent my house");
	    }    
		@Override
	    public void hello(String str)
	    {
	        System.out.println("hello: " + str);
	    }
	}

定义一个动态代理类了,每一个动态代理类都必须要实现 InvocationHandler 这个接口

	public class DynamicProxy implements InvocationHandler{    
		// 这个就是我们要代理的真实对象
	    private Object subject;    
	    // 构造方法,给我们要代理的真实对象赋初值
	    public DynamicProxy(Object subject)
	    {        
	    	this.subject = subject;
	    }    
	    @Override
	    public Object invoke(Object object, Method method, Object[] args)
	            throws Throwable    {        
	    	//  在代理真实对象前我们可以添加一些自己的操作
	        System.out.println("before rent house");
	        System.out.println("Method:" + method);        
	        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
	        method.invoke(subject, args);        
	        // 在代理真实对象后我们也可以添加一些自己的操作
	        System.out.println("after rent house");        
	        return null;
	    }
	}
	public class Client{    
		public static void main(String[] args) {        
				//我们要代理的真实对象
	        Subject realSubject = new RealSubject();        
	        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
	        InvocationHandler handler = new DynamicProxy(realSubject);        
	        /*
	         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
	         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
	         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
	         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
	         */
	        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
	                .getClass().getInterfaces(), handler);
	        System.out.println(subject.getClass().getName());
	        subject.rent();
	        subject.hello("world");
	    }
	}
控制台的输出:

$Proxy0

before rent house
Method:public abstract void com.**.dynamicproxy.Subject.rent()
I want to rent my house
after rent house

before rent house
Method:public abstract void com.**.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house

通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

缺点:
1.必须基于接口来实现,即代理类一定要有接口,且只能调用接口对应的方法,实例的其他方法无法访问。
2.进入实现方法后可随便调用,方法之间的调用不会再进行AOP;只在外部调用这些方法时,才会调用代理的invoke()方法。

实现机制

  1. 创建原始类的子类,也就是动态创建的这个类继承原来的类(SpringAOP实现方式)
  2. 直接修改原来的class字节码(类的跟踪)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值