上一篇博客介绍了AOP思想,并且解释了使用JDK的动态代理实现AOP思想,下面介绍使用Spring 来实现AOP。首先我们对AOP思想的基本术语必须要了解,基本术语:
* 横切性关注点(cross cutting conern): 使用AOP思想所能解决的问题,比如,在调用目的请求前进行字符集转换、在进行业务逻辑前进行权限控制、在关键方法前进行日志记录等都是对问题的抽象。
*切面(Aspect):对横切性关注点的模块化实现,也就是字符转换逻辑,事物控制逻辑,在JDK动态代理中就是继承了InvocationHandler的代理类。
*连接点(JoinPoint):表示横切性关注点逻辑通过切入点最终作用的目标对象的那个点的抽象。通俗点讲就是AOP作用的具体的点的抽象,例如method、attribute、object,在spring中是方法。
*切入点(Pointcut):横切性关注点适用的范围的抽象,AOP思想最终要起作用的范围。切入点有一个表达式语言,spring aop可以限定到方法层次,例如 execution(“* com.wj.service.*.*Service(..)”) 表示 com.wj.service包下所有的类的所有方法名以Service结尾的方法
*通知(Advice):对切入点所匹配的连接点上触发的aop操作类型。通知分为很多种,前置通知、后置通知、环绕通知、异常通知、返回通知等。适用于不同的场景。
*代理(proxy):spring会给切入点适用范围的类生成代理,spring 默认适用JDK的动态代理,但JDK动态代理要求目标对象不需要有实现接口,对于没有接口的内容,spring会适用CGLIB库生成目标类的子类代理。
*织入(weave):通过切面、切入点、连接点来对目标对象进行代理的过程称为织入。
基本概念基本就那么多。直接看文字解释理解起来比较费劲,下面看看例子,适用AspectJ注解实现Aop;
假设我们要对操作添加日志和参数验证,那么我们的横切性关注点就是添加日志、参数验证,基本代码如下:
基本类如下:
package com.wj.aop;
public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [username=" + username + ", password=" + password + "]"; } }
package com.wj.aop;
public interface IUserService { int addService(String username,String password); User findUserService(String username); void printlInfo(); }
package com.wj.aop;
import java.util.*; public class UserService implements IUserService { private static final List<User> userList = new ArrayList<User>(); public int addService(String username, String password) { User user = new User(); user.setPassword(password); user.setUsername(username); System.out.println("add User user = " + user); userList.add(user); return 1; } public User findUserService(String username) { User result = null; for(User user : userList){ if(user.getUsername().equals(username)){ result = user; break; } } return result; } public static List<User> getUserlist() { return userList; } public void printlInfo() { System.out.println(this.userList); } }
上面是基本的数据操作,下面是对日志添加、参数验证的实现,也就是切面、切入点、通知的实现:
package com.wj.aop; import java.text.DateFormat; import java.util.Date; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; @Aspect public class AopAspect { private static Logger logger = LoggerFactory.getLogger(AopAspect.class ); @Pointcut("execution(* com.wj.aop.*.*Service(..))") private void processorRange(){} @Before("processorRange()") public void checkArg(JoinPoint joinpoint){ System.out.println("=====================================AopAspect.checkArg()"); Object[] args = joinpoint.getArgs(); for(Object obj : args){ if(obj == null || obj.toString().length() == 0){ System.out.println("=====================================参数内容为空,程序异常不能处理"); throw new NullPointerException("the args is null"); } } } @Before("processorRange()") public void logProcessor(JoinPoint joinpoint){ System.out.println("=====================================AopAspect.logProcessor()"); String argStr =""; Object[] args = joinpoint.getArgs(); for(Object obj : args){ argStr+=obj.toString(); } String methodName = joinpoint.getSignature().getName(); String time = DateFormat.getDateInstance().format(new Date()); System.out.println(time+" 调用方法是 "+ methodName +" 参数是 "+argStr); } }
代码中使用@Aspect定义了切面,使用@PointCut定义了切入点,使用@Before定义了前置通知,在方法中有个JoinPoint连接点参数,这个参数spring会自动注入,它代表了目标对象连接点的抽象,可以得到代理对象对方方法的签名、参数等数据,spring支持多种切入点指定者,其中我们用的最多的是execution匹配特定方法的制定者,其他指定者分别是call, initialization, preinitialization, staticinitialization, get, set, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this
和 @withincode:
-
execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指定者。
-
within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。
-
this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。
-
target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的appolication object)是指定类型的实例。
-
args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。
-
@target
- 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中执行的对象的类已经有指定类型的注解。 -
@args
- 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型有指定类型的注解。 -
@within
- 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。 -
@annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题有某种给定的注解
解释是翻译spring2.0 官方文档。下面给出一些常见切入点表达式的例子。
-
任意公共方法的执行:
execution(public * *(..))
-
任何一个以“set”开始的方法的执行:
execution(* set*(..))
-
AccountService
接口的任意方法的执行:execution(* com.xyz.service.AccountService.*(..))
-
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
-
定义在service包或者子包里的任意方法的执行:
execution(* com.xyz.service..*.*(..))
-
最后需要将上述类添加到spring配置文件中:
<?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"> <aop:aspectj-autoproxy /> <bean id="User" class="com.wj.aop.User" /> <bean id="UserService" class="com.wj.aop.UserService" /> <bean id="AopAspect" class="com.wj.aop.AopAspect" /> </beans>
下面是测试的客服端代码:
package com.wj.test;
import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wj.aop.*; public class client { public static void main(String[] args) { BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml"); IUserService service = (IUserService) factory.getBean("UserService"); service.addService("zhangsan", "123"); service.addService("lisi", "234"); service.printlInfo(); service.addService("AAA", ""); User user = service.findUserService("zhangsan"); System.out.println("result=" + user); } }
运行结果:
[manager] 2012-10-22 15:41:23,125 - org.springframework.beans.factory.support.DefaultListableBeanFactory -2219 [main] DEBUG - Returning cached instance of singleton bean 'UserService'
=====================================AopAspect.checkArg() =====================================AopAspect.logProcessor() 2012-10-22 调用方法是 addService 参数是 zhangsan123 add User user = User [username=zhangsan, password=123] [User [username=zhangsan, password=123]] =====================================AopAspect.checkArg() =====================================参数内容为空,程序异常不能处理 Exception in thread "main" java.lang.NullPointerException: the args is null at com.wj.aop.AopAspect.checkArg(AopAspect.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597)
上面是使用AspectJ注解方式来实现AOP,其实就是定义切面、切入点、通知,在将切入点和通知关联起来。下面我们适用spring的xml配置方式实现,将AopAspect里的所有注解去掉,spring配置文件如下:
<?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"> <aop:aspectj-autoproxy /> <bean id="User" class="com.wj.aop.User" /> <bean id="UserService" class="com.wj.aop.UserService" /> <bean id="AopAspect" class="com.wj.aop.AopAspect" /> <aop:config> <aop:aspect id="aspect" ref="AopAspect"> <aop:pointcut expression="execution(* com.wj.aop.*.*Service(..))" id="pointcut"/> <aop:before method="checkArg" pointcut-ref="pointcut"/> <aop:before method="logProcessor" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>
核心配置如下:
<aop:config> <aop:aspect id="aspect" ref="AopAspect"> <aop:pointcut expression="execution(* com.wj.aop.*.*Service(..))" id="pointcut"/> <aop:before method="checkArg" pointcut-ref="pointcut"/> <aop:before method="logProcessor" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
实际就是定义切入点、切面、通知以及他们的关系。
相关推荐
2. **Spring AOP实现方式** - **代理模式**:Spring AOP使用两种代理方式,JDK动态代理和CGLIB代理。如果目标类实现了接口,Spring会使用JDK动态代理;如果没有实现接口,Spring会使用CGLIB代理生成子类。 - **JDK...
6. **代理(Proxy)**:Spring AOP通过动态代理实现对目标对象的拦截,有JDK动态代理和CGLIB代理两种方式。JDK代理适用于目标对象实现接口的情况,而CGLIB代理则可以处理没有接口的对象。 7. **织入(Weaving)**:...
总结来说,Spring AOP通过代理模式和切面编程思想,实现了代码的解耦和模块化,提高了软件的可维护性和可扩展性。了解并熟练掌握Spring AOP的底层实现技术,对于提升开发效率和编写高质量的Java应用程序具有重要意义...
在`springAop1`这个压缩包中,可能包含了一个简单的应用示例,展示了如何定义一个切面类,以及如何在该类中定义通知方法。例如,我们可能会看到一个名为`LoggingAspect`的类,其中包含了`@Before`注解的方法,用于在...
Spring AOP主要通过两种方式实现: - **静态代理**:基于Java的接口实现,如JDK动态代理。适用于接口驱动的设计,代理类在运行时自动生成。 - **动态代理**:基于字节码操作,如CGLIB。当目标类没有接口时,Spring会...
AOP的核心思想是将应用程序的横切关注点(如日志、事务管理、安全检查等)从核心业务逻辑中解耦。在C#中,实现AOP的方法多种多样,以下将详细介绍几种常见的实现方式。 1. **静态织入**: 静态织入是在编译时完成...
在Spring中,AOP可以通过代理实现,有JDK动态代理和CGLIB两种方式。JDK动态代理适用于实现了接口的类,而CGLIB则可以代理任何类。例如,以下代码定义了一个简单的切面: ```java @Aspect @Component public class ...
Java动态代理是Java提供的一种在运行时创建新类型对象的技术,它可以让我们在不修改原有代码的情况下,对方法的调用进行增强,这正是Spring AOP(面向切面编程)的核心思想。Spring AOP通过代理模式实现了对业务逻辑...
Spring AOP是Spring框架提供的面向切面编程的实现。 ##### 2. AOP术语 - **切面**(Aspect):一个关注点的模块化。 - **通知**(Advice):在切面的特定连接点执行的代码。 - **连接点**(Joinpoint):程序执行...
- CGLIB代理:如果目标对象没有实现接口,或者我们希望为类创建代理,Spring会使用CGLIB库生成一个子类来实现AOP代理。CGLIB代理在运行时通过字节码技术生成目标类的子类,并在子类的方法上插入切面逻辑。 3. AOP...
AOP的核心思想是将那些横切多个对象的功能(如日志、事务)抽取出来,形成独立的模块,这些模块被称为“切面”。切面可以在不影响业务代码的情况下,通过“织入”(Weaving)的方式,将自身的行为添加到目标对象的...
Spring AOP是基于代理的,它提供了声明式的方式来实现AOP。 1. **切面(Aspect)**:切面是AOP的核心,它结合了关注点(例如,日志记录)和它们的实现。在Spring AOP中,一个切面通常是一个包含通知(advisors)和...
总结一下,Spring AOP提供了JDK动态代理和CGLIB代理两种方式来实现面向切面编程,通过这两种方式,我们可以方便地在不修改原始业务代码的前提下,添加额外的功能,如日志、事务控制等。`SprigAopJdk`和`...
Spring AOP通过两种方式实现:代理模式(Proxy-based)和基于ASM的字节码增强(Class-Based)。代理模式适用于接口,而字节码增强则可以应用于任何类。在配置AOP时,我们需要定义切面类,其中包含切点表达式和通知...
在IT领域,面向切面编程(Aspect-Oriented Programming,AOP)是一种强大的设计模式,它允许程序员将关注点从核心业务逻辑中分离出来,如...同时,这也是一个很好的实践机会,帮助你在实际项目中更好地应用AOP思想。
【Spring AOP与代理模式】 Spring AOP,即Spring中的面向切面编程,是一种用于解决传统面向对象编程(OOP)中代码冗余问题的技术。在OOP中,常见的如日志记录、权限验证等功能往往需要在每个业务方法中重复编写,...
面向切面编程(Aspect-Oriented Programming, AOP)是一种编程思想,它作为面向对象编程(Object-Oriented Programming, OOP)的一种补充。传统的面向对象设计模式往往难以解决横切关注点的问题,例如日志记录、性能...
Spring AOP通过代理机制来实现。根据目标对象的不同,Spring提供了两种代理方式:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的目标对象,而CGLIB代理则用于没有实现接口或需要对类进行增强的情况。 3. **切...
AOP(Aspect Oriented Programming)即面向切面编程,是一种编程思想,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。在传统的面向对象编程(OOP)中,横切关注点通常会散布在多个模块...