Spring之AOP

Spring之AOP

【什么是aop?】

AOP,Aspect-Oriented Programming 面向切面编程
【AOP的由来】
OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需 要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。
AOP技术利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低 模块间的耦合度,并有利于未来的可操作性和可维护性。
AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为; 那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手 将这些剖开的切面复原,不留痕迹。

【知识铺垫】
Spring对AOP的支持,底层采用的是动态代理。所以我们要先来了解一下代理模式。

什么是代理模式?
Proxy Pattern,23种常用的面向对象软件的设计模式之一。
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的组成

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

为什么要用代理模式?

  1. 中介隔离
    在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

  2. 开闭原则,增加功能
    代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。

代理模式分类

1. 静态代理模式

优点:符合开闭原则,可以实现功能的增加,并起到中介隔离的作用
缺点:动态拓展性不好,一个代理类只能代理一个实际组件
实现

抽象角色 UserService.java

package com.neusoft.SpringAop.Service;

public interface UserService {
	void add();
	void delete();
	void query();
	void modify();
}

真实角色 UserImpl .java

package com.neusoft.SpringAop.Impl;

import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import com.neusoft.SpringAop.Service.UserService;

@Service
public class UserImpl implements UserService{

	@Override
	public void add() {
//		int a = 2/0;
		System.out.println("this is add method");
	}

	@Override
	public void delete() {
		System.out.println("this is delete method");
	}

	@Override
	public void query() {
		System.out.println("this is query method");
	}

	@Override
	public void modify() {
		System.out.println("this is modify method");
	}

}

代理角色 UserProxyStatic.java

package com.neusoft.SpringAop.proxy;

import com.neusoft.SpringAop.Impl.UserImpl;
import com.neusoft.SpringAop.Service.UserService;

public class UserProxyStatic implements UserService{
	private UserService us = new UserImpl();
	@Override
	public void add() {
		System.out.println(System.currentTimeMillis());
		us.add();
	}

	@Override
	public void delete() {
		System.out.println(System.currentTimeMillis());
		us.delete();
	}

	@Override
	public void query() {
		System.out.println(System.currentTimeMillis());
		us.query();
	}

	@Override
	public void modify() {
		System.out.println(System.currentTimeMillis());
		us.modify();
	}

}

测试类 Test1 .java

package com.neusoft.SpringAop.Test;

import com.neusoft.SpringAop.Service.UserService;
import com.neusoft.SpringAop.proxy.UserProxyStatic;

public class Test1 {
	public static void main(String[] args) {
		UserService us = new UserProxyStatic();
		us.add();
	}

}

2.动态代理模式
优点: 相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度
缺点: 无法摆脱仅支持interface代理的桎梏
实现:

抽象角色 UserService.java 如上
真实角色 UserImpl .java 如上
代理角色 UserProxyDynamic.java

package com.neusoft.SpringAop.proxy;

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

public class UserProxyDynamic implements InvocationHandler{
	
	private Object target;
	
	public UserProxyDynamic(Object object) {
		this.target = object;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//功能添加区
			System.out.println(System.currentTimeMillis());
		//功能添加区
		//反射,获取对应方法
		Object result = method.invoke(target, args);
		return result;
	}
	
	//获取代理对象
	public <T> T getProxy(){
		return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

}

测试类 Test.java

package com.neusoft.SpringAop.Test;

import java.util.HashMap;
import java.util.Map;

import com.neusoft.SpringAop.Impl.UserImpl;
import com.neusoft.SpringAop.Service.UserService;
import com.neusoft.SpringAop.proxy.UserProxyDynamic;
import com.neusoft.SpringAop.proxy.UserProxyStatic;

public class Test {
	public static void main(String[] args) {
		UserService us = new UserProxyDynamic(new UserImpl()).getProxy();
		Map<String, String> map = new UserProxyDynamic(new HashMap<String,String>()).getProxy();
		us.add();
		System.out.println(map.size());
	}

}

【Spring中AOP】
Spring中AOP的使用,分两种形式:

  • 声明式aop(依赖xml配置文件配置关系)
  • 注解式aop

在这里我们只介绍声明式AOP,因为在xml配置文件中会使相关的配置显的简单明了,若采用注解式,将会让人变的头大。

实现:
抽象角色 UserService.java 如上
真实角色 UserImpl .java 如上

配置文件 app.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
       
	<bean id = "userImpl" class = "com.neusoft.SpringAop.Impl.UserImpl"></bean>
	<bean id = "timer" class = "com.neusoft.SpringAop.util.Timer"></bean>
	
	<aop:config>
		<!-- 要切入的对象 -->
		<aop:aspect id = "test1" ref="timer">
			<!-- 切面 -->
			<aop:pointcut expression="execution(* com.neusoft.SpringAop.Service.*.*(..))" id="service"/>
			<aop:after-throwing method="showTime" pointcut-ref="service"/>
		</aop:aspect>
	</aop:config>
       
</beans>

其中,
<aop:aspect id = “test1” ref=“timer”> 声明要切入的对象

<aop:pointcut 中 expression 的值表示切面,即被代理对象
上述代码中expression 值表示,任意返回值 com.neusoft.SpringAop.Service中任意类 任意方法 任意参数

<aop:after-throwing method=“showTime” pointcut-ref=“service”/>代表切入对象切入的位置,这里又分5种情况

  1. 前置通知 <aop:before method=""/>
  2. 后置通知 <aop:after method=""/>
  3. 环绕通知(前+后) <aop:around method=""/>
  4. 最终通知 <aop:after-returning method=""/>
  5. 异常通知 <aop:after-throwing method=""/>

下面伪代码,可表示其位置

		try{
			前置通知
			目标方法
			后置通知
		}catch(){
			异常通知
		}finally{
			最终通知
		}

测试类:Test.java

package com.neusoft.SpringAop.Test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neusoft.SpringAop.Impl.UserImpl;
import com.neusoft.SpringAop.Service.UserService;
import com.neusoft.SpringAop.proxy.UserProxyStatic;

public class Test {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
//		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app3.xml");
		UserService us = (UserService) applicationContext.getBean("userImpl");
		us.add();
	}

}

补充:
个人认为,使用AOP时,在获取bean采用标注形式,aop配置采用xml声明配置,效果最佳。

【AOP应用场景】

  • 日志
  • spring提供事务管理器,利用aop将事务管理器切入到我们的代码逻辑上
  • springmvc中的拦截器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜鹊先生Richard

随缘~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值