Spring AOP的实现
Aspectj增强
aop类
@Aspect
@Component
public class AopSerivce {
@Before("execution(* com.example.springtest.UserService.login())")
public void befor(){
System.out.println("登入日志");
}
}
业务类
@Service
@Component
public class UserService {
public void login(){
System.out.println("登入成功");
}
}
Springboot启动类
@SpringBootApplication
public class SpringtestApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringtestApplication.class, args);
UserService bean = context.getBean(UserService.class);
bean.login();
context.close();
}
}
控制台输出
2022-04-11 12:08:15.799 INFO 2468 --- [ main] c.e.springtest.SpringtestApplication : Starting SpringtestApplication using Java 1.8.0_65 on DESKTOP-K588PHT with PID 2468 (D:\JavaDemo\springtest\target\classes started by Jack Yang in D:\JavaDemo\springtest)
2022-04-11 12:08:15.801 INFO 2468 --- [ main] c.e.springtest.SpringtestApplication : No active profile set, falling back to 1 default profile: "default"
2022-04-11 12:08:16.256 INFO 2468 --- [ main] c.e.springtest.SpringtestApplication : Started SpringtestApplication in 0.717 seconds (JVM running for 1.686)
登入日志
登入成功
我们发现完成了aop实现
AJC增强是改写了class实现的增强,因为它改写了class文件所有没有进行代理,因为改写了class所有也不依靠spring容器
使用这种Aspectj增强的好处就是可以突破代理的限制,比如方法是一个静态方法,用代理就不能增强,而Aspectj就可以增强
Aspectj就是把编译好的类进行一个增强
注意: 如果idea调用了java的编译器,那么就不会实现aop功能,必须调用aspectj的编译器才可以(在pom文件的build引用aspectj编译器)。
Agent增强
agent是在类加载过程的时候实现增强,
运行的时候需要在VM options里引入 -javaagent:${maven仓库地址}/org/aspectj/aspectjweaver/1.9.7/aspectjwaver-1.9.7.jar
proxy增强
jdk动态代理(java自带的代理功能)
jdk动态代理有一个限制,只能针对接口代理
基于JDK实现的代理可以允许目标为final的类型
jdk代理与目标类的关系是平级关系
jdk一个方法对应一个代理
public class Jdkproxy {
interface Foo{
void foo();
}
static class Target implements Foo{
@Override
public void foo() {
System.out.println("巴啦啦能量");
}
}
public static void main(String[] args) {
Target target = new Target();
ClassLoader loder = Jdkproxy.class.getClassLoader(); //用来在加载过程期间动态生成的字节码
/**
* loder 加载过程中动态生命的字节码
* new Class[]{Foo.class} 代理类要实现的哪些接口
* new InvocationHandler().... 执行代理类要执行的行为
*/
Foo before = (Foo) Proxy.newProxyInstance(loder, new Class[]{Foo.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Target invoke = (Target) method.invoke(target, args);
System.out.println("after");
return invoke; //让代理也返回目标方法执行的结果
}
});
before.foo();
}
}
jdk方法反射的优化
jdk调用16次,第17次的时候他会针对一个方法会产生一个代理类,这个代理类能让反射变成无需反射,
cglib(第三方的代理技术)
cglib不像jdk代理一样有限制,它可以针对针对接口,如果目标没有实现任何接口也可以
cglib与类的关系是子父关系,代理是子类型,目标是父类型,不能根据final生成,只要是子父级关系肯定是通过重写来实现的代码增强
cglib一个代理类会对应2个FastClass,一个fastclass配合目标一起调用,另一个fastclass配合代理本身一起调用的,一个fastclas对应多个方法
public class Jdkproxy {
static class Target {
public void foo() {
System.out.println("巴啦啦能量");
}
}
public static void main(String[] a) {
Target target = new Target();
Target o1 = (Target) Enhancer.create(Target.class, (MethodInterceptor) (o, method, args, methodProxy) -> {
System.out.println("brfore");
Target invoke = (Target) method.invoke(target, args); //用方法反射调用目标
//methodProxy避免反射调用方法
methodProxy.invoke(target, args); //需要目标,内部类没有反射
methodProxy.invokeSuper(target,args);//内部类没有反射,需要代理
System.out.println("after");
return invoke;
});
o1.foo();
}
}
Spring用的是 methodProxy.invoke(target, args);
我们代理类去调用目标有3种方法,
1.用方法反射区调用目标
2.invoke是配合目标使用的(Spring是用的这个)
3.invokeSuper是配合代理使用的
cglib的反射优化
第一次就产生代理,就无需反射
Spring选择代理
Spring选择代理技术的规则
情况A:proxyTargerClass = false ,目标实现了接口,用jdk实现
情况B:proxyTargerClass = false , 目标没有实现接口,用cglib实现(因为jdk必须要目标有实现接口)
请假C:proxyTargerClass = true , 总是使用cglib实现