AOP:面向切面编程
Spring 的 动态代理模式有两种: JDK动态代理和 CGLIB动态代理。
JDK动态代理:通过 java.lang.reflect.Proxy类实现的,必须实现一个或多个接口,代码实现。
CGLIB动态代理:不需要实现接口,底层通过使用字节码处理框架ASM转换字节码,并生成新的类。
目前流行的AOP框架有两种 Spring AOP 和 AspectJ
Spring AOP :使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。
AspectJ :是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
Spring AOP开发AOP:
声明式可以选择性使用 JDK和CGLIB
<!--true:使用cglib; false :使用jdk动态代理-->
<property name="proxyTargetClass" value="true" />
AspectJ开发AOP两种方式:
基于 XML 的声明式。
基于 Annotation 的声明式。
一下代码逐一举例-------------------
JDK动态代理:
1、创建业务类接口 CustomerService 以及业务类实现类 CustomerServiceImpl;
package com.woo.service;
public interface CustomerService {
//crud
void create();
void retrieve();
void update();
void delete();
}
package com.woo.service.impl;
import com.woo.service.CustomerService;
public class CustomerServiceImpl implements CustomerService {
@Override
public void create(){
System.out.println("create----");
}
@Override
public void retrieve(){
System.out.println("retrieve----");
}
@Override
public void update(){
System.out.println("update----");
}
@Override
public void delete(){
System.out.println("delete-----");
}
}
2、创建切面类:MyAspect,写增强方法 myBefore ,myAfter;
package com.woo.service.impl;
public class MyAspect {
public void myBefore(){
System.out.println("before");
}
public void myAfter(){
System.out.println("after");
}
}
3、创建代理类:MyFactory
package com.woo.factory;
import com.woo.service.CustomerService;
import com.woo.service.impl.CustomerServiceImpl;
import com.woo.service.impl.MyAspect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyFactory {
public static CustomerService getBean() {
//准备目标类
final CustomerService customerService = new CustomerServiceImpl();
//创建切面类实例,用于调用切面类中相应的方法
final MyAspect myAspect = new MyAspect();
//使用代理,进行增强
return (CustomerService) Proxy.newProxyInstance(
MyFactory.class.getClassLoader(),//当前类的类加载器
new Class[]{CustomerService.class},//所创建实例的实现类的接口
new InvocationHandler() {//需要增强的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.myBefore();//前增强
Object obj = method.invoke(customerService,args);
myAspect.myAfter();
return obj;
}
}
);
}
}
4、测试
@Test
public void test3(){
//从工厂获取指定的内容,
CustomerService customerService = MyFactory.getBean();
//执行方法
customerService.create();
customerService.retrieve();
customerService.update();
customerService.delete();
}
5、输出:
CGLIB动态代理:
1、创建业务类 CustomerServiceImpl;
package com.woo.service.impl;
import com.woo.service.CustomerService;
public class CustomerServiceImpl implements CustomerService {
@Override
public void create(){
System.out.println("create----");
}
@Override
public void retrieve(){
System.out.println("retrieve----");
}
@Override
public void update(){
System.out.println("update----");
}
@Override
public void delete(){
System.out.println("delete-----");
}
}
2、创建切面类:MyAspect,写增强方法 myBefore ,myAfter;
package com.woo.service.impl;
public class MyAspect {
public void myBefore(){
System.out.println("before");
}
public void myAfter(){
System.out.println("after");
}
}
3、创建代理类:MyFactory
package com.woo.factory;
import com.woo.service.impl.CustomerServiceImpl;
import com.woo.service.impl.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyFactory {
public static CustomerServiceImpl getCgLibBean(){
//创建目标类
final CustomerServiceImpl customerServiceImpl = new CustomerServiceImpl();
//创建切面类
final MyAspect myAspect = new MyAspect();
//生成代理,CGLIB在运行时,生成指定对象的子类,增强
Enhancer enhancer = new Enhancer();
//确定需要增强的类
enhancer.setSuperclass(CustomerServiceImpl.class);
//添加回调函数;
enhancer.setCallback(new MethodInterceptor() {
//intercept相当于JDK代理的invoke方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
myAspect.myAfter();
Object obj = method.invoke(customerServiceImpl,objects);
myAspect.myBefore();
return obj;
}
});
//创建代理类
CustomerServiceImpl customerServiceImplProxy = (CustomerServiceImpl) enhancer.create();
return customerServiceImplProxy;
}
}
4、测试:
@Test
public void test4(){
//从工厂获取指定的内容,
CustomerServiceImpl customerServiceImpl = MyFactory.getCgLibBean();
//执行方法
customerServiceImpl.create();
customerServiceImpl.retrieve();
customerServiceImpl.update();
customerServiceImpl.delete();
}
5、输出:
Spring AOP 声明式创建 AOP代理
1、创建业务类接口 CustomerService 以及业务类实现类 CustomerServiceImpl;
package com.woo.service;
public interface CustomerService {
//crud
void create();
void retrieve();
void update();
void delete();
}
package com.woo.service.impl;
import com.woo.service.CustomerService;
public class CustomerServiceImpl implements CustomerService {
@Override
public void create(){
System.out.println("create----");
}
@Override
public void retrieve(){
System.out.println("retrieve----");
}
@Override
public void update(){
System.out.println("update----");
}
@Override
public void delete(){
System.out.println("delete-----");
}
}
2、创建切面类:SpringMyAspect,实现接口:MethodInterceptor写增强方法 myBefore ,myAfter;
package com.woo.service.impl;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//需要实现接口,确定哪个通知,及告诉Spring 应该执行哪个方法;
public class SpringMyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("方法执行之前---");
//执行目标方法
Object obj = methodInvocation.proceed();
System.out.println("方法执行之后---");
return obj;
}
}
3、修改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: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">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<!--目标类-->
<bean id = "customerService" class="com.woo.service.impl.CustomerServiceImpl"/>
<!--通知advice-->
<bean id = "springMyAspect" class="com.woo.service.impl.SpringMyAspect"/>
<!--生成代理对象-->
<bean id = "customerServiceProxy" class = "org.springframework.aop.framework.ProxyFactoryBean">
<!--代理实现接口-->
<property name = "proxyInterfaces" value = "com.woo.service.CustomerService" />
<!--代理目标对象-->
<property name = "target" ref = "customerService"/>
<!--用通知增强目标-->
<property name = "interceptorNames" value="springMyAspect"/>
<!--如何生成代理,true-使用CGLIB,false-使用JDK-->
<property name = "proxyTargetClass" value = "true"/>
</bean>
</beans>
4、测试:
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
CustomerService customerService = (CustomerService) context.getBean("customerServiceProxy");
customerService.create();
customerService.retrieve();
customerService.update();
customerService.delete();
}
5、输出:
AspectJ 基于XML声明式创建AOP代理:
1、加入maven依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
2、创建业务类接口CustomerService和业务类CustomerServiceImpl
package com.woo.service;
public interface CustomerService {
//crud
void create();
void retrieve();
void update();
void delete();
}
package com.woo.service.impl;
import com.woo.service.CustomerService;
public class CustomerServiceImpl implements CustomerService {
@Override
public void create(){
System.out.println("create----");
}
@Override
public void retrieve(){
System.out.println("retrieve----");
}
@Override
public void update(){
System.out.println("update----");
}
@Override
public void delete(){
System.out.println("delete-----");
}
}
3、创建切面类 MyAspectJ
package com.woo.service.impl;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectJ {
//前置通知,joinPoint 参数可以获得目标对象的类名、目标方法名和目标方法参数等
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知---");
System.out.println(joinPoint.getTarget() + "方法名称:" + joinPoint.getSignature().getName());
}
//后置通知
public void myAfter(JoinPoint joinPoint){
System.out.println("后置通知---");
System.out.println(joinPoint.getTarget() + "方法名称:" + joinPoint.getSignature().getName());
}
//环绕通知,必须有入参proceedingJoinPoint,有返回obj,抛出异常Throwable
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始---");
Object obj = proceedingJoinPoint.proceed();//执行当前目标方法
System.out.println("环绕通知结束---");
return obj;
}
//异常通知,throwable用于输出异常信息
public void myThrowing(JoinPoint joinPoint,Throwable throwable){
System.out.println("异常通知,出错了:---" + throwable.getMessage());
}
//最终通知
public void myFinally(){
System.out.println("最终通知---");
}
}
4、配置Spring.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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 = "customerService" class="com.woo.service.impl.CustomerServiceImpl"/>
<!--切面类-->
<bean id = "myAspectJ" class="com.woo.service.impl.MyAspectJ"/>
<!--AOP编程-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="myAspectJ">
<!--配置切入点,通知使用哪些增强方法-->
<aop:pointcut id = "myPointCut" expression="execution(* com.woo.service.*.*(..))"/>
<!--前置通知,关联通知advice和切入点pointCut-->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
<!--后置通知,在方法返回之后执行,就可以获得返回值returning属性-->
<aop:after-returning method="myAfter" pointcut-ref="myPointCut" />
<!--环绕通知-->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!--异常通知,throwing用于设置属性名称-->
<aop:after-throwing method="myThrowing" pointcut-ref="myPointCut" throwing="throwable"/>
<!--最终通知,无论程序发生什么,都会执行-->
<aop:after method="myFinally" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
5、测试
@Test
public void test6(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
CustomerService customerService = (CustomerService) context.getBean("customerService");
customerService.create();
}
6、输出
AspectJ 基于Annotation声明式创建AOP代理:
1、创建业务类接口CustomerService和业务类CustomerServiceImpl
package com.woo.service;
public interface CustomerService {
//crud
void create();
void retrieve();
void update();
void delete();
}
package com.woo.service.impl;
import com.woo.service.CustomerService;
import org.springframework.stereotype.Service;
@Service(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
@Override
public void create(){
System.out.println("create----");
}
@Override
public void retrieve(){
System.out.println("retrieve----");
}
@Override
public void update(){
System.out.println("update----");
}
@Override
public void delete(){
System.out.println("delete-----");
}
}
2、创建切面类 MyAspectJAnno
package com.woo.service.impl;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspectJAnno {
/**
* 用于取代<aop:pointcut id = "myPointCut" expression="execution(* com.woo.service.*.*(..))"/>
* rule:方法必须是private,空方法,名称自定义,没有入参
*/
@Pointcut(value = "execution(* com.woo.service.*.*(..))")
private void myPointCut(){
}
//前置通知
@Before(value = "myPointCut()")
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知---");
System.out.println(joinPoint.getTarget() + "方法名称:" + joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning(value = "myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("后置通知---");
System.out.println(joinPoint.getTarget() + "方法名称:" + joinPoint.getSignature().getName());
}
//环绕通知
@Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始---");
Object obj = proceedingJoinPoint.proceed();//执行当前目标方法
System.out.println("环绕通知结束---");
return obj;
}
//异常通知
@AfterThrowing(value = "myPointCut()",throwing = "throwable")
public void myThrowing(JoinPoint joinPoint,Throwable throwable){
System.out.println("异常通知,出错了:---" + throwable.getMessage());
}
//最终通知
@After(value = "myPointCut()")
public void myFinally(){
System.out.println("最终通知---");
}
}
3、配置Spring.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!--扫描com.woo包下的所有注解-->
<context:component-scan base-package="com.woo"/>
<!--使用切面,开启自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4、测试
@Test
public void test6(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
CustomerService customerService = (CustomerService) context.getBean("customerService");
customerService.create();
}
5、输出
注:AspectJ 的两种实现 AOP 的方式 测试出的 通知顺序略有不同。