Spring进阶(三)—AOP


背景

   当我们的项目中需要给某些方法添加日志记录时,这些需要记录的方法散布在30多个类中.解决这个问题最直接的

方法是:创建一个超类/接口,然后让所有的日志功能的类继承它。如果开发期间需求变动,那么就要修改就会散布

30多个类中,这样大量的修改,无疑会增加出错的几率,并且加大系统维护的难度。AOP的出现解决了这个问题。

      问题:写多个重复的权限验证


 

解决方案:利用AOP横切,将权限验证抽离出来


 

 

介绍   

   AOP为Aspect OrientedProgramming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功

能的统一维护的一种技术。AOP 的核心构造是方面,它将那些影响多个类的行为(日志或事务)封装到可重用的模块中。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提

高了开发的效率。

 

OOPAOP

       OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。OOP允许你定

义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,

而与它所散布到的对象的核心功能毫无关系。这种散布在各处的无关的代码被称为横切代码,在OOP设计中,它导致

了大量代码的重复,而不利于各个模块的重用。

    AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行

为封装到一个可重用模块。

 

实现原理

        AOP的实现原理即为动态代理,AOP框架自动创建的AOP代理

1.静态织入(AspectJ)

       在代码的编译阶段植入Pointcut的内容 ,即静态代理的实现方式。优点是性能好

2.动态代理(Spring)

       代码执行阶段,在内存中截获对象,动态的插入Pointcut的内容,优点是不需要额外的编译,但是性能比静态织入要

低。 AspectJ相同的是,Spring AOP同样需要对目标类进行增强,也就是生成新的 AOP代理类;与 AspectJ不同的

是,Spring AOP无需使用任何特殊命令对 Java源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”

的方式来生成 AOP代理。

      Spring AOP代理由 Spring IoC容器负责生成、管理,其依赖关系也由 IoC容器负责管理。因此,AOP代理可

以直接使用容器中的其他 Bean实例作为目标,这种关系可由 IoC容器的依赖注入提供

 

具体实现

1、日常使用

<pre name="code" class="csharp"><span style="font-size:18px;">client:
	public class Client {
	
		public static void main(String[] args) {
			//使用IOC容器来管理对象,加载配置文件applicationContextDaily.xml
			BeanFactory factory = new ClassPathXmlApplicationContext("applicationContextDaily.xml");
			
			//获得具体的实例
			UserManager userManager = (UserManager)factory.getBean("userManager");
			
			//调用相应的方法
			userManager.addUser("张三", "123");
		}
	
	}</span>
<span style="font-size:18px;">
用户管理接口:
      public interface UserManager {
	    //添加用户
		public void addUser(String username, String password);
		
		//删除用户
		public void delUser(int userId);
		
		//查询用户
		public String findUserById(int userId);
		
		//修改用户
		public void modifyUser(int userId, String username, String password);
	}</span>
<span style="font-size:18px;">

用户管理的具体实现:
	public class UserManagerImpl implements UserManager {
	
		public void addUser(String username, String password) {
			//每个方法必须写 checkSecurity();
			checkSecurity();
			System.out.println("----Add方法:"+username);
		}
	
		public void delUser(int userId) {
			//每个方法必须写 checkSecurity();
			checkSecurity();
			System.out.println("---del方法:"+userId);
		}
	
		public String findUserById(int userId) {
			//每个方法必须写  checkSecurity();
			checkSecurity();
			System.out.println("---Find方法:"+userId);
			return "张三";
		}
	
		public void modifyUser(int userId, String username, String password) {
			//每个方法必须写  checkSecurity();
			checkSecurity();
			System.out.println("---Modify方法:"+userId);
		}
	
	    private void checkSecurity() {
			System.out.println("-------checkSecurity-------");
		}
	}</span>
<span style="font-size:18px;">
配置文件:applicationContextDaily.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-2.0.<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">xsd</span>
	           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
	           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">              
		
		<bean id="userManager" class="com.bjpowernode.daily.UserManagerImpl"/>	
	</beans></span>

 
   

结果:

存在的问题:

       如果每个方法都需要使用checkSecurity()方法时,带来的问题:一重复的代码增多;二如果需求改变,所涉及的

代码需要全部修改。

 

2、静态代理:AspectJ(采用注解形式)

      静态代理解决了上面高频率改代码的问题。

<span style="font-size:18px;">依赖包配置:
	* SPRING_HOME/dist/spring.jar
	* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
	* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
	* SPRING_HOME/lib/aspectj/*.jar</span>
<span style="font-size:18px;">
	
引入代理类SecurityHandler,让它去执行相同的模块checkSecurity()方法
@Aspect
public class SecurityHandler {
	/**
	 * 定义Pointcut,Pointcut的名称为addAddMethod(),此方法没有返回值和参数
	 * 该方法就是一个标识,不进行调用
	 */
	@Pointcut("execution(* add*(..))")
	private void addAddMethod(){};
	
	
	/**
	 * 定义Advice,表示我们的Advice应用到哪些Pointcut订阅的Joinpoint上
	 */
	@Before("addAddMethod()")
	//@After("addAddMethod()")
	private void checkSecurity() {
		System.out.println("-------checkSecurity-------");
	}		
}</span>
<span style="font-size:18px;">
配置文件:
<?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-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
           
    <!-- 启用AspectJ对Annotation的支持 -->       
	<aop:aspectj-autoproxy/>           
	
	<bean id="userManager" class="com.bjpowernode.spring.UserManagerImpl"/>	
	<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler"/>
</beans>

</span>
<span style="font-size:18px;">
client:
	public class Client {
	
		public static void main(String[] args) {
			//使用IOC容器来管理对象,加载配置文件applicationContextDaily.xml
			BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
			
			//获得具体的实例
			UserManager userManager = (UserManager)factory.getBean("userManager");
			
			//调用相应的方法
			userManager.addUser("张三", "123");
		}
	
	}</span>
<span style="font-size:18px;">
用户管理接口:
      public interface UserManager {
	    //添加用户
		public void addUser(String username, String password);
		
		//删除用户
		public void delUser(int userId);
		
		//查询用户
		public String findUserById(int userId);
		
		//修改用户
		public void modifyUser(int userId, String username, String password);
}</span>
<span style="font-size:18px;">
用户管理具体实现:
public class UserManagerImpl implements UserManager {

	public void addUser(String username, String password) {
		System.out.println("----Add方法:"+username);
	}

	public void delUser(int userId) {
		System.out.println("---del方法:"+userId);
	}

	public String findUserById(int userId) {
		System.out.println("---Find方法:"+userId);
		return "张三";
	}

	public void modifyUser(int userId, String username, String password) {
		System.out.println("---Modify方法:"+userId);
	}
}</span>

结果:


存在问题:

      代码重复问题依然存在,SecurityHandler中每个方法需要重复设置

3、动态代理:Sping AOP

       解决了静态代理不能解决的代码重复问题,系统运行时动态获得需要的对象

依赖包:
	* SPRING_HOME/dist/spring.jar
	* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
	* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
	* SPRING_HOME/lib/aspectj/*.jar
配置文件:
<?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-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	
	<bean id="userManager" class="com.bjpowernode.spring.UserManagerImpl"/>
	<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler"/>
	
	<aop:config>
		<!--获得公共模块的引用-->
		<aop:aspect id="securityAspect" ref="securityHandler">	
			<!-- 公共模块应用在哪些方法上 -->
<aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.add*(..)) || execution(* com.bjpowernode.spring.*.del*(..))"/> 		
			<!-- 指定公共模块出现方法的位置 -->
			<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
		</aop:aspect>
	</aop:config>
</beans>

//公共模块
public class SecurityHandler {	
	private void checkSecurity() {
		System.out.println("-------checkSecurity-------");
	}		
}


用户管理接口:
      public interface UserManager {
	    //添加用户
		public void addUser(String username, String password);
		
		//删除用户
		public void delUser(int userId);
		
		//查询用户
		public String findUserById(int userId);
		
		//修改用户
		public void modifyUser(int userId, String username, String password);
}
</pre><pre code_snippet_id="633594" snippet_file_name="blog_20150401_15_1971169" name="code" class="csharp">用户管理具体实现:
public class UserManagerImpl implements UserManager {

	public void addUser(String username, String password) {
		System.out.println("----Add方法:"+username);
	}

	public void delUser(int userId) {
		System.out.println("---del方法:"+userId);
	}

	public String findUserById(int userId) {
		System.out.println("---Find方法:"+userId);
		return "张三";
	}

	public void modifyUser(int userId, String username, String password) {
		System.out.println("---Modify方法:"+userId);
	}
}

结果:



注:

方面/切面:Aspect

   公共模块,比如事务、日志,相当于我们上面的公共方法checkSecurity()

连接点:JoinPoint

   表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处

理异常等等,Spring只支持方法执行连接点,在AOP中表示为“在哪里做”;相当于我们程序运行时,使用配置

文件来初始化目标对象和代理对象。

切入点:PointCut

它定义了Advice应用到哪些JoinPoint上,即我们上例中的增、删、改、查方法

增强:Advice

     Advice 定义了在 pointcut里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别

是在每个joinPoint 之前、之后还是代替执行的代码。

目标对象(TargetObject):

   需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被增强的对象。由于Spring AOP 通过代

理模式实现,从而这个对象永远是被代理对象,在AOP中表示为“对谁做”;

AOP代理(AOPProxy):

    AOP框架使用代理模式创建的对象,从而实现在连接点处插入增强,就是通过代理来对目标对象应用切面。

织入(Weaving):

   织入是一个过程,是将公共模块应用到目标对象从而创建出AOP代理对象的过程。

 

AOP实现部分:

定义普通业务组件。

定义切入点,一个切入点可能横切多个业务组件。

定义增强处理,增强处理就是在 AOP框架为普通业务组件织入的处理动作。

总结

    Jboss 4.0 完全采用 AOP 的思想来设计的 EJB 容器,它已经通过了J2EE 的认证,并且在工业化应用中证明是

一个优秀的产品。相信在不远的将来,会出现更多采用 AOP 思想设计的产品和行业应用。面向切面编程为我们带来

了新的想法、新的思想,需要我们慢慢的吸收。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值