Spring学习之AOP

一.什么是AOP?

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

看到过这段话:aop切面编程就是在常规的执行java类中方法前或执行后加入自定义的方法。比如你本来每天都去打酱油:去,打酱油,回。现在我每天在你打酱油路上等着,你去打酱油的时候我打你一顿,回来的时候给你点糖果吃。你根本不知道为什么我会在路上拦住打你。所以在切面中插入你自定义的方法,这个方法的执行和本身要执行的类方法无关系,也就是不是这个类的方法来调用你写的方法的,你写的方法什么时候执行都是要通过在配置指定。我打完你,你该打酱油还是去打酱油,当然我如果是拦住你让你酱油打少点,你打酱油的时候还是会打那么多,但是在你打完酱油回来的时候我可以把你的酱油倒些出去,所以嵌入的自定义方法对要调用的类方法本身没有影响,但是可以操纵这个方法的返结果或者处理结果。

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

二.AOP使用场景
AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

三.AOP相关概念
方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。


四.spring AOP的实现
常用的AOP实现方式有两种。第一种是基于xml配置文件方式的实现,第二种是基于注解方式的实现。

1.首先是基于xml配置实现。

该项目是基于maven的。

pom.xml 添加依赖包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>SpringAOP</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringAOP Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>4.1.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.5.0</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.7.3</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.7.3</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>SpringAOP</finalName>
	</build>
</project>

User.java实体类

public class User {
	private String username;
	private String pswd;
	User(){
		
	}
	
	public User(String username, String pswd) {
		super();
		this.username = username;
		this.pswd = pswd;
	}

	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPswd() {
		return pswd;
	}
	public void setPswd(String pswd) {
		this.pswd = pswd;
	}
	@Override
	public String toString() {
		return "User [username=" + username + ", pswd=" + pswd + "]";
	}
}


LoginService.java

public class LoginService {
	private User user;
	public  void login() {
	System.out.println(user.getUsername() + "你已经登录过了~~~");
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}

}


XmlLogger.java 日志切面类

public class XmlLogger {
	
	public void before() {
		System.out.println("xml before method..");
	}
	
	public void after() {
		System.out.println("xml method..");
	}

	public Object around(ProceedingJoinPoint joinPoint) {
		System.out.println("xml start around method..");
		Object[] args = joinPoint.getArgs();
		Object object = null;
		try {
			object = joinPoint.proceed(args);
		} catch (Throwable e) {
			e.printStackTrace();
		}
 		System.out.println("xml end around method..");
 		
		return object;
	}

}

spring-config.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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd ">
	<context:annotation-config />
    <context:component-scan base-package="com.example"/>  <!-- 自动扫描 -->
	<aop:aspectj-autoproxy />
	
	<bean id="user" class="com.example.model.User">
		<property name="username" value="mike"></property>
		<property name="pswd" value="123456"></property>
	</bean>
	
	<bean id="xmlLogger" class="com.example.aop.XmlLogger">
	</bean>
	
	<bean id="loginService" class="com.example.service.LoginService">
		<property name="user" ref="user"></property>
	</bean> 
	 
	<aop:config>
		<aop:pointcut expression="execution(* com.example.service.LoginService.login(..))" id="LoginServiceLog"></aop:pointcut>
		<aop:aspect id="xmlAspect" ref="xmlLogger">
			<aop:before method="before" pointcut-ref="LoginServiceLog"/>
			<aop:after method="after" pointcut-ref="LoginServiceLog"/>
			<aop:around method="around" pointcut-ref="LoginServiceLog"/>
		</aop:aspect>
	
	</aop:config>
	






</beans>



Test.java 测试类

public class Test {

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
		LoginService loginService = (LoginService) applicationContext.getBean("loginService");
		//User user = new User("huang", "147258");
		loginService.login();
	}

}

结果:




2.基于注解


AspectJLogger.java 日志切面类

@Aspect()
public class AspectJLogger {
	
	//public static final String EDP = "execution(* com.example.service.LoginService..*(..))";
	//注意*和包那有空格
	@Pointcut("execution(* com.example.service.LoginService..*(..))")
	public void pointCut() {
		
	}
	
	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		//获取目标对象对应的类名  
        String className = joinPoint.getTarget().getClass().getName();  
        //获取目标对象上正在执行的方法名  
        String methodName = joinPoint.getSignature().getName();  
		System.out.println("annotation before method.." + className + methodName);
	}
	
	@After("pointCut()")
	public void after() {
		System.out.println("annotation after method..");
	}


	@Around("pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		System.out.println("annotation start around method..");
		Object[] args = joinPoint.getArgs();
		Object object = null;
		//也可以执行
		this.before(joinPoint);
		this.after();
		try {
			object = joinPoint.proceed(args);
		} catch (Throwable e) {
			e.printStackTrace();
		}
 		System.out.println("end around method..");
 		
		return object;
	}


}


spring-config.xml 这里只需写出日志切面类的bean

<?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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd ">
	<aop:aspectj-autoproxy />
	<bean id="user" class="com.example.model.User">
		<property name="username" value="mike"></property>
		<property name="pswd" value="123456"></property>
	</bean>
	<bean id="aspect" class="com.example.aop.AspectJLogger">
	</bean>
	<bean id="loginService" class="com.example.service.LoginService">
		<property name="user" ref="user"></property>
	</bean>




</beans>


Test.java

public class Test {

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
		LoginService loginService = (LoginService) applicationContext.getBean("loginService");
		loginService.login();
	}

}

结果:


这只是一个简单的入门demo,以后会写一个更复杂的小程序。


参考博客: 点击打开链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值