title: 重拾Spring5
tag: spring
top: 80
Spring概念
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,能够让编码变得更加简单,核心组件IOC容器和AOP面向切面编程。
IOC控制反转:把整个创建对象的工程,交给我们的SpringIOC容器来管理,底层实现原理:反射+工厂模式实现。
AOP面向切面编程:对我们功能(方法)实现前后增强,比如打印日志,事务管理,权限管理,底层是基于动态代理模式实现的。减少代码的冗余性。
Spring优势
方法的解耦,简化开发
Aop技术的支持
提供声明式事务
Junit单元测试
方便整合其他框架(Mybatis,SpringMVC,SpringBoot,Redis)等
Spring 与 SpringBoot的关系
SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。
总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。
比如:
1 2 3 4 5 6 7
| 1.@RestController 2.@ComponentScan("com.hluck.me") 3.@Configuration 4.@Component 5.@Scheduled 6.@Value 7.@Bean
|
SpringIOC底层容器原理
1.Spring模块介绍
1.Test
对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.
2. Spring Core
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
3.Spring-Beans
这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
4.Spring Contetxt
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
5.Spring-Expression
模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,它也支持列表选择和投影以及常见的列表聚合。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
6.Spring AOP
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
7.JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
8.Spring-Transaction
以前是在这里org.springframework.transaction
为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
9.Spring ORM
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
10.Spring Web MVC
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.1.RELEASE</version> </dependency>
|
获取bean对象
1 2 3 4
| ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserEntity userEntity = classPathXmlApplicationContext.getBean("userEntity", UserEntity.class);
|
2.SpringIOC
IOC容器非常核心的接口 BeanFactory(bean对象工厂)
IOC容器基本概念:控制反转,把创建对象的过程与使用统一交给Spring来进行管理,不需要开发者自己new对象。
IOC底层实现技术:反射技术 + 解析xml + 工厂模式
IOC作用:降低代码的耦合度。
3.IOC的核心接口
IOC的核心思想底层基于反射+工厂模式实现
Spring提供IOC容器实现两种方式:
BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。
ApplicationContext BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。
当我们在加载配置文件的过程中,就会将配置文件中的对象创建。
在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。
ApplicationContext主要实现类
ClassPathXmlApplicationContext:对应类路径下的xml格式的配置文件
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
ConfigurableApplicationContext 是ApplicationContext的子接口,包含一些扩展方法
refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext需要new此接口的对象调用close()方法。
WebApplicationContext 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
4.SpringBean的注入方式
什么是Bean管理
- 使用Spring创建对象
- 使用Spring注入属性
Bean的管理有两种方式
基于XML方式配置
1
| <bean id="userEntity" class="com.it.entity.UserEntity"></bean>
|
Id:获取bean对象 唯一bean对象的名称; bean的名称不允许重复
Class属性: 类的完整路径地址(类名称+包名称)
默认底层使用反射技术执行无参数构造函数
基于xml方式注入属性
DI 依赖注入: 对象的属性注入值;
基于对象属性set方法实现
1 2 3 4
| <bean id="bookEntity" class="com.mayikt.entity.BookEntity"> <property name="bookName" value="Java"></property> <property name="bookPrice" value="108.00"></property> </bean>
|
2.有参构造注入
1 2 3 4 5 6 7 8
| <bean id="orderEntity" class="com.mayikt.entity.OrderEntity">
<constructor-arg index="0" value="123456"></constructor-arg> <constructor-arg index="1" value="mayikt"></constructor-arg> </bean>
|
*<constructor-arg name* 指定参数列表名称
*<constructor-arg index 指定参数列表索引*
Spring工厂Bean
1.SpringBean的作用域
1
| <bean id="userEntity" class="com.it.entity.UserEntity" scope="singleton"></bean> --单例(默认)
|
1
| <bean id="userEntity" class="com.it.entity.UserEntity" scope="prototype"></bean> --多例
|
在spring默认的情况下,bean的作用域就是为单例 节约服务器内存。
2.SpringBean的生命周期
简单分为:实例化 –> 属性赋值 –> 初始化 –> 销毁
生命周期的概念:
- 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
- 为bean的属性设置(使用反射调用set方法)
- 调用bean的初始化方法(需要单独在类中配置初始化的方法)
- 正常使用bean对象
- Spring容器关闭,调用该类的销毁回调方法(需要单独在类中配置销毁的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class MemberEntity { private String name; public MemberEntity(){ System.out.println("[第一步]-无参构造函数被执行---反射机制调用"); }
public void setName(String name) { System.out.println("[第二步]-set方法初始化属性---反射机制调用"); this.name = name; }
public void initMethod(){ System.out.println("[第三步]-回调调用init初始化方法"); }
public void destroyMethod(){ System.out.println("[第五步]-回调调用destroyMethod方法"); } }
|
1 2 3 4
| <bean id="memberEntity" class="com.it.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod"> <property name="name" value="mayikt"></property> </bean> <bean id="mayiktBeanPost" class="com.it.bean.MayiktBeanPost"></bean>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import com.mayikt.entity.MemberEntity; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test07 { public static void main(String[] args) { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_07.xml"); MemberEntity memberEntity= app.getBean("memberEntity",MemberEntity.class); System.out.println("[第四步]-获取使用到的memberEntity"); System.out.println(memberEntity); app.close(); } }
|
3.Bean的后置处理器 作用提供更多的扩展功能 BeanPostProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor;
public class MayiktBeanPost implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean 初始化方法之前执行"); return bean; }
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean 初始化方法之后执行"); return bean; } }
|
1
| <bean id="mayiktBeanPost" class="com.mayikt.bean.MayiktBeanPost"></bean>
|
- 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
- 为bean的属性设置 (使用反射调用set方法)
- 将bean传递给后置处理器 调用初始化方法之前执行
- 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
- 将bean传递给后置处理器 调用初始化方法之后执行
- 正常使用bean对象
- Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)
4.配置多个 BeanPostProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered;
public class MayiktBeanPost implements BeanPostProcessor, Ordered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用该bean的 init方法之前"); return bean; }
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用该bean的 init方法之后"); return bean; }
public int getOrder() { return 1; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered;
public class MayiktBeanPost02 implements BeanPostProcessor, Ordered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("[MayiktBeanPost02:]调用该bean的 init方法之前"); return bean; }
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("[MayiktBeanPost02:]调用该bean的 init方法之后"); return bean; }
public int getOrder() { return 0; } }
|
1 2 3
| <bean id="mayiktBeanPost" class="com.mayikt.bean.MayiktBeanPost"></bean> <bean id="mayiktBeanPost02" class="com.mayikt.bean.MayiktBeanPost02"></bean>
|
实现Ordered接口 getOrder 值越小越优先加载.
5.混合配置
混合配置就是Java配置+ XML配置。混用的话,可以在Java配置中引入xml配置。
1 2 3
| @Configuration @ImportResource("classpath:applicationContext.xml") public class JavaConfig{}
|
在Java配置中,通过@ImportResource注解可以导入一个XML配置。
使用注解注入对象
@Autowired
@Resources
@Injected
@Autowired 是根据类型去查找,然后赋值,这就要有一个要求,这个类型只可以有一个对象,否则就会报错。
@Resources 是根据名称去查找,默认情况下,定义的变量名,就是查找的名称,当然也可以在@Resources中手动指定。
所以,如果一个类存在多个实例,那么就应该使用@Resources去注入;如果使用@Autowired,也是可以的,此时需要配合另外一个注解,@Qualifier,在@Qualifier中可以指定变量名。
条件注解
条件注解就是在满足某一个条件的情况下,生效的配置。
1.条件注解
首先在Windows中如何获取操作系统信息?Windows中查看文件夹目录的命令是dir,Linux中查看文件夹目录的命令是ls,我现在希望当系统运行在Windows上时,自动打印出Windows上的目录展示命令,linux运行时,则自动展示Linux上的目录展示命令。
首先定义一个显示文件夹目录的接口:
1 2 3
| public interface ShowCmd { String showCmd(); }
|
然后,分别实现Windows下的实例和 Linux下的实例:
1 2 3 4 5 6
| public class WinShowCmd implements ShowCmd{ @Override public String showCmd() { return "dir"; } }
|
1 2 3 4 5 6
| public class LinuxShowCmd implements ShowCmd{ @Override public String showCmd() { return "ls"; } }
|
接下来,定义两个条件,一个是Windows下的条件,另一个是Linux下的条件:
1 2 3 4 5 6
| public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("windows"); } }
|
1 2 3 4 5 6 7 8
| public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("linux"); } }
|
接下来,在定义Bean的时候,就可以去配置条件注解了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Configuration public class JavaConfig {
@Bean("showCmd") @Conditional(WindowsCondition.class) ShowCmd winCmd(){ return new WinShowCmd(); }
@Bean("showCmd") @Conditional(LinuxCondition.class) ShowCmd linuxCmd(){ return new WinShowCmd(); } }
|
这里,一定要给两个Bean取一样的名字,这样在调用时,才可以自动匹配。
然后,给每一个Bean加上条件注解,当条件中的matches 方法返回true的时候,这个Bean的定义就会生效。
1 2 3 4 5 6 7
| public class JavaMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class); ShowCmd showCmd = (ShowCmd) context.getBean("showCmd"); System.out.println(showCmd); } }
|
条件注解有一个非常典型的使用场景,就是多环境切换。
2.多环境切换
开发中,如何在 开发/生产/测试 环境之间进行快速切换?Spring提供了Profile来解决这个问题,Profile底层就是条件注解。从这个@Profile注解的定义就可以看出来:
1 2 3 4 5 6 7
| @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional({ProfileCondition.class}) public @interface Profile { String[] value(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class ProfileCondition implements Condition { ProfileCondition() { }
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { Iterator var4 = ((List)attrs.get("value")).iterator();
Object value; do { if (!var4.hasNext()) { return false; }
value = var4.next(); } while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true; } else { return true; } } }
|
我们定义一个DataSource:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class DataSource { private String url; private String username; private String password;
@Override public String toString() { return "DataSource{" + "url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; } }
|
然后,在配置Bean时,通过@Profile注解指定不同的环境:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Bean("ds") @Profile("dev") DataSource devDataSource(){ DataSource dataSource = new DataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dev"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; }
@Bean("ds") @Profile("prod") DataSource prodDataSource(){ DataSource dataSource = new DataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dev"); dataSource.setUsername("root11"); dataSource.setPassword("root111"); return dataSource; }
|
最后,再加载配置类,注意,需要先设置当前环境,然后再去加载配置类:
1 2 3 4 5 6 7 8 9 10
| public class JavaMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class); context.getEnvironment().setActiveProfiles("dev"); context.register(JavaConfig.class); context.refresh(); DataSource ds = (DataSource) context.getBean("ds"); System.out.println(ds); } }
|
也可以在XML文件配置,如下配置在XML文件中,必须放在其他节点后面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <beans profile="dev"> <bean class="com.ljh.redis01.entity.DataSource" id="dataSource"> <property name="url" value="jdbc:mysql://127.0.0.1:3306/dev" /> <property name="password" value="root" /> <property name="username" value="root" /> </bean> </beans>
<beans profile="prod"> <bean class="com.ljh.redis01.entity.DataSource" id="dataSource"> <property name="url" value="jdbc:mysql://111.111.111.111/dev" /> <property name="password" value="root111" /> <property name="username" value="root111" /> </bean> </beans>
|
启动类中设置当前环境并加载配置:
1 2 3 4 5 6 7 8 9 10
| public class JavaMain { public static void main(String[] args) { ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext(); context1.getEnvironment().setActiveProfiles("prod"); context1.setConfigLocation("applicationContext.xml"); context1.refresh(); DataSource ds = (DataSource) context1.getBean("dataSource"); System.out.println(ds); } }
|
3.Aware接口
Aware接口,从字面意思上理解就是感知捕获。单纯的一个Bean是没有知觉的。
之所以UserDao可以注入到UserService中,有一个前提,就是这两个Bean都是被Spring容器管理的。如果直接new一个UserService,这是没有用的,因为UserService没有被Spring容器管理,所以也不会给它注入Bean。
在实际开发中,我们可能会遇到一些类,需要获取到容器的相关信息,那就可以通过Aware接口来实现。
Aware是一个空接口,有很多实现类:
这些实现的接口,有一些共性:
- 都是以Aware结尾
- 都继承自Aware
- 接口均定义了一个set方法
每一个子接口均提供了一个set方法,方法的参数就是当前Bean需要感知的内容,因此我们需要在Bean中声明相关的成员变量来接收这个参数。接收到这个参数后,就可以通过这个参数获取到容器的详细信息了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component public class AppUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }
public static Object getObject(String id){ boolean userDao = applicationContext.containsBean("userDao"); System.out.println(userDao); return applicationContext.getBean(id); } }
|
- 在spring的配置文件中,注册方法类AppUtil,这里我们使用
@Component
来进行注册。之所以方法类AppUtil能够灵活自如地获取ApplicationContext,就是因为spring能够为我们自动地执行了setApplicationContext。
- 加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware 接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware 接口中的setApplicationContext方法。获得ApplicationContext对象,ApplicationContext对象是由spring注入的。
- 前提必须在Spring配置文件中指定该类。
AOP
Aop(Aspect Oriented Programming),面向切面编程,这是对面向对象思想的一个补充。
面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能。
概念 |
说明 |
切点 |
要添加代码的地方,称作切点 |
通知(增强) |
就是向切点动态添加的代码 |
切面 |
切点 + 通知 |
连接点 |
切点的定义 |
1. AOP的实现
AOP基于Java的动态代理实现:
- cglib
- jdk
2. 动态代理
1.基于JKD的动态代理
定义一个计算器接口:
1 2 3
| public interface MyCalculator { int add(int a,int b); }
|
定义计算器接口的实现:
1 2 3 4 5 6
| public class MyCalculatorImpl implements MyCalculator{ @Override public int add(int a, int b) { return a + b; } }
|
定义代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CalculatorProxy { public static Object getInstance(final MyCalculator myCalculator){ return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法开始执行啦..."); Object invoke = method.invoke(myCalculator, args); System.out.println(method.getName() + "方法执行结束啦..."); return invoke; } }); } }
|
Proxy.newProxyInstance方法接收三个参数,第一个是一个classloader,第二个是代理多项实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在invoke方法中实现。