借鉴:慕课网探秘aop
aop的产生和作用:
aop面向切面是oop面向对象的补充,主要目的是解决面向对象在处理非业务性的功能需求时代码重复过高,复用率低的问题
面向对象解决代码重复率时 我们一般使用水平分离 即 MVC模式 MVP模式等
垂直分离 即我们将一个大的系统分成一个个小的系统 模块分离
现在使用AOP对面向对象进行补充,使的代码的重复率更低 进行 切面分离 即非功能行业务和功能行业务
水平分离
切面分离 : 例如权限管理:每次我们的人员进行增删改操作都要判断权限,我们的一般操作都是在这之前加上判断权限的代码,这个权限他就是非功能性业务要求,现在我们把它提取出来他就是一个切面。
AOP的优缺点和作用场景:
优点:集中管理和开发某一个关注点即(切面)
代码的侵入性少,易于伸展和维护
使得代码的复用率更高,是开发人员更关注于业务逻辑的开发
缺点:增加了一定的理解难度
作用场景: 非业务功能(事物控制,日志管理,异常的管理,安全管理,权限管理等)
AOP的2个核心点:切面和织入
AOP织入实例(注解方式):
先定一个我们需要织入的注解:
package com.imooc.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AdminOnly {
}
在织入的地方使用advice(5种即在当前方法之前,之后,返回时等)注解
@Aspect
@Component
public class ExecutionAspectConfig {
@Pointcut("@annotation(@AdminOnly)")
public void matchCondition(){}
//这是在使用AdminOnly这个注解的方法之前注入逻辑
@Before("matchCondition()")
public void before(){
System.out.println("");
System.out.println("###before");
}
}
在实际的调用中:
@Component
public class LogService implements Loggable{
@Override
@AdminOnly
public void log() {
System.out.println("log from LogService");
}
public void annoArg(Product product){
System.out.println("execute log service annoArg");
}
}
这个例子就是在调用LogService类的log方法之前可以进行权限等的控制
织入的底层原理:动态代理 包含: jdk动态代理和cglib动态代理
代理模式:
代理分为静态代理和动态代理
静态代理例子:
public interface UserInterface
{
public void put();
}
public class UserImple implements UserInterface
{
@Override
public void put(){
System.out.println("suer put");
}
}
//代理类:
public class Proxy1
{
UserImple u = null;
public Proxy1(UserImple u)
{
this.u = u;
}
public void put(){
System.out.println("代理类 put");
u.put();
}
}
//测试类:
public class Test()
{
}
JDK动态代理(接口的方式):
先写一个接口 再 目的类实现它
public interface Subject {
void doSomething();
}
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject do something");
}
}
再定义一个类实现 InvocationHandler 实现其invoke方法
public class JDKDynamicProxy implements InvocationHandler {
private Object target;
public JDKDynamicProxy(Object target) {
this.target = target;
}
/**
* 获取被代理接口实例对象
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Do something before");
Object result = method.invoke(target, args);
System.out.println("Do something after");
return result;
}
}
在类中的 getProxy方法调用了 newProxyInstance方法 (生成一个代理类(实现了Subject接口)的对象)
这样在测试类中就可以使用这个方法代理目的类进行操作
cglib代理(继承的方式):
目的类:
public class PersonService {
public PersonService() {
System.out.println("PersonService构造");
}
//该方法不能被子类覆盖
final public Person getPerson(String code) {
System.out.println("PersonService:getPerson>>"+code);
return null;
}
public void setPerson() {
System.out.println("PersonService:setPerson");
}
}
写一个 CglibProxyIntercepter 实现 MethodInterceptor 接口
public class CglibProxyIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前...");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("执行后...");
return object;
}
}
再测试类中 使用 Enhancer来创建代理类对象
public static void main(String[] args) {
//代理类class文件存入本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback(new CglibProxyIntercepter());
PersonService proxy= (PersonService) enhancer.create();
proxy.setPerson();
proxy.getPerson("1");
} }
这样就实现了cglib代理
静态代理和动态代理的对比:
静态代理结构逻辑简单 就是嵌套调用 将目的类嵌套在代理类中 测试类调用代理类 代理类调用目的类
但是目的类有几个方法,代理类旧的有几个对应的方法,代码的复用不高
动态代理就是将代理类在运行时才通过发射动态生成其字节码,不用写代理类的java文件,实现了代码的复用
简单来说就是 静态代理类是手动写,提前编译好字节码文件,动态代理是运行时更具反射自动生成字节码文件
cglib代理和jdk代理的对比:
cglib代理是继承的方式 ,jdk代理是接口实现的方式
cglib代理不能实现final , static ,private 等关键字和方法类型修饰的方法,而jdk代理全是public 方法 也不能实现其自己的私有,final等方法
Spring的 AOP 的底层实现
通过
判断目的类是否实现接口,是否强制使用cglib动态代理,判断内存中是否已有jdk动态代理的对象来决定是使用cglib代理还是jdk代理