【需求】
有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()-----------------------");
}
}
需要在添加方法之前進行安全校驗,或者在添加方法之後進行安全校驗。有三種比較不錯的實現方式。
【方式一:動態代理】
要了解Spring的AOP就必須先了解動態代理,因爲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】
Spring對AOP進行擴展,增加了如Pointcut、Advisor等一些接口使得其更加靈活。先來了解一下一些概念。
①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)
⑨同時也只是或者關係用“||”表示