笔记参考B站黑马程序员视频:spring原理
1.AOP实现方式ajc增强
先加入依赖,pom.xml文件如下:
重点是build标签里的插件,aspectj-maven-plugin
AspectJ Maven插件是一个Maven插件,用于将AspectJ(一个强大的面向方面编程框架)集成到Maven项目中。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dreams</groupId>
<artifactId>aspectj_01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aspectj_01</name>
<description>aspectj_01</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>8</source>
<target>8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
切面类
package com.dreams.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect // 注意此切面并未被 Spring 管理
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
@Before("execution(* com.dreams.service.MyService.send())")
public void before() {
log.debug("before()");
}
}方法类
package com.dreams.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
public static void send() {
log.debug("send方法执行中!");
}
}启动类
package com.dreams;
import com.dreams.service.MyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class AOPApplication {
private static final Logger log = LoggerFactory.getLogger(AOPApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AOPApplication.class, args);
MyService service = context.getBean(MyService.class);
log.debug("service class: {}", service.getClass());
service.send();
}
}maven插件,所以需要点击maven的编译

可以看到虽然是AOP代理,但是却并不是代理对象,也就是aspectj-maven-plugin并不需要代理

原理是在构建过程中将 AspectJ 切面织入到Java 代码中。
打开编译的代码class文件,可以看到多了一个前置的代码

优点是即使是静态方法,也能被代理,而传统的代理是不能代理静态方法的
2.AOP实现方式agent增强
pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dreams</groupId>
<artifactId>aspectj_02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aspectj_02</name>
<description>aspectj_02</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>切面类不变
方法类
package com.dreams.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
final public void foo() {
log.debug("foo()");
this.bar();
}
public void bar() {
log.debug("bar()");
}
}启动类没什么变化
package com.dreams;
import com.dreams.service.MyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class AOPApplication {
private static final Logger log = LoggerFactory.getLogger(AOPApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AOPApplication.class, args);
MyService service = context.getBean(MyService.class);
// MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
log.debug("service class: {}", service.getClass());
service.foo();
}
}要使用agent增强
运行时需要在 VM options 里加入 -javaagent:D:\software\apache-maven-3.6.3-bin\maven-repository\org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 D:\software\apache-maven-3.6.3-bin\maven-repository 改为自己 maven 仓库起始地址
运行成功

3.AOP实现方式JDK代理
使用方式:
package com.dreams.demo11;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface Foo {
void foo();
}
static final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
// jdk 只能针对接口代理
public static void main(String[] param) throws IOException {
// 目标对象
Target target = new Target();
ClassLoader loader = JdkProxyDemo.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
System.out.println("before...");
// 目标.方法(参数)
// 方法.invoke(目标, 参数);
Object result = method.invoke(target, args);
System.out.println("after....");
return result; // 让代理也返回目标方法执行的结果
});
System.out.println(proxy.getClass());
proxy.foo();
}
}运行一下

要求必须有一个接口,生成代理类与目标类是兄弟关系,所以如上代码可以是final的类
原理
仿照JDK代理实现
package com.dreams.demo12;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class $Proxy0 implements Demo.Foo {
private Demo.InvocationHandler h;
public $Proxy0(Demo.InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
Object result = h.invoke(this, bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
static Method foo;
static Method bar;
static {
try {
foo = Demo.Foo.class.getMethod("foo");
bar = Demo.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}目标类
package com.dreams.demo12;
import java.lang.reflect.Method;
public class Demo {
interface Foo {
void foo();
int bar();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public int bar() {
System.out.println("target bar");
return 100;
}
}
interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public static void main(String[] param) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("before...");
return method.invoke(new Target(), args);
}
});
proxy.foo();
proxy.bar();
}
}
jdk反射有优化
package com.dreams.a12;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
// 运行时请添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
foo.invoke(null, i);
}
System.in.read();
}
// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}
public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}可以看到16次时更换了实现方式,由反射调用变为直接调用

4.AOP实现方式CGLIB代理
使用方法
package com.dreams.demo11;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
// 代理是子类型, 目标是父类型
public static void main(String[] param) {
Target target = new Target();
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
Object result = method.invoke(target, args); // 用方法反射调用目标
System.out.println("after...");
return result;
});
System.out.println(proxy.getClass());
proxy.foo();
}
}
目标类与生成代理类是父子关系,所以如上代码目标类不能是final
methodProxy.invoke(target, args)内部没有用反射,需要目标
package com.dreams.demo11;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
// 代理是子类型, 目标是父类型
public static void main(String[] param) {
Target target = new Target();
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
// methodProxy 它可以避免反射调用
Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
System.out.println("after...");
return result;
});
System.out.println(proxy.getClass());
proxy.foo();
}
}
使用methodProxy内部没有用反射, 需要代理
package com.dreams.demo11;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
// 代理是子类型, 目标是父类型
public static void main(String[] param) {
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
// methodProxy 它可以避免反射调用
Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理
System.out.println("after...");
return result;
});
System.out.println(proxy.getClass());
proxy.foo();
}
}
原理
同样仿造源码的操作的示例
package com.dreams.demo13;
public class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long j) {
System.out.println("save(long)");
}
}Proxy类
package com.dreams.demo13;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class Proxy extends Target {
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}Demo主方法
package com.dreams.demo13;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
return method.invoke(target, args); // 反射调用
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
使用无反射方法
package com.dreams.demo13;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class Proxy extends Target {
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}Demo主方法
package com.dreams.demo13;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}上面还可以
return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用

MethodProxy提供了一种高效的方式来调用被代理对象的原始方法,避免了反射调用的开销,这使得CGLIB在某些场景下比JDK动态代理更具性能优势。
5.代理的局限性
spring 代理的局限性:
- 依赖注入和初始化影响的是原始对象
- 代理与目标是两个对象,二者成员变量并不共用数据
- static 方法、final 方法、private 方法均无法增强
Bean2类
package com.dreams.demo45;
import org.springframework.stereotype.Component;
@Component
public class Bean2 {
}Bean1类
这里有两个属性bean2和initialized,注意这里有注入和初始化方法
package com.dreams.demo45;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
protected Bean2 bean2;
protected boolean initialized;
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("setBean2(Bean2 bean2)");
this.bean2 = bean2;
}
@PostConstruct
public void init() {
log.debug("init");
initialized = true;
}
public Bean2 getBean2() {
log.debug("getBean2()");
return bean2;
}
public boolean isInitialized() {
log.debug("isInitialized()");
return initialized;
}
public void m1() {
System.out.println("m1() 成员方法");
}
final public void m2() {
System.out.println("m2() final 方法");
}
static public void m3() {
System.out.println("m3() static 方法");
}
private void m4() {
System.out.println("m4() private 方法");
}
}然后我们对所有方法增强
package com.dreams.demo45;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
// 故意对所有方法增强
@Before("execution(* com.dreams.demo45.Bean1.*(..))")
public void before() {
System.out.println("before");
}
}Demo主方法调用
package com.dreams.demo45;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Demo {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Demo.class, args);
Bean1 proxy = context.getBean(Bean1.class);
context.close();
}
}可以看到Bean1的注入和初始化方法没有被增强

如果是使用代理对象调用的话就能增强
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Demo.class, args);
Bean1 proxy = context.getBean(Bean1.class);
System.out.println(">>>>>>>>>>>>>>>>>>>");
System.out.println(proxy.getBean2());
System.out.println(proxy.isInitialized());
}也就是说依赖注入和初始化影响的是原始对象。

还有代理与目标是两个对象,二者成员变量并不共用数据
package com.dreams.demo45;
import org.springframework.aop.framework.Advised;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Demo {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Demo.class, args);
Bean1 proxy = context.getBean(Bean1.class);
showProxyAndTarget(proxy);
context.close();
}
public static void showProxyAndTarget(Bean1 proxy) throws Exception {
System.out.println(">>>>> 代理中的成员变量");
System.out.println("initialized=" + proxy.initialized);
System.out.println("bean2=" + proxy.bean2);
if (proxy instanceof Advised) {
Advised advised = (Advised)proxy;
System.out.println(">>>>> 目标中的成员变量");
Bean1 target = (Bean1) advised.getTargetSource().getTarget();
System.out.println("initialized=" + target.initialized);
System.out.println("bean2=" + target.bean2);
}
}
}可以看到代理对象是没有被注入的,还为null,也就是代理与目标是两个对象,二者成员变量并不共用数据。

而平时我们使用代理对象时实际并不会直接调用属性。如下:
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Demo.class, args);
Bean1 proxy = context.getBean(Bean1.class);
showProxyAndTarget(proxy);
System.out.println(">>>>>>>>>>>>>>>>>>>");
System.out.println(proxy.getBean2());
System.out.println(proxy.isInitialized());
context.close();
}也就是底层调用了原始方法的属性。

static 方法、final 方法、private 方法均无法增强
Bean1提供了4个方法

public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Demo.class, args);
Bean1 proxy = context.getBean(Bean1.class);
proxy.m1();
System.out.println("-------------");
proxy.m2();
System.out.println("-------------");
proxy.m3();
System.out.println("-------------");
Method m4 = Bean1.class.getDeclaredMethod("m4");
m4.setAccessible(true);
m4.invoke(proxy);
context.close();
}可以看到只有m1被成功代理

也就是不能被重写,所以代理对象没有该方法。
6.参考
黑马程序员:spring


