spring - AOP
-
一、代理模式
基本原则
1. 代理类与委托类具有相似的行为(共同)
2. 代理类增强委托类的行为
静态代理
代理角色固定
代理类和委托类实现相同的接口
父接口
代理类
委托类
单元测试
动态代理
通过反射机制在程序运行期动态的为目标对象创建代理对象
JDK实现
实现InvocationHandler接口,重写invoke()
获取目标类
程序运行期间动态的创建代理角色
获取代理对象
1.类加载器
2.目标类实现的接口class
3.当前类
package com.lwx.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/30 21:50 .
*/
public class JdkHandler implements InvocationHandler {
// 目标类
private Object you;
public JdkHandler(Object you) {
this.you = you;
}
/**
* 程序运行期间动态创建代理角色
* @return
*/
public Object getProxy() {
/**
* 获取代理对象
* 1.类加载器
* 2.目标类实现的接口class
* 3.当前类
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(),you.getClass().getInterfaces(),this);
}
public void before() {
System.out.println("正在紧张布置婚礼现场。。。");
System.out.println("放炮。。。开始~");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
// 执行真正的target方法
Object invoke = method.invoke(you, args);
after();
return invoke;
}
public void after() {
System.out.println("婚礼结束。。。开启人生第二阶段");
}
}
package com.lwx.jdk;
import com.lwx.You;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/30 21:59 .
*/
public class JdkHandlerTest {
@Test
public void invoke() {
// 委托类
You you = new You();
// 创建代理类
JdkHandler jdkHandler = new JdkHandler(you);
// 代理对象
Object proxy = jdkHandler.getProxy();
// 执行方法
try {
jdkHandler.invoke(proxy, You.class.getMethod("toMarry"), null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
cglib实现
code generator library ,操作字节码。与jdk 提供的代理区别,Proxy:委托类必须有接口,制作过程比较快,执行慢;cglib:委托类可以没有接口,继承的思维来实现相似性,制作代理过程比较慢,执行快。主要解决没有接口类的代理实现。
实现MethodInterceptor接口,重写intercept()
获取目标类
程序运行期间动态的创建代理角色
获取代理对象
1.创建Enhancer对象
2.设置父类
3.设置回调
package com.lwx.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/30 23:39 .
*/
public class CglibHandler implements MethodInterceptor {
private Object you;
public CglibHandler(Object you) {
this.you = you;
}
/**
* 运行期间创建代理对象
*/
public Object getProxy() {
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(you.getClass());
// 设置回调
enhancer.setCallback(this);
return enhancer.create();
}
public void before() {
System.out.println("正在紧张布置婚礼现场。。。");
System.out.println("放炮。。。开始~");
}
public void after() {
System.out.println("婚礼结束。。。开启人生第二阶段");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = method.invoke(you, objects);
after();
return obj;
}
}
package com.lwx.cglib;
import com.lwx.You;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/30 23:50 .
*/
public class CglibHandlerTest {
@Test
public void intercept() {
// 委托类
You you = new You();
// 代理类
CglibHandler cglibHandler = new CglibHandler(you);
// 代理对象
You proxy = (You) cglibHandler.getProxy();
proxy.toMarry();
}
}
两者区别
JDK:Proxy:委托类必须有接口,制作过程比较快,执行慢
原理是实现接口
cglib:委托类可以没有接口,继承的思维来实现相似性,制作代理过程比较慢,执行快
原理是继承
-
二、日志处理带来的问题
-
三、理解AOP
what
Aspect Oriented Programing 面向切面编程,相比较oop 面向对象编程来说,Aop 关注的不再是程序代码中某个类,某些方法,而aop 考虑的更多的是一种面到面的切入即层与层之间的一种切入,所以称之为切面。
can
用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用
advantage
1. 降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)
2. 提高了代码的复用性。
3. 提高系统的扩展性。
-
四、AOP基本概念
1.JoinPoint(连接点)
被拦截到的每个点,spring 中指被拦截到的每一个方法
2.PointCut(切入点)
对连接点进行拦截的定义
3.Advice(通知)
拦截到每一个连接点即(每一个方法)后所要做的操作
i. 前置通知(前置增强) --before() 执行方法前通知
ii.返回通知(返回增强)--afterReturn 方法正常结束返回后的通知
iii.异常抛出通知(异常抛出增强)--afetrThrow()
iv.最终通知—after 无论方法是否发生异常,均会执行该通知。
v.环绕通知—around 包围一个连接点(join point)的通知,如方法调用。
在环绕通知里面,必须执行目标方法
注意: 虽然环绕通知包含了另外三种,但还是要依据业务逻辑来选择,这样有利于代码的编程量 |
4.Aspect(切面)
切入点与通知的结合
5.Target(目标对象)
6.Weave(织入)
将切面应用到目标对象并生成代理对象的这个过程即为织入。
7.Introduction(引入)
在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入
-
五、使用AOP解决日志处理问题
注解方式
1.Jar包坐标引入
2.xml文件配置
添加命名空间aop
配置aop代理 <aop:aspectj-autoproxy/>
3.业务方法
aop实现类
切入点
aop实现类
package com.lwx.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/31 21:55 .
*
* 声明切面组件
*/
@Component
@Aspect
public class LogCut {
/**
* 连接点
* 切入点
* 通知
*/
@Pointcut("execution(* com.lwx.service..*.*(..))")
public void cut() {
}
@Before(value = "cut()")
public void before() {
System.out.println("before...");
}
@AfterReturning(value = "cut()")
public void afterRrturning() {
System.out.println("afterReturning ...");
}
@After(value = "cut()")
public void after() {
System.out.println("after ...");
}
@AfterThrowing(value = "cut()",throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing ..." + e);
}
/**
* 声明环绕通知并将通知应用到切入点上
* 方法执行前后通过环绕通知定义相应处理
*/
@Around(value = "cut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before ...");
System.out.println("around afterReturning ...");
System.out.println(pjp.getTarget() + "---" + pjp.getSignature());
Object result = pjp.proceed();
System.out.println("around after ...");
return result;
}
}
切入点
package com.lwx.service;
import org.springframework.stereotype.Service;
/**
* By:刘文旭 ~ https://blog.csdn.net/qq_40414738
* Date:2018/10/31 22:20 .
*/
@Service
public class UserService {
public void test() {
// int i = 1/0;
System.out.println("测试 LogCut...");
}
}
getSignature():方法签名
getArgs():方法的实参
getTarget(): 目标对象
xml配置方式
Aop 匹配方法规则表达式语言
切入点表达式
执行任意公共方法 | execution(public *(..)) |
---|---|
执行任意的set 方法 | execution(* set*(..)) |
执行com.lwx.service 包下任意类的任意方法 | execution(* com.lwx.service.*.*(..)) |
执行所有类的所有方法 | execution(* *.*(..)) |
执行com.lwx.service 包以及子包下任意类的任意方法 | execution(* com.lwx.service..*.*(..)) |
-
六、AOP面试常见问题