目录
1 代理(Proxy)模式
1.1 静态代理
1.2 动态代理
1.2.1 基于接口的动态代理
1.2.2 基于子类的动态代理
2 AOP
2.1 注解开发
1 代理(Proxy)模式
一种设计模式,它的作用是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接调用,而是通过代理类间接调用
1.1 静态代理
代理类持有目标对象,并且实现相同的接口,代理类在目标方法调用前后进行额外的操作,代理类和目标对象的关系是在编译期就写死的
静态代理工作原理:
(1) 定义一个接口(或抽象类),目标对象实现这个接口
(2) 创建一个代理类,实现目标接口,并持有目标对象
(3) 代理类重写目标接口方法,在重写方法中调用目标对象同名方法
(4) 代理类的重写方法在目标对象同名方法前后做额外操作
示例代码:
// 接口
public interface Travel {
void vacation();
}
// 目标对象
public class TravelImpl implements Travel{
@Override
public void vacation() {
System.out.println("度假...");
}
}
// 代理类
public class TravelProxy implements Travel {
private TravelImpl travel; //引入目标对象
public void setTravel(TravelImpl travel) {
this.travel = travel;
}
@Override
public void vacation() {
System.out.println("去的车票购买");
travel.vacation();
System.out.println("回的车票购买");
}
}
// 测试
@Test
public void travelTest() throws Exception {
TravelProxy travel = new TravelProxy();
travel.setTravel(new TravelImpl());
travel.vacation();
// 去的车票购买
// 度假...
// 回的车票购买
}
1.2 动态代理
JAVA动态代理是基于反射实现的,代理类的创建和方法都是在运行期完成的
1.2.1 基于接口的动态代理
原理: 生成一个方法被增强的接口的实现类的代理对象
涉及的类: java.lang.reflect.Proxy
涉及的接口: java.lang.reflect.InvocationHandler
要求: 被代理的类至少实现一个接口
示例代码:
@Test
public void travelTest() throws Exception {
// 目标对象
TravelImpl travelImpl = new TravelImpl();
/**
* newProxyInstance参数说明:
* ClassLoader(类加载器):将被代理类的字节码文件加载到JVM
* Class[](字节码数组):被代理类实现的所有接口的字节码数组
* InvocationHandler(拦截器,被代理类实现的任何"接口方法"都会被拦截)
* proxy:代理对象的引用(一般不用)
* method:被代理对象的方法对象(反射原理)
* args:当前方法所需参数
*/
// 生成代理对象
Travel travelProxy = (Travel) Proxy.newProxyInstance(TravelImpl.class.getClassLoader(), TravelImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取方法参数
Object myName = args[0];
// 获取方法名
String methodName = method.getName();
// 执行原始方法(有返回值)
//参数1:目标对象
//参数2,方法参数
System.out.println("去的车票购买");
Object methodReturn = method.invoke(travelImpl, args);
System.out.println("回的车票购买");
// 返回参数
return methodReturn;
}
});
// 执行代理对象
travelProxy.vacation();
//去的车票购买
//度假...
//回的车票购买
travelProxy.study();
//去的车票购买
//去求学...
//回的车票购买
}
1.2.2 基于子类的动态代理
原理: 生成一个方法被增强的该类的子类的代理对象
引入jar包: cglib
涉及的类: net.sf.cglib.proxy.Enhancer
要求: 被代理的类不能是最终类(final,不能被继承)
示例代码:
@Test
public void staticProxyTest() {
// 目标对象
TravelImpl travelImpl = new TravelImpl();
// 代理对象
TravelImpl travelProxy = (TravelImpl) Enhancer.create(TravelImpl.class, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("去的车票购买");
Object methodReturn = method.invoke(travelImpl, objects);
System.out.println("回的车票购买");
return methodReturn;
}
});
// 执行代理对象
travelProxy.vacation();
}
2 AOP
底层核心原理是动态代理
AOP(Aspect Oriented Programming)面向切面编程(使用动态代理技术,在不修改源码的基础上,给程序添加额外的功能)
2.1 注解开发
注解 | 说明 |
@Aspect | 标识类为"切面类" |
@Pointcut("execution(表达式)") | 切入点表达式 例: public * xyz.aboluo.service.impl.*.*(..) |
@Before | 前置通知 |
@AfterReturning | 后置通知 |
@AfterThrowing | 异常通知 |
@After | 最终通知(总会执行) |
@Around | 环绕通知 |
(1) 引入依赖
(2) 切面类
@Component
@Aspect //标识该类为一个"切面类"
@Order(1) //针对"多切面",数字越小"越先执行,越后结束"
public class LogAdvice {
@Pointcut("execution(public * xyz.aboluo.controller.*.*(..))")
private void pc1() {
}
@Pointcut("execution(public * xyz.aboluo.service.impl.*.*(..))")
private void pc2() {
}
@Pointcut("pc1() || pc2()")
private void pc3() {
}
//前置通知
@Before("pc2()")
public void beforeAdvice() {
System.out.println("前置通知,方法前执行");
}
//后置通知
@AfterReturning("pc2()")
public void afterReturningAdvice() {
System.out.println("后置通知,方法后执行");
}
//异常通知
@AfterThrowing("pc2()")
public void afterThrowingAdvice() {
System.out.println("异常通知,异常时执行");
}
//最终通知
@After("pc2()")
public void afterAdvice() {
System.out.println("最终通知,始终执行");
}
//环绕通知
@Around("pc2()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object methodReturn = null;
try {
System.out.println("前置通知");
Object target = pjp.getTarget(); //获取被代理对象
String methodName = pjp.getSignature().getName(); //获取当前方法名
Object[] args = pjp.getArgs(); //获取当前方法执行所需的参数
methodReturn = pjp.proceed(args); //原方法执行
System.out.println("后置通知");
} catch (Throwable throwable) {
System.out.println("异常通知");
} finally {
System.out.println("最终通知");
}
return methodReturn;
}
}
(3) 切面执行顺序
单切面: @Around --> @Before --> 原始方法 -->@After --> @AfterReturning --> @Throwing
多切面: 使用@Order(1)注解,数字越小"越先执行,越后结束"