Spring的AOP的理解

一、什么是AOP?

Spring AOP是OOP思想的补充和完善,OOP引进的是抽象、封装、继承和多态等的概念,它强调的是一个完整的事务自上而下的概念,对于事务的具体内部实现是不清楚的。OOP允许开发者定义的是纵向的关系,而AOP是定义的是横向的关系。这里就像日志功能,它的功能是横向的分布在所有对象层次中,与对象的核心代码功能是无关的,这样造成了大量代码的重用,不利于模块之间的重用。而AOP是利用横切的技术,能够剖解开封装对象内部,并将影响多个类的共同代码且不影响主要的核心代码,剥离出出来封装成一个独立的模块(切面),减少系统重复代码的调用的,降低模块之间的耦合度,有利于系统的可操作性和可维护性。

AOP将系统软件分为核心业务周边业务,核心业务具指的是实现某种特定的功能,而周边业务指的是与核心业务无关的、系统的重复调用的代码,来配合核心业务的周边业务,比如日志、权限和事务等。一句就是,AOP作用于将核心关注点和横切关注点分离出来。

二、AOP的基本技术术语和概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

三、基于Spring实现AOP

Spring 中的AOP代理是由Spring的IOC容器负责生成和管理的,其依赖关系也是由IOC来管理的,所以AOP代理可以直接使用容器中的bean实列作为目标,这种关系可以由IOC容器的依赖注入提供。

AOP的实现策略:

(1)JAVA SE动态代理:

          使用动态代理可以为一个或者多个接口在运行期动态生成实现对象,生成的对象接口的实现方法可以添加增强代码,从而实现AOP。但是只正对接口代理,实现动态代理是通过反射,从而增加了反射的开销。

(2)字节码生成     CGLib动态代理

          动态字节码生成技术是指在运行时动态生成指定类的一个子类对象,并覆盖其中特定的方法,覆盖方法时可以增强代码,c从而实现AOP。

(3)定制的类加载器

         当需要对类的所有对象都需要增加增强代码,动态代理和字节码生成的本质上都需要动态的构造动态代理对象,即最终呗增强的对象是有AOP宽假生成的。

(4)代码生成

         利用工具在已有的代码基础上生成新的代码,其中的可以增加任意横切代码来实现AOP。

(5)语言增强

         可以对构造和属相的赋值进行增强

下面举例说明一个简单的实现AOP过程的:(原文:  http://www.cnblogs.com/xrq730/p/4919025.html )

package com.maven.study.service;

public interface HelloWorld {
	public void printHelloWorld();
    public void doPrint();
}
package com.maven.study.service.impl;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import com.maven.study.service.HelloWorld;

@Service
@Component
public class HelloWorldImpl implements HelloWorld{

	@Override
	public void printHelloWorld() {
		System.out.println("say printHelloWorld");
	}

	@Override
	public void doPrint() {
		System.out.println("say doPrint");
	}

}
package com.maven.study.service;

public class TimeHandle {
	public void printTime()
    {
        System.out.println("CurrentTime = " + System.currentTimeMillis());
    }
}

aop.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:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
     
    <bean  id="helloWorldImpl" class="com.maven.study.service.impl.HelloWorldImpl" />
	<bean  id="timeHandler" class="com.maven.study.service.TimeHandle" />
	
	<aop:config>
		<aop:aspect id="time" ref="timeHandler">
			<aop:pointcut id="hello" expression="execution(* com.maven.study.service.HelloWorld.*(..))"/>
			<aop:before method="printTime" pointcut-ref="hello"/>
			<aop:after method="printTime" pointcut-ref="hello"/>
		</aop:aspect>
	</aop:config>
</beans>

测试:

package com.test;

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

import com.maven.study.service.HelloWorld;

public class TestAop {
	
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:aop.xml");
		HelloWorld hw = (HelloWorld) ctx.getBean("helloWorldImpl");
		hw.printHelloWorld();
		System.out.println("===============");
		hw.doPrint();
	}
}

结果:

CurrentTime = 1553828546555
say printHelloWorld
CurrentTime = 1553828546555
===============
CurrentTime = 1553828546556
say doPrint
CurrentTime = 1553828546556

       这就是增强代码,定义了<aop:pointcut id="hello" expression="execution(* com.maven.study.service.HelloWorld.*(..))"/>为切入点,这里运行代码需要增加项目所需的jar包,我是maven项目,所以依赖加入:

<dependency>
	<groupId>aopalliance</groupId>
	<artifactId>aopalliance</artifactId>
	<version>1.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.2</version>
</dependency>

四、AOP增强

4.1  编程式增强

接口:

package com.maven.study.service;

public interface SayHello {
	public void  sayHello();
}

实现:

package com.maven.study.service.impl;

import org.springframework.stereotype.Component;

import com.maven.study.service.SayHello;

public class SayHelloImpl implements SayHello {

	@Override
	public void sayHello() {
		System.out.println("你好,美女!");
	}
}

增强代码:

package com.maven.study.service.impl;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

public class SayHelloAdvice implements MethodBeforeAdvice,AfterReturningAdvice {

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("成功!");
	}

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("鼓足勇气的说:");
	}

}

测试:

package com.test;

import org.springframework.aop.framework.ProxyFactory;

import com.maven.study.service.SayHello;
import com.maven.study.service.impl.SayHelloAdvice;
import com.maven.study.service.impl.SayHelloAroundAdvice;
import com.maven.study.service.impl.SayHelloImpl;

public class TestSayHello {

	public static void main(String[] args) {
		ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
		proxyFactory.setTarget(new SayHelloImpl());//设置目标对象
		/**
		 * 前置增强和后置增强
		 */
		proxyFactory.addAdvice(new SayHelloAdvice());//添加增强对象
		
		SayHello sayHello = (SayHello) proxyFactory.getProxy();//从工厂中获取代理,调用代理的方法
		sayHello.sayHello();
	}
}

结果:

鼓足勇气的说:
你好,美女!
成功!

还有一种方式是环绕增强:

增强代码:

package com.maven.study.service.impl;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SayHelloAroundAdvice implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		before();
		Object obj = invocation.proceed();
		after();
		return obj;
	}

	private void before(){
		System.out.println("鼓足勇气:");
	}
	
	private void after(){
		System.out.println("成功!!!");
	}
		
}

测试的时候只要将代理兑现添加增强对象换成SayHelloAroundAdvice就可以了,总结上面的实现方法就是通过代码的形式来实现的。

4.2 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:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  
   
   <context:component-scan base-package="com.maven.study.service.*"></context:component-scan>
	
   <!-- 声明式增强 -->
   <bean id="sayHelloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   		<property name="interfaces" value="com.maven.study.service.SayHello"></property>
   		<property name="target" ref="sayHelloImpl"></property>
   		<property name="interceptorNames" value="sayHelloAroundAdvice"></property>
   </bean>

</beans>

在相应的SaHelloImol.java和SayHelloAroundAdvice.java添加相应的注解@Component

测试:

package com.test;

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

import com.maven.study.service.HelloWorld;
import com.maven.study.service.Love;
import com.maven.study.service.SayHello;
import com.maven.study.service.impl.HelloWorldImpl;
import com.maven.study.service.impl.SayHelloImpl;

public class TestBeanAroundAdvice {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
		SayHello sayHello = (SayHello) ctx.getBean("sayHelloProxy");
		sayHello.sayHello();
	}
}

结果:

鼓足勇气:
你好,美女!
成功!!!

注意:xml配值的interfaces和target一定是想对应的,否则会报错,而interceptorNames这个是增强的代码的,当调用业务的是时候会增强。进项强制转换的时候是转换的interfaces。上面的增强是对方法的增强。

4.3引入式增强  Introduction Advice

引入式的增强是对类的增强,如果看完不想让SayHelloImpl.java去实现SayHello.java的接口。这是门可以应如是增强,来实现此功能。

先创建一个增强接口:

package com.maven.study.service;

public interface Love {
	public void sayLove();
}

定义一个授权引入增强类,它的意思就是当调用的SayHelloImpl的时候,我们可以转换成增强类,从而调用增强类的方法,使SayHelloImpl不必去实现接口:

package com.maven.study.service.impl;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.stereotype.Component;

import com.maven.study.service.Love;
@Component
public class SayLoveImpl extends DelegatingIntroductionInterceptor implements Love {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1412651594048232141L;

	@Override
	public void sayLove() {
		System.out.println("l love you !!!");
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		return super.invoke(mi);
	}

}

xml配置:

<!-- 引入式增强  -->
<bean id="sayHelloProxy2" class="org.springframework.aop.framework.ProxyFactoryBean">
   		<property name="interfaces" value="com.maven.study.service.Love"></property>
   		<property name="target" ref="sayHelloImpl"></property>
   		<property name="interceptorNames" value="sayLoveImpl"></property>
   		<property name="proxyTargetClass" value="false"></property>
</bean>

测试:

package com.test;

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

import com.maven.study.service.HelloWorld;
import com.maven.study.service.Love;
import com.maven.study.service.SayHello;
import com.maven.study.service.impl.HelloWorldImpl;
import com.maven.study.service.impl.SayHelloImpl;

public class TestBeanAroundAdvice {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
		/*SayHelloImpl sayHelloImpl = (SayHelloImpl) ctx.getBean("sayHelloProxy2");
		Love love = (Love)sayHelloImpl;
		love.sayLove();*/
		
		Love love = (Love) ctx.getBean("sayHelloProxy2");
		love.sayLove();
		
	}
}

proxyTargetClass代表是是否地理的接口,默认是false,如果设置成true,说明代理是不是接口,而是target的目标类。

4.4 定义切面类

<!-- 自动代理 -->
   <!-- 定义切面 -->
   <bean id="sayHelloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
   		<property name="advice" ref="sayHelloAroundAdvice"></property>
   		<property name="pattern" value="com.maven.study.service.impl.SayHelloImpl.say.*"></property>
   </bean>   
   <bean id="sayHelloProxy3" class="org.springframework.aop.framework.ProxyFactoryBean">
   		<property name="interfaces" value="com.maven.study.service.Love"></property>
   		<property name="target" ref="sayHelloImpl"></property>
   		<property name="interceptorNames" value="sayHelloAdvisor"></property>
   		<property name="proxyTargetClass" value="true"></property>
   </bean>

sayHelloAdvisor这是定义的一个切面,作用于com.maven.study.service.impl.SayHelloImpl.say.*,表达的意思就是这个类下以say开头的方法(切点)。

4.5 AOP自动代理

<bean id="sayHelloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
   		<property name="advice" ref="sayHelloAroundAdvice"></property>
   		<property name="pattern" value="com.maven.study.service.impl.SayHelloImpl.say.*"></property>
</bean>  
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
   		<property name="optimize" value="true"></property>
</bean>

optimize 表示代理生成优化,如果是true,则表示目标类有接口则代理接口,没有则代理类,这样的就可以省略proxyTargetClass的设置。

4.6 根据bean名称来生成自动代理

<bean id="sayHelloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
   		<property name="advice" ref="sayHelloAroundAdvice"></property>
   		<property name="pattern" value="com.maven.study.service.impl.SayHelloImpl.say.*"></property>
</bean>   
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
   		<property name="beanNames" value="*Impl"></property>
   		<property name="optimize" value="true"></property>
   		<property name="interceptorNames" value="sayHelloAdvisor"></property>
</bean>

beanNames的意思属性代表只能是以Impl结尾,

测试:

package com.test;

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

import com.maven.study.service.SayHello;
import com.maven.study.service.impl.SayHelloImpl;

public class TestSpringAop {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
		SayHello sayHelloImpl = (SayHello) ctx.getBean("helloWorld");
		sayHelloImpl.sayHello();
		
	}
}

报错: No bean named 'helloWorld' is defined,这就是beanNames限制作用。

总结:AOP就是核心业务和周边业务的分离,最常见的日志通过AOP实现的。

===========================================================================================

最简单的、直接的方式的来实现了AOP功能,对于多种博客,相结合的,里面的代码敲一遍,肯定有了自己的理解和想法。

猜你喜欢

转载自blog.csdn.net/qq_33356585/article/details/88864608
今日推荐