Spring AOP 概念理解及@AspectJ支持
2012-02-24 10:56:35
| 分类:
Java
|
字号
订阅
为了更好的理解Spring简介一文http://quicker.iteye.com/blog/670056
中的概念,下面通过一些示例来加以说明。
首先要理解代理模式:有静态代理和动态代理
有关代理模式相关文章:
http://quicker.iteye.com/blog/571494
http://quicker.iteye.com/blog/571493
下面先给出静态代理的代码。
Java代码
-
public
interface
UserManager {
-
-
public
void
add(String name, String password);
-
public
void
del(String id);
-
public
void
modify(
int
id ,String name, String password);
-
}
Java代码
-
public
class
UserManagerImpl
implements
UserManager {
-
-
public
void
add(String name, String password) {
-
-
}
-
-
public
void
del(String id) {
-
-
}
-
-
public
void
modify(
int
id, String name, String password) {
-
-
}
-
-
}
Java代码
-
public
class
UserManagerProxy
implements
UserManager {
-
-
private
UserManager userManager ;
-
public
void
add(String name, String password) {
-
check();
-
userManager.add(name, password);
-
}
-
-
public
void
del(String id) {
-
check();
-
userManager.del(id);
-
}
-
-
public
void
modify(
int
id, String name, String password) {
-
check();
-
userManager.modify(id, name, password);
-
}
-
-
public
void
check(){
-
System.out.println("check security"
);
-
}
-
-
public
void
setUserManager(UserManager userManager){
-
this
.userManager = userManager;
-
}
-
-
public
static
void
main(String[] args) {
-
UserManagerProxy proxy = new
UserManagerProxy();
-
proxy.setUserManager(new
UserManagerImpl());
-
proxy.add("name"
,
"pwd"
);
-
}
-
}
上例中代理类控制在UserManagerImpl进行操作前对用户进行检查即check()方法。
那么下面用Spring Aop来实现。
配置步骤:
Java代码
-
通过示例,理解概念
-
一、创建普通JAVA项目,加入用户自定义的包:
-
包里有spring.jar,log4j-1.2
.15
.jar,commons-logging.jar
-
二、拷贝log4j.properties和applicationContext.xml到src目录
-
三、创建代码其中UserManager,UserManagerImpl类是用户管理接口及实现类
-
MySecurityManager,MySecurityManagerImpl类是包含安全检查方法的接口及实现类。
-
四、要启用@AspectJ
支持,@AspectJ
使用了Java 5
的注解,必须是Java 5
及后的才能使用。
-
在applicationContext.xml加入:<aop:aspectj-autoproxy/>启用@AspectJ
支持。
-
并在我们的用户自定义包中要加入aspectjrt.jar,aspectjweaver.jar,这两个包可以spring发布包
-
的lib\aspectj下找到。
-
五、声明一个切面:
-
在类的定义前加入@Aspect
,并引入包 org.aspectj.lang.annotation.Aspect
-
@Aspect
我们把用Aspect注解的类就叫切面
切面如:
Java代码
-
package
com.lwf.aop;
-
-
import
org.aspectj.lang.annotation.Aspect;
-
-
@Aspect
-
public
class
MySecurityManagerImpl
implements
MySecurityManager {
-
-
public
void
checkSecurity() {
-
System.out.println("User security Check"
);
-
}
-
-
}
Java代码
-
六、声明一个切入点(pointcut)
-
在前面我们提到,切入点决定了连接点关注的内容,使得我们可以控制通知什么时候执行。
-
Spring AOP只支持Spring bean的方法执行连接点。所以你可以把切入点看做是Spring bean上方法执行的匹配。
-
一个切入点声明有两个部分:一个包含名字和任意参数的签名,还有一个切入点表达式,该表达式决定了我们关注那个方法的执行。
-
在@AspectJ
注解风格的AOP中,一个切入点签名通过一个普通的方法定义来提供,
-
并且切入点表达式使用@Pointcut
注解来表示(作为切入点签名的方法必须返回
void
类型)。
如:
Java代码
-
package
com.lwf.aop;
-
-
import
org.aspectj.lang.annotation.Aspect;
-
import
org.aspectj.lang.annotation.Pointcut;
-
/*
-
* 定义切面
-
*/
-
@Aspect
-
public
class
MySecurityManagerImpl
implements
MySecurityManager {
-
-
/*
-
* 定义切入点,该方法返回值为void,该方法只是一个标识,就象配置文件的id
-
* 切入点的内容是一个表达式,来描述切入哪些对象的哪些方法
-
* ("excute (*add*(..))")切入点表达表示将要切入所有以add开头的方法,该方法可带任意个数的参数
-
*/
-
@Pointcut
(
"execution (* add*(..))"
)
-
public
void
addAllMethod(){};
-
-
public
void
checkSecurity() {
-
System.out.println("User security Check"
);
-
}
-
-
}
Java代码
-
七、声明通知
-
通知是跟一个切入点表达式关联起来的,并且在切入点匹配的方法执行之前或者之后或者前后运行。
-
切入点表达式可能是指向已命名的切入点的简单引用或者是一个已经声明过的切入点表达式。
-
通知有:前置通知,后置通知,异常通知,最终通知,环绕通知
如:我们声明一个前置通知
Java代码
-
package
com.lwf.aop;
-
-
import
org.aspectj.lang.annotation.Aspect;
-
import
org.aspectj.lang.annotation.Before;
-
import
org.aspectj.lang.annotation.Pointcut;
-
/*
-
* 定义切面
-
*/
-
@Aspect
-
public
class
MySecurityManagerImpl
implements
MySecurityManager {
-
-
/*
-
* 定义切入点,该方法返回值为void,该方法只是一个标识,就象配置文件的id
-
* 切入点的内容是一个表达式,来描述切入哪些对象的哪些方法
-
* ("excute (*add*(..))")切入点表达表示将要切入所有以add开头的方法,该方法可带任意个数的参数
-
*/
-
@Pointcut
(
"execution (* add*(..))"
)
-
public
void
addAllMethod(){};
-
-
-
-
/*
-
* 前置通知,在addAllMethod切入点所代表的方法前调用checkSecurity方法
-
*
-
*/
-
@Before
(
"addAllMethod()"
)
-
public
void
checkSecurity() {
-
System.out.println("User security Check"
);
-
}
-
-
}
上面是分步的配置,下面我把整个配置好的项目代码列出来:
Java代码
-
package
com.lwf.aop;
-
-
public
interface
UserManager {
-
-
public
void
add(String name, String password);
-
public
void
del(String id);
-
public
void
modify(
int
id ,String name, String password);
-
}
Java代码
-
package
com.lwf.aop;
-
-
public
class
UserManagerImpl
implements
UserManager {
-
-
public
void
add(String name, String password) {
-
-
System.out.println("add method"
);
-
}
-
-
public
void
del(String id) {
-
System.out.println("del method"
);
-
}
-
-
public
void
modify(
int
id, String name, String password) {
-
System.out.println("modify method"
);
-
}
-
-
}
Java代码
-
package
com.lwf.aop;
-
-
public
interface
MySecurityManager {
-
-
public
void
checkSecurity();
-
}
Java代码
-
package
com.lwf.aop;
-
-
import
org.aspectj.lang.annotation.Aspect;
-
import
org.aspectj.lang.annotation.Before;
-
import
org.aspectj.lang.annotation.Pointcut;
-
/*
-
* 定义切面
-
*/
-
@Aspect
-
public
class
MySecurityManagerImpl
implements
MySecurityManager {
-
-
/*
-
* 定义切入点,该方法返回值为void,该方法只是一个标识,就象配置文件的id
-
* 切入点的内容是一个表达式,来描述切入哪些对象的哪些方法
-
* ("excute (*add*(..))")切入点表达表示将要切入所有以add开头的方法,该方法可带任意个数的参数
-
*/
-
@Pointcut
(
"execution(* add*(..))"
)
-
public
void
addAllMethod(){};
-
-
-
/*
-
* 前置通知,在addAllMethod切入点所代表的方法前调用checkSecurity方法
-
*
-
*/
-
@Before
(
"addAllMethod()"
)
-
public
void
checkSecurity() {
-
System.out.println("User security Check"
);
-
}
-
-
}
配置文件:
Java代码
-
<?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.5.xsd
-
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
-
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
-
default
-autowire=
"byType"
-
>
-
-
-
<aop:aspectj-autoproxy/>
-
<bean id="userManager"
class
=
"com.lwf.aop.UserManagerImpl"
></bean>
-
<bean id="mySecurityManager"
class
=
"com.lwf.aop.MySecurityManagerImpl"
></bean>
-
</beans>
下面我们创建一个测试类:
Java代码
-
package
com.lwf.aop;
-
-
import
junit.framework.TestCase;
-
-
import
org.springframework.context.ApplicationContext;
-
import
org.springframework.context.support.ClassPathXmlApplicationContext;
-
-
public
class
Client
extends
TestCase{
-
-
public
void
testAop(){
-
ApplicationContext ac = new
ClassPathXmlApplicationContext(
"applicationContext.xml"
);
-
UserManager userManager = (UserManager)ac.getBean("userManager"
);
-
userManager.add("zhangshang"
,
"123456"
);
-
}
-
}
好了,上面的测试类,应该输出什么呢?
按照我们的静态代理,在调用add之前要先调用check方法,这里我们是先调用 checkSecurity()方法。
看下面的输出结果:
Java代码
-
2010
-
05
-
20
17
:
08
:
44
,
461
INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext
@affc70
: display name [org.springframework.context.support.ClassPathXmlApplicationContext
@affc70
]; startup date [Thu May
20
17
:
08
:
44
CST
2010
]; root of context hierarchy
-
2010
-
05
-
20
17
:
08
:
44
,
633
INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from
class
path resource [applicationContext.xml]
-
2010
-
05
-
20
17
:
08
:
45
,
055
INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - Bean factory
for
application context [org.springframework.context.support.ClassPathXmlApplicationContext
@affc70
]: org.springframework.beans.factory.support.DefaultListableBeanFactory
@1c9a690
-
2010
-
05
-
20
17
:
08
:
45
,
243
INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory
@1c9a690
: defining beans [org.springframework.aop.config.internalAutoProxyCreator,userManager,mySecurityManager]; root of factory hierarchy
-
User security Check
-
add method
显然调用了 checkSecurity()方法。
需要注意的是在切入点:@Pointcut ("execution(* add*(..))")这个地方一定要写对
是execution而不是execute,还有我们设置的是所有以add字符串开头的方法,注意前面的*与add之间要有空隔。因为最前面的*号代表所有的返回类型,而add*(..)中的*表示所有以add开头的方法名。
上面我们只定义了在add开头的方法前执行检查,那么我们也可以在del之前执行检查,使用||操作符,如下:
Java代码
-
@Pointcut
(
"execution(* add(..)) || execution(* del(..))"
)
还要注意通知@Before("addAllMethod()"),不要写成@Before("addAllMethod")
还应该注意到切入点addAllMethod()这个方法是不会被执行的,只是起到一个标志作用。
现
在来总结一下:从静态代理到spring
aop我们都实现了在操作用户之前调用方法进行用户检查。静态代理我们看成是OOP的处理,它需要代理类通过继承,是树型结构,要实现就要改变原来的树
型,即是有侵入性的。而spring aop则是横向的切入。没有改变原来的结构,是没有侵入性的。
AOP的没有侵入性的特性,是对OOP的一个补充。
对于常用的切入点表达式有:
Java代码
-
使用的最频繁的返回类型模式是*,它代表了匹配任意的返回类型。
-
一个全限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用*通配符作为所有或者部分命名模式。
-
参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法, 而(..)匹配了一个接受任意数量参数的方法(零或者更多)。
-
模式(*)匹配了一个接受一个任何类型的参数的方法。 模式(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。
-
任意公共方法的执行:
execution(public * *(..))
-
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
-
AccountService
接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
-
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
-
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
-
在service包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service.*)
-
在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service..*)
-
实现了AccountService
接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):
this(com.xyz.service.AccountService)
'this'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得代理对象在通知体内可用。
-
实现AccountService
接口的目标对象的任意连接点 (在Spring AOP中只是方法执行):
target(com.xyz.service.AccountService)
'target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得目标对象在通知体内可用。
-
任何一个只接受一个参数,并且运行时所传入的参数是Serializable
接口的连接点(在Spring AOP中只是方法执行)
args(java.io.Serializable)
'args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得方法参数在通知体内可用。
请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable))
: args版本只有在动态运行时候传入参数是Serializable时才匹配,而execution版本在方法签名中声明只有一个 Serializable
类型的参数时候匹配。
-
目标对象中有一个 @Transactional
注解的任意连接点 (在Spring AOP中只是方法执行)
@target(org.springframework.transaction.annotation.Transactional)
'@target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
-
任何一个目标对象声明的类型有一个 @Transactional
注解的连接点 (在Spring AOP中只是方法执行):
@within(org.springframework.transaction.annotation.Transactional)
'@within'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
-
任何一个执行的方法有一个 @Transactional
注解的连接点 (在Spring AOP中只是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
-
任何一个只接受一个参数,并且运行时所传入的参数类型具有@Classified
注解的连接点(在Spring AOP中只是方法执行)
@args(com.xyz.security.Classified)
'@args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
-
任何一个在名为'tradeService
'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):
bean(tradeService)
-
任何一个在名字匹配通配符表达式'*Service
'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):
bean(*Service)
分享到:
相关推荐
在压缩包文件"spring-aop-aspectj"中,可能包含了关于Spring AOP和@AspectJ的示例代码或文档,可以帮助你进一步理解和实践这些知识。通过学习和实践,你将能更好地掌握这一强大的工具,从而在你的IT职业生涯中征服...
`springAOP2`可能是一个包含具体示例代码的目录。`基于@AspectJ配置Spring AOP之一 - 飞扬部落编程仓库-专注编程,网站,专业技术.htm`和其关联的`_files`目录可能包含了一个详细的教程或演示如何配置和运行@AspectJ的...
一个基于@AspectJ的spring2.0 AOP应用实例,很小很简单,没有任何额外信息,最适合AOP入门学习。使用log4j打印信息。把项目直接import进myeclipse就可以使用啦......
本实例将带你深入理解并实践Spring AOP与@AspectJ的结合使用。 首先,了解AOP的基本概念。面向切面编程是一种编程范式,它允许程序员定义“切面”,即跨越多个对象的行为或责任。这些切面可以包含业务逻辑、日志、...
通过阅读Spring AOP和AspectJ的源码,可以深入理解其内部工作原理。例如,`MethodBeforeAdvice` 接口是如何被调用来执行前置通知的,`AspectJExpressionPointcut` 类如何解析和匹配切入点表达式,以及`...
Spring 框架是 Java 开发中的重要组件,它提供了丰富的功能,其中之一就是对面向切面编程(AOP)的支持。面向切面编程是一种编程范式,旨在将关注点分离,使得业务逻辑与横切关注点(如日志、事务管理、安全检查等)...
本篇文章将深入探讨如何利用Spring的@AspectJ注解来实现AOP,这是一个入门级别的例子,旨在帮助开发者理解并掌握这一关键特性。 首先,我们要明白什么是AOP。面向切面编程是一种编程范式,它允许程序员定义“切面”...
在Spring4.3.7版本中,Spring支持使用AspectJ进行AOP编程。引入AspectJ的依赖包,可以使用AspectJ的表达式语言(Pointcut Expression Language, PEL)来精确地定义切点。例如,`execution(* com.example.service.*(....
通过这种方式,Spring结合AspectJ提供的AOP支持,使得我们可以编写更加模块化和可维护的代码,将关注点分离,提高代码的重用性和可读性。在实际开发中,这尤其适用于那些横切多个业务逻辑的功能,如事务管理、日志...
Spring AOP的AspectJ支持jar包; 包括: com.springsource.net.sf.cglib-2.2.0.jar com.srpingsource.org.aopalliance-1.0.0.jar com.srpingsource.org.aspectj.weaver-1.68.RELEASE.jar
接下来,我们通过一个简单的Spring AOP示例来加深对上述概念的理解。假设我们需要在调用某个公共方法前记录日志,我们可以定义一个`BeforeAdvice`,并在目标方法上应用此通知。 ```java package com.example.aop; ...
首先,我们需要理解AOP的核心概念: 1. **切面(Aspect)**:切面是关注点的模块化,它封装了多个相关方法,这些方法在程序的不同点执行,比如通知(advice)。 2. **连接点(Join Point)**:程序执行过程中的某个...
Spring支持两种AOP的实现方式:Spring AspectJ注解风格和Spring XML配置风格。使用AspectJ注解风格是最常见的,它允许开发者直接在方法上使用注解来定义切面。 Spring AOP中有五种不同类型的的通知(Advice): 1....
除了注解式AOP,Spring还支持基于XML的配置,但注解方式更简洁且易于理解和维护。在实际应用中,通常会结合使用`@Aspect`和其他Spring注解如`@Service`、`@Repository`和`@Controller`,以实现全面的依赖注入和AOP...
2. **AspectJ集成**:虽然Spring AOP功能强大,但AspectJ提供了更全面的AOP特性,如注解支持、类型匹配、环绕通知等。当Spring与AspectJ结合使用时,可以实现更复杂、更细粒度的切面。这通常需要引入AspectJ的库,如...