【Spring】AOP瞭解加實現

【需求】

     有UserManager接口以及相應的實現類UserManagerImpl,詳見下。

UserManager接口:

package com.bjpowernode.spring;

public interface UserManager {
	public void addUser(String username, String password);
}

UserManagerImpl類:

package com.bjpowernode.spring;

public class UserManagerImpl implements UserManager {

	@Override
	public void addUser(String username, String password) {
		System.out
				.println("------------------------UserManagerImpl.addUser()-----------------------");

	}

}

      需要在添加方法之前進行安全校驗,或者在添加方法之後進行安全校驗。有三種比較不錯的實現方式。

【方式一:動態代理】

      要了解SpringAOP就必須先了解動態代理,因爲AOP就是基於動態代理實現的。有一個代理類Proxy,在這個類中有一個方法newProxyInstance,這個方法就是創建代理對象的方法,該方法源碼如下:

public static Object newProxyInstance(ClassLoader loader,
					  Class<?>[] interfaces,
					  InvocationHandler h)
	throws IllegalArgumentException
    {
	if (h == null) {
	    throw new NullPointerException();
	}

	/*
	 * Look up or generate the designated proxy class.
	 */
	Class cl = getProxyClass(loader, interfaces);

	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }

      這個方法需要3個參數:①ClassLoader,用於加載代理類的Loader類,通常這個Loader類被代理的類是同一個Loader類;②Interfaces,是要被代理的那些接口;③InvocationHandler,用於執行除了被代理接口中方法之外的用戶自定義的操作,也是用戶需要代理的最終目的。用戶調用目標方法都被代理到在InvocationHandler類中定義的唯一方法invoke()中。下面來看具體的實現。

創建一個代理類,實現代碼如下:

package com.bjpowernode.spring;

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

public class SecurityHandler implements InvocationHandler {

	private Object targetObject;

	public Object createProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();
		// 調用目標方法
		Object ret = method.invoke(targetObject, args);
		return ret;
	}

	private void checkSecurity() {
		System.out.println("-------checkSecurity-------");
	}

}

客戶端代碼如下:

package com.bjpowernode.spring;

public class Client {

	public static void main(String[] args) {
		SecurityHandler hander = new SecurityHandler();
		UserManager useraManager = (UserManager) hander
				.createProxyInstance(new UserManagerImpl());
		useraManager.addUser("楚喬", "123");
	}

}

       小結:動態代理的實現,也算是應用了AOP的思想,將本不應該耦合在一起的類或功能分離開,重新在代理類中進行實現。其實,spring的aop就是用的動態代理 或者cglib來實現的。下面來看一下AOP的兩種實現方式吧。

AOP

      SpringAOP進行擴展,增加了如PointcutAdvisor等一些接口使得其更加靈活。先來了解一下一些概念。

Cross Cutting Concern

是一種獨立服務,它會遍佈在系統的處理流程之中

Aspect        

對橫切性關注點的模塊化

Advice

對橫切性關注點的具體實現

Pointcut

它定義了Advice應用到哪些JoinPoint上,對Spring來說是方法調用、 

JoinPoint

Advice在應用程序上執行的點或時機,Spring只支持方法的JoinPoint,這個點也可以使屬性修改,如:Aspecj可以支持 

Weave

將Advice應用到TargetObject上的過程叫織入,Spring支持的是動態織入

Target Object

Advice被應用的對象

Proxy

SpringAOP默認使用JDK的動態代理,它的代理是運行時創建,也可以使用CGLIB代理

Introduction

可以動態的爲類添加方法        

下面來看看AOP的兩種實現方式吧

【方式二:使用@Aspect註解定義的切面】

1、spring的依賴包配置

spring.jar

log4j-1.2.14.jar

commons-logging.jar

aspectjrt.jar、aspectjweaver.jar

 

2、將橫切性關注點模塊化,建立SecurityHandler.java

package com.bjpowernode.spring;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SecurityHandler {
	// 定義Pointcut,Pointcut的名稱爲addAddMethod(),此方法沒有返回值和參數,該方法就是一個標識,不進行調用。
	@Pointcut("execution(* add*(..))")
	private void addAddMethod() {
	};

	// 定義Advice,表示我們的Advice應用到哪些Pointcut訂閱的JoinPoint上,
	// 註解@Before表示在調用方式之前執行checkSecurity方法;若要求之後執行,則改爲@After
	@Before("addAddMethod()")
	private void checkSecurity() {
		System.out.println("----------checkSecurity-----------");
	}
}

3、採用註解指定SecurityHandler爲Aspect,採用註解定義Advice和Pointcut,啓用AspectJ對Annotation的支持,並且將目標類和Aspect類配置到IoC容器中

<?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" />

	<!-- 配置切面aspect -->
	<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler" />
</beans>

4、開發客戶端

package com.bjpowernode.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

	public static void main(String[] args) {
		BeanFactory factory = new ClassPathXmlApplicationContext(
				"applicationContext.xml");

		UserManager userManager = (UserManager) factory.getBean("userManager");

		userManager.addUser("楚喬", "123");
	}

}

【方式三:AOP XML配置】

1、spring的依賴包配置

spring.jar

log4j-1.2.14.jar

commons-logging.jar

2、建立SecurityHandler.java

package com.bjpowernode.spring;

public class SecurityHandler {

	private void checkSecurity() {
		System.out.println("----------checkSecurity-----------");
	}
}

3.具體的配置:

<?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" />
	<!-- 配置切面aspect -->
	<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler" />

	<aop:config>
		<aop:aspect id="securityAspect" ref="securityHandler">
			<aop:pointcut id="addAddMethod" expression="execution(* add*(..))" />
			<aop:before method="checkSecurity" pointcut-ref="addAddMethod" />
		</aop:aspect>
	</aop:config>
</beans>

小結:對比一下兩種AOP的實現方式,相對來說xml配置更簡單一些,AOP框架本身就十分易於用XML配置,不再那麼繁瑣。

【補充】

下面給出一些常見切入點表達式的例子:

①任意公共方法的執行

Execution(public* *(. .))

②任意一個以set開始的方法的執行

Execution(*set*(. .))

③定義在service包裏的任意方法的執行

Execution(*com.xyz.service.*.*(. .))

④定義在service包或者子包裏的任意方法的執行

Execution(*com.xyz.service. . *.*(. .))

AccountService接口的任意方法的執行

Execution(*com.xyz.service.AccountService.*(. . ))

⑥在service包裏的任意連接點(在SpringAOP中只是方法執行)

Within(com.xyz.service.*)

⑦在service包或者子包裏的任意連接點(在spring AOP中只是方法執行)

Within(com.xyz.service.. *)

⑧實現了AccountService接口的代理對象的任意連接點(在Spring AOP中只是方法執行)

This(com.xyz.service.AccountService)

⑨同時也只是或者關係用“||”表示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章