Aspect Oriented Programing 面向切面编程
为某一个对象(委托类)准备一个代理(代理类),用来控制对这个对象的访问。
两者有一个共同的父类或父接口。
代理类会对请求做预处理、过滤,将请求分配给指定对象。
代理三要素:以结婚为例
有共同的行为(结婚)- 定义成接口
package com.sundingyi.proxy;
/**
* 定义行为
*/
public interface Marry {
public void toMarry();
}
目标角色(新人)- 实现接口
package com.sundingyi.proxy;
/**
* 目标角色 实现行为
*/
public class You implements Marry {
@Override
public void toMarry() {
System.out.println("you的结婚");
}
}
代理角色(婚庆公司) - 实现接口、用户行为的增强
package com.sundingyi.proxy;
/**
* 静态代理的代理角色
* 1. 实现行为
* 2. 增强行为
*/
public class MarryCompany implements Marry{
// 准备目标对象
private Marry target;
// 通过带参构造传递目标对象
public MarryCompany(Marrytarget) {
this.target =target;
}
//1
@Override
public void toMarry() {
// 用户行为增强
System.out.println("增强方法");
// 调用目标对象方法
target.toMarry();
}
}
执行测试:
package com.sundingyi.proxy;
public class StaticProxy {
public static void main(String[] args) {
// 目标对象
You you = new You();
// 代理对象
MarryCompany marryCompany = new MarryCompany(you);
// 通过代理对象
marryCompany.toMarry();
}
}
package com.sundingyi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK 动态代理类
* 每一个代理类都需要实现 InvocationHandler 接口
*/
public class JdkHandler implements InvocationHandler {
// 目标对象 不固定, 创建时动态生成
private Object target;
// 通过带参构造器传递目标对象
public JdkHandler(Objecttarget) {
this.target =target;
}
/**
* 获取代理对象
* @return
*/
public Object getProxy() {
Object object = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return object;
}
/**
* 调用目标对象的方法
* 增强目标对象的行为
* @param proxy调用该方法的代理实例
* @param method目标对象的方法
* @param args目标对象的方法所需要的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 用户增强行为(前后都可能会有)
System.out.println("增强方法");
// 调用目标对象中的方法(返回 Object)
Object object =method.invoke(target,args);
return object;
}
}
使用
package com.sundingyi.proxy;
public class JdkHandlerTest {
public static void main(String[] args) {
// 目标对象
You you = new You();
// 代理类
JdkHandler jdkHandler = new JdkHandler(you);
// 得到代理对象
Marry marry = (Marry) jdkHandler.getProxy();
marry.toMarry();
}
}
实现原理:继承
<!-- <https://mvnrepository.com/artifact/cglib/cglib> -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
package com.sundingyi.proxy;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.tracing.dtrace.ArgsAttributes;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
public class CglibInterceptor implements MethodInterceptor {
// 目标对象
private Object target;
// 通过构造传入目标对象
public CglibInterceptor(Object target) {
this.target = target;
}
/**
* 获取代理对象
* @return
*/
public Object getProxy(){
// 通过 Enhancer 中的 create() 生成一个类
Enhancer enhancer = new Enhancer();
// 将目标类作为代理类的父类
enhancer.setSuperclass(target.getClass());
// 设置拦截器 回调对象为本身
enhancer.setCallback(this);
// 生成代理类对象,返回
return enhancer.create();
}
/**
* 拦截器
* 1. 目标对象的方法调用
* 2. 行为增强
* @param o cglib 动态生成的代理类的实例
* @param method 实体类中所调用的被代理的方法的引用
* @param objects 参数列表
* @param methodProxy 生成的代理类对方法的代理引用(代理对象)
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 增强行为
System.out.println("增强行为");
// 调用目标类中的方法
Object object = method.invoke(target, objects);
return object;
}
}
package com.sundingyi.proxy;
public class JdkHandlerTest {
public static void main(String[]args) {
// 目标对象
You you = new You();
// 代理类
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
// 得到代理对象
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();
}
}
区别:
<!-- <https://mvnrepository.com/artifact/org.aspectj/aspectjweaver> -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<?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: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.sundingyi"/>
<!-- 配置AOP自动代理-->
<aop:aspectj-autoproxy/>
</beans>
切面是切入点和通知的抽象。
切入点:定义拦截哪些类的哪些方法
通知:定义拦截以后要做什么事情
切入点表达式:
执行所有的 public 方法 @Pointcut("execution(* com.sundingyi.service.*.*(..))")
第一个 * 代表了方法的修饰范围,有三个(public private protected) 如果是一个 * 则表示所有范围。所以要这么写: execution(public *(..))
执行所有的set() execution(* *(..))
设置指定包下的任意一个类的任意方法(比如com.xxxx.service) execution(* com.xxxx.service.*.*(..))
设置指定包(service)及子包下的所有方法
execution(* com.xxxx.service..*.*(..))
package com.sundingyi.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component// 交给 IOC 容器实例化
@Aspect// 声明当前类是一个切面 定义切入点和通知
public class LogCut {
// 切入点
@Pointcut("execution(* com.sundingyi.service..*.*(..))")
public void cut() {
}
/**
* 声明前置通知,并将通知应用到指定的切入点上
* 目标类的方法执行前会执行该通知
*/
@Before(value = "cut()")
public void before() {
System.out.println("前置通知");
}
/**
* 声明返回通知,并将通知应用到指定的切入点上
* 目标类的方法在无异常执行后,执行该通知。
*/
@AfterReturning(value = "cut()")
public void afterReturn() {
System.out.println("返回通知");
}
/**
* 声明最终通知,并将通知应用到指定的切入点上
* 目标类的方法无论是有无异常执行后,该通知都会执行
*/
@After(value = "cut()")
public void after() {
System.out.println("after()...");
}
/**
* 声明最终通知,并将通知应用到指定的切入点上
* 目标类的方法在异常时,执行该通知
*/
@AfterThrowing(value = "cut()")
public void afterThrow() {
System.out.println("异常通知...");
}
/**
* 声明环绕通知,并将通知应用到指定的切入点上
* 目标类的方法执行前后都可以通过环绕通知定义相应的处理
* 需要通过显式调用方法,否则无法访问指定方法,pjp.proceed()
*/
@Around(value = "cut()")
public Object around(ProceedingJoinPointpjp) {
System.out.println("环绕通知-前置通知");
Object object = null;
try {
// 显式调用对应的方法
object =pjp.proceed();
System.out.println(pjp.getTarget());
System.out.println("环绕通知-返回通知");
} catch (Throwablethrowable) {
throwable.printStackTrace();
System.out.println("环绕通知异常通知");
} finally {
System.out.println("环绕通知-最终通知");
}
return object;
}
}
<aop:config>
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
<aop:before method="printTime" pointcut-ref="addAllMethod" />
<aop:after method="printTime" pointcut-ref="addAllMethod" />
</aop:aspect>
</aop:config>