zoukankan      html  css  js  c++  java
  • 第二十七部分_AOP详解

    AOP技术本质:

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

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

      使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,它们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

      AOP的核心思想就是"将应用程序中的商业逻辑同其提供支持的通用服务进行分离"。

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

    • join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
    • point cut(切入点):本质上是一个捕获连接点的结构,在AOP中,可以定义一个point cut,来捕获相关方法的调用。
    • advice(通知):是point cut的执行代码,是执行"方面"的具体逻辑。
    • aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
    • introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。

    上述的技术特性组成了基本的AOP技术,大多数AOP工具均实现了这些技术。它们也可以是研究AOP技术的基本术语。

    上图:

      "横切"是AOP的专有名词。它是一种蕴含强大力量的相对简单的设计和编程技术,尤其是用于建立松散耦合的、可扩展的企业系统时。横切技术可以使得AOP在一个给定的编程模型中穿越既定的职责部分(比如日志记录和性能优化)的操作。

      如果不使用横切技术,软件开发是怎样的情形呢?在传统的程序中,由于横切行为的实现是分散的,开发人员很难对这些行为进行逻辑上的实现或更改。例如,用于日志记录的代码和主要用于其他职责的代码缠绕在一起。根据所解决的问题的复杂程度和作用域的不同,所引起的混乱可大可小。更改一个应用程序的日志记录策略可能涉及数百次编辑——即使可行,这也是个令人头疼的任务。

      在AOP中,我们将这些具有公共逻辑的,与其他模块的核心逻辑纠缠在一起的行为称为"横切关注点(Crosscutting Concern)",因为它跨越了给定编程模型中的典型职责界限。

    下面我们通过一个小的Demo,演示一下AOP在Spring中的具体应用:

    首先新建一个Java Project:aopproxy,增加对Spring的支持。选择Spring2.5,库选择:Spring 2.5 AOP Libraries、Spring 2.5 Core Libraries。点击Finish。

    新建包lee,在下面新建六个类:

    package lee;
    
    public interface Person
    {
    	void info();
    	void run();
    }
    ---------------------------------------------------------------------------------------------
    package lee;
    
    public class PersonImpl implements Person
    {
    	private String name;
    	private int age;
    	public void setName(String name)
    	{
    		this.name = name;
    	}
    	public void setAge(int age)
    	{
    		this.age = age;
    	}
    	
    	@Override
    	public void info()
    	{
    		System.out.println("我的名字是: " + name + ", 今年年龄为: " + age);
    	}
    	
    	@Override
    	public void run()
    	{
    		if(age < 45)
    			System.out.println("我还年轻,奔跑迅速...");
    		else
    			System.out.println("我年老体弱,只能慢跑...");
    	}
    }
    ----------------------------------------------------------------------------------------------------------
    package lee;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    public class MyBeforeAdvisor implements MethodBeforeAdvice
    {
    	@Override
    	public void before(Method method, Object[] args, Object target)
    			throws Throwable
    	{
    		System.out.println("方法调用之前...");
    		System.out.println("下面是方法调用的信息:");
    		System.out.println("所执行的方法是:" + method);
    		System.out.println("调用方法的参数是: " + args);
    		System.out.println("目标对象是: " + target);
    	}
    }
    -------------------------------------------------------------------------------------------------------
    package lee;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    public class MyAfterAdvisor implements AfterReturningAdvice
    {
    	@Override
    	public void afterReturning(Object returnValue, Method method, Object[] args,
    			Object target) throws Throwable
    	{
    		System.out.println("方法调用结束...");
    		System.out.println("目标方法的返回值是:");
    		System.out.println("目标方法是:" + method);
    		System.out.println("目标方法的参数是: " + args);
    		System.out.println("目标对象是: " + target);
    	}
    }
    -----------------------------------------------------------------------------------------------------
    package lee;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAroundInterceptor implements MethodInterceptor
    {
    
    	@Override
    	public Object invoke(MethodInvocation invocation) throws Throwable
    	{
    		System.out.println("调用方法之前:invocation对象:[" + invocation + "]");
    		Object rval = invocation.proceed();
    		System.out.println("调用结束...");
    		return rval;
    	}
    
    }
    ----------------------------------------------------------------------------------------------------
    package lee;
    
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class BeanTest
    {
    	public static void main(String[] args) throws Exception
    	{
    		XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    		
    		Person p = (Person) factory.getBean("person");
    		
    		// p.info();
    		
    		p.run();
    	}
    }
    

    下面是src下的配置文件applicationContext.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"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
    	<bean id="personTarget" class="lee.PersonImpl">
    		<property name="name">
    			<value>Wawa</value>
    		</property>
    		<property name="age">
    			<value>51</value>
    		</property>
    	</bean>
    
    	<bean id="myAdvisor" class="lee.MyBeforeAdvisor" />
    	<bean id="myAroundInterceptor" class="lee.MyAroundInterceptor" />
    	
    	<bean id="runAdvisor"
    		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    		<!-- advice属性确定处理bean -->
    		<property name="advice">
    			<!-- 此处的处理bean定义采用嵌套bean, 也可引用容器的另一个bean -->
    			<bean class="lee.MyAfterAdvisor" />
    		</property>
    		<!-- patterns确定正则表达式模式 -->
    		<property name="patterns">
    			<list>
    				<!-- 确定正则表达式列表 -->
    				<value>.*run.*</value>
    			</list>
    		</property>
    	</bean>
    	
    	
    	<bean id="person"
    		class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="proxyInterfaces">
    			<value>lee.Person</value>
    		</property>
    		<property name="target">
    			<ref local="personTarget"/>
    		</property>
    		<property name="interceptorNames">
    			<list>
    				<value>runAdvisor</value>
    				<value>myAdvisor</value>
    				<value>myAroundInterceptor</value>
    			</list>
    		</property>
    	</bean>
    	
    	
    	
    </beans>
    

    运行BeanTest类的run方法的结果:

    log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader).
    log4j:WARN Please initialize the log4j system properly.
    方法调用之前...
    下面是方法调用的信息:
    所执行的方法是:public abstract void lee.Person.run()
    调用方法的参数是: [Ljava.lang.Object;@48ff2413
    目标对象是: lee.PersonImpl@7b36a43c
    调用方法之前:invocation对象:[ReflectiveMethodInvocation: public abstract void lee.Person.run(); target is of class [lee.PersonImpl]]
    我年老体弱,只能慢跑...
    调用结束...
    方法调用结束...
    目标方法的返回值是:
    目标方法是:public abstract void lee.Person.run()
    目标方法的参数是: [Ljava.lang.Object;@669980d5
    目标对象是: lee.PersonImpl@7b36a43c
    

    运行info方法的结果:

    log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader).
    log4j:WARN Please initialize the log4j system properly.
    方法调用之前...
    下面是方法调用的信息:
    所执行的方法是:public abstract void lee.Person.info()
    调用方法的参数是: [Ljava.lang.Object;@bd10a5c
    目标对象是: lee.PersonImpl@736921fd
    调用方法之前:invocation对象:[ReflectiveMethodInvocation: public abstract void lee.Person.info(); target is of class [lee.PersonImpl]]
    我的名字是: Wawa, 今年年龄为: 51
    调用结束...
    

    接下来,再看一个实例:使用Spring进行权限验证。

    首先新建一个Java Project:authority,增加对Spring的支持。选择Spring2.5,库选择:Spring 2.5 AOP Libraries、Spring 2.5 Core Libraries。点击Finish。

    新建包lee,在下面新建六个类:

    package lee;
    
    public interface TestService
    {
    	void view();
    	void modify();
    }
    ----------------------------------------------------------------------------------------------------------
    package lee;
    
    public class TestServiceImpl implements TestService
    {
    	@Override
    	public void view()
    	{
    		System.out.println("用户查看数据");
    	}
    	
    	@Override
    	public void modify()
    	{
    		System.out.println("用户修改数据");
    	}
    	
    }
    ---------------------------------------------------------------------------------------------------------
    package lee;
    
    public interface TestAction
    {
    	public void modify2();
    	public void view2();
    }
    ------------------------------------------------------------------------------------------------------
    package lee;
    
    public class TestActionImpl implements TestAction
    {
    	private TestService ts;
    	
    	public void setTs(TestService ts)
    	{
    		this.ts = ts;
    	}
    	
    	@Override
    	public void modify2()
    	{
    		ts.modify();
    	}
    
    	@Override
    	public void view2()
    	{
    		ts.view();
    	}
    
    }
    ------------------------------------------------------------------------------------------------------
    package lee;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class AuthorityInterceptor implements MethodInterceptor
    {
    	private String user;
    	
    	public void setUser(String user)
    	{
    		this.user = user;
    	}
    	@Override
    	public Object invoke(MethodInvocation invocation) throws Throwable
    	{
    		System.out.println("=================");
    		String methodName = invocation.getMethod().getName();
    		
    		if(!user.equals("admin") && !user.equals("registerUser"))
    		{
    			System.out.println("您无权执行该方法");
    			return null; // 直接返回,不再执行后续方法。
    		}
    		else if(user.equals("registerUser") && methodName.equals("modify"))
    		{
    			System.out.println(methodName);
    			System.out.println("您不是管理员,无法修改数据");
    			return null;
    		}
    		else
    		{
    			return invocation.proceed(); // 代理过后,真实对象的方法得到执行。
    		}
    	}
    }
    ----------------------------------------------------------------------------------------------------------
    package lee;
    
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class BeanTest
    {
    	public static void main(String[] args) throws Exception
    	{
    		XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    		
    		TestAction ta = (TestAction) factory.getBean("testAction");
    		
    		ta.view2(); // 不同于TestService接口的方法名,以示区别。
    		ta.modify2();
    	}
    }
    

    下面是ApplicationContext.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"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
    	<bean id="serviceTarget" class="lee.TestServiceImpl" />
    	<bean id="authorityInterceptor" class="lee.AuthorityInterceptor">
    		<property name="user">
    			<value>registerUser</value> <!-- 测试:admin,aa,registerUser -->
    		</property>
    	</bean>
    	
    	<bean id="service"
    		class="org.springframework.aop.framework.ProxyFactoryBean">
    		<!-- 也可以使用不借助接口的实现方式,直接作用于类,后面会给出相应的xml文件 -->
    		<property name="proxyInterfaces">
    			<value>lee.TestService</value>
    		</property>
    		<property name="target">
    			<ref local="serviceTarget"/>
    		</property>
    		<property name="interceptorNames">
    			<list>
    				<value>authorityInterceptor</value>
    			</list>
    		</property>
    	</bean>
    
    	<bean id="testAction" class="lee.TestActionImpl">
    		<property name="ts">
    			<ref local="service"/>
    		</property>
    	</bean>
    </beans>
    

    通过修改ApplicationContext.xml中的bean-authorityInterceptor的user属性,可以看到权限验证起作用了。

    此外,我们也可以直接代理实现类类,而不是接口,它本身不是Java提供的支持,而是Spring的AOP三方库提供的支持,通过直接修改Class文件的方式来达到目的。xml中相关部分如下:

    <bean id="service"
    		class="org.springframework.aop.framework.ProxyFactoryBean">
    		<!-- 也可以使用不借助接口的实现方式,直接作用于类,后面会给出相应的xml文件 -->
    		<property name="proxyTargetClass">
    			<value>true</value>
    		</property>
    		<property name="target">
    			<ref local="serviceTarget"/>
    		</property>
    		<property name="interceptorNames">
    			<list>
    				<value>authorityInterceptor</value>
    			</list>
    		</property>
    	</bean>
    

    删除TestAction接口,对TestActionImpl修改如下:

    package lee;
    
    public class TestActionImpl implements TestAction
    {
    	private TestServiceImpl ts;
    	
    	public void setTs(TestServiceImpl ts)
    	{
    		this.ts = ts;
    	}
    	
    	@Override
    	public void modify2()
    	{
    		ts.modify();
    	}
    
    	@Override
    	public void view2()
    	{
    		ts.view();
    	}
    
    }
    

    运行main方法,可得到相同的结果。

  • 相关阅读:
    [hdu2196]Computer树的直径
    [poj2342]Anniversary party树形dp入门
    链式前向星模板
    LintCode-50.数组剔除元素后的乘积
    Markdown的基本语法
    LintCode-8.旋转字符串
    LintCode-1.A + B 问题
    LintCode-61.搜索区间
    LintCode-88.最近公共祖先
    LintCode-54.转换字符串到整数
  • 原文地址:https://www.cnblogs.com/Code-Rush/p/4975927.html
Copyright © 2011-2022 走看看