原理
AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在Spring的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP
常用场景:
切面一般对如下内容进行封装,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
- 事务
- 权限处理
- 日志
- 性能检测
- 异常处理
示例工程:
代码:
XML配置AOP
applicationContext.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="productservice" class="com.how2java.service.ProductService"/>
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
<bean id="diy" class="com.how2java.aspect.DiyAspect"/>
<aop:config>
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:around pointcut-ref="loggerCutpoint" method="log" />
<aop:around pointcut-ref="loggerCutpoint" method="performance" />
</aop:aspect>
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.how2java.service.ProductService.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
DiyAspect.java
package com.how2java.aspect;
public class DiyAspect {
public void before() {
System.out.println("方法执行前");
}
public void after() {
System.out.println("方法执行后");
}
}
LoggerAspect.java
package com.how2java.aspect;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
public Object performance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.nanoTime(); // 获取开始时间
System.out.println(new Date().toString());
Object object = joinPoint.proceed();
long endTime = System.nanoTime(); // 获取结束时间
System.out.println(new Date().toString());
System.out.println("程序运行时间: " + (endTime - startTime) + "ns");
return object;
}
}
ProductService.java
package com.how2java.service;
import org.springframework.stereotype.Component;
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
public void doSomeOtherService() {
System.out.println("doSomeOtherService");
}
}
TestSpring.java
package com.how2java.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.how2java.service.ProductService;
public class TestSpring {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductService s = (ProductService) context.getBean("productservice");
s.doSomeService();
s.doSomeOtherService();
}
}
注解配置AOP
applicationContext.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.how2java.aspect"/>
<context:component-scan base-package="com.how2java.service"/>
<aop:aspectj-autoproxy/>
</beans>
DiyAspect.java
package com.how2java.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DiyAspect {
@Before(value = "execution(* com.how2java.service.ProductService.*(..))")
public void before() {
System.out.println("方法执行前");
}
@After(value = "execution(* com.how2java.service.ProductService.*(..))")
public void after() {
System.out.println("方法执行后");
}
}
LoggerAspect.java
package com.how2java.aspect;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggerAspect {
@Around(value = "execution(* com.how2java.service.ProductService.*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
@Around(value = "execution(* com.how2java.service.ProductService.*(..))")
public Object performance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.nanoTime(); // 获取开始时间
System.out.println(new Date().toString());
Object object = joinPoint.proceed();
long endTime = System.nanoTime(); // 获取结束时间
System.out.println(new Date().toString());
System.out.println("程序运行时间: " + (endTime - startTime) + "ns");
return object;
}
}
ProductService.java
package com.how2java.service;
import org.springframework.stereotype.Component;
@Component("productservice")
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
public void doSomeOtherService() {
System.out.println("doSomeOtherService");
}
}
TestSpring.java
package com.how2java.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.how2java.service.ProductService;
public class TestSpring {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductService s = (ProductService) context.getBean("productservice");
s.doSomeService();
s.doSomeOtherService();
}
}
要点:
1.Aop在xml要配置
xmlns:aop=”http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xs
分别对应applicationContext.xml第五行、第十四十五行。
2.Advice主要类型:
- @Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;
- @AfterReturning:该注解标注的方法在业务模块代码执行之后执行;
- @AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;
- @After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;
- @Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;
- @DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
@Aspect:严格来说,其不属于一种Advice,该注解主要用在类声明上,指明当前类是一个组织了切面逻辑的类,并且该注解中可以指定当前类是何种实例化方式,主要有三种:singleton、perthis和pertarget,具体的使用方式后面会进行讲解。
这里需要说明的是,@Before是业务逻辑执行前执行,与其对应的是@AfterReturning,而不是@After,@After是所有的切面逻辑执行完之后才会执行,无论是否抛出异常。
3.在aop中execution的写法规则
执行表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外,所有的部分都是可选的。 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是 ,它代表了匹配任意的返回类型。 一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:() 匹配了一个不接受任何参数的方法, 而 (..) 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 () 匹配了一个接受一个任何类型的参数的方法。 模式 (,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型。
一些常见切入点表达式的例子。
execution(public (..)) 任意公共方法的执行;
execution( set(..)) 任何一个以“set”开始的方法的执行;
execution( com.xyz.service.AccountService.(..)) AccountService接口的任意方法的执行;
execution( com.xyz.service..(..)) 定义在service包里的任意方法的执行;
execution( com.xyz.service...(..)) 定义在service包或者子包里的任意方法的执行;4.作用同@Component的注解
@Repository 作用同@Component; 在持久层使用
@Service 作用同@Component; 在业务逻辑层使用
@Controller 作用同@Component; 在控制层使用