Spring高级篇
此文章仅为学习笔记,并在不断学习更新中,如有错误,欢迎指出
学习视频为黑马 spring高级49讲,如有侵权,联系我删除
学习传送门
day-01
1.BeanFactory与ApplicationContext
核心问题
-
1.到底什么是BeanFactory?
- 它是ApplicationContext的父接口
- 它才是spring的核心容器,主要的ApplicationContext
- 实现都是组合了BeanFactory的功能
- 换句话说,ApplicationContext中有beanFactory成员变量
-
2.BeanFactory 能干啥?
- 表面上只有getBean
- 实际上控制反转、基本的依赖注入 直至bean的生命周期的各种功能
- 都是由他的实现类提供
-
3.ApplicationContext 比 BeanFactory多出些什么?
- 国际化能力由MessageSource父接口提供
- EnvironmentCapable接口获取环境配置
- ApplicationEventPublisher接口
SpringDepth01Application
@SpringBootApplication
public class SpringDepth01Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringDepth01Application.class, args);
// ctx.getBean("aaa");
/* ApplicationContext的getBean方法实际上是组合了getBeanFactory
* public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
* */
System.out.println("ctx = " + ctx);
// System.out.println("下面是单例");
/*
* 1.到底什么是BeanFactory
* 它是ApplicationContext的父接口
* 它才是spring的核心容器,主要的ApplicationContext
* 实现都是组合了BeanFactory的功能
* 换句话说,ApplicationContext中有beanFactory成员变量
* */
/*
* 2.BeanFactory 能干啥
* 表面上只有getBean
* 实际上控制反转、基本的依赖注入 直至bean的生命周期的各种功能
* 都是由他的实现类提供
* */
// 通过反射拿到单例
try {
// 类加载拿到Field对象
// singletonObjects管理单例
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 因为是私有对象,反射需要设置Accessible为true
singletonObjects.setAccessible(true);
// beanFactory来调用私有成员变量,因此获取他的属性
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
// 由于容器管理的单例太多,查看自己定义的component1和2
// 对于map进行过滤,只想拿到component开头的
map.entrySet().stream().filter(e->e.getKey().startsWith("component"))
.forEach(e->{
// System.out.println(e.getKey() + "=" + e.getValue());
});
// 拿到bean的对象以及bean的实例
// component1=com.briup.Component1@5f0e9815
// component2=com.briup.Component2@76884e4b
} catch (Exception e) {
e.printStackTrace();
}
/*
* ApplicationContext 比 BeanFactory多出些什么
* */
// 国际化能力由MessageSource父接口提供
// ctx.getMessage();
// 根据通配符来获取多个资源
// 比如获取classpath(类路径)下的application.properties资源
// 假如是file表示从磁盘开始读入
try {
Resource[] resources = ctx.getResources("classpath:application.properties");
for (Resource resource : resources) {
// System.out.println("resource = " + resource);
// resource = class path resource [application.properties]
}
} catch (Exception e) {
e.printStackTrace();
}
// EnvironmentCapable接口获取环境配置
// System.out.println(ctx.getEnvironment().getProperty("java_home"));
// System.out.println(ctx.getEnvironment().getProperty("server.port"));
// C:\Program Files\Java\jdk1.8.0_301 jdk安装路径
// 8888 properties文件中的配置
// ApplicationEventPublisher接口
// 发布事件的方法 参数代表事件源
// 发事件完成 需要收事件 即监听器
// spring任何组件都可以作为监听器
ctx.publishEvent(new UserRegisteredEvent(ctx));
// 设计的监听器代码
// @EventListener
// public void aaa(UserRegisteredEvent event){
// log.debug("{}",event);
// }
//2022-10-12 00:02:26.137 INFO 12944 --- [ main] com.briup.Component2
// : com.briup.UserRegisteredEvent[source=org.springframework.context.annotation
// .AnnotationConfigApplicationContext@1165b38, started on Wed Oct 12 00:02:25 CST 2022]
}
Component1
@Component
public class Component1{
private static final Logger log = LoggerFactory.getLogger(Component1.class);
}
Component2
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
@EventListener
public void aaa(UserRegisteredEvent event){
log.info("{}",event);
}
}
UserRegisteredEvent
/**
* @author 火云勰神
* @date 2022-10-11 23:55
* @description 代表一个用户注册事件
*/
public class UserRegisteredEvent extends ApplicationEvent {
// source代表事件源
public UserRegisteredEvent(Object source) {
super(source);
}
}
day-02
BeanFactory的实现
认识DefaultListableBeanFactory,是beanFactory中比较重要的一个类
beanFactory添加bean的步骤:
- 创建一个DefaultListableBeanFactory对象
- 由BeanDefinitionBuilder中的genericBeanDefinition方法来定义一个bean
- 再通过beanFactory来注册一个bean
管理单个bean已经完成,但是如果这个bean中还有其他的bean,或者依赖注入等功能
需要后处理器来完成对beanFactory功能的拓展
使用后处理器的步骤
BeanFactory后处理器:
- 工具类AnnotationConfigUtils来生成beanFactory的后处理器,里面有五个
- beanFactory通过getBeansOfType方法可以拿到后处理器
- 调用后处理器,实现对功能的拓展
TestBeanFactory
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 火云勰神
* @date 2022-10-12 22:37
* @description
*/
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 添加bean的定义 再由beanFactory根据bean的定义来提供 控制反转
// (class,scope,初始化,销毁)
// 定义一个类型为Config,单例的bean
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
// 交给beanFactory管理一个命为config的bean
beanFactory.registerBeanDefinition("config",beanDefinition);
// 此时只存在一个名为config的bean
// for ( String name: beanFactory.getBeanDefinitionNames()) {
// System.out.println("name = " + name);
// }
// 给beanFactory添加一些常用的后处理器(对beanFactory的拓展)
// 原始的beanFactory并没有解析Configuration的能力
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 拿到Bean工厂的后处理器
// 根据bean的类型拿到bean,返回结果是个map集合
// beanFactory后处理器的主要功能,补充了一些bean的定义
// 没有添加后处理器之前只拿到config一个bean,加了后处理器后
// 拿到所有的bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
// Bean 后处理器 针对 bean的生命周期的各个阶段提供扩展,列入@Autowired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(beanFactory::addBeanPostProcessor);
for ( String name: beanFactory.getBeanDefinitionNames()) {
//
System.out.println("name = " + name);
}
System.out.println("下面是拿取bean的操作");
// beanFactory中已经存在Bean1和Bean2,那么考虑拿出来使用
// Bean1自动注入了bean2,需要考虑依赖绑定
Bean2 bean2 = beanFactory.getBean(Bean1.class).getBean2();
System.out.println("bean2 = " + bean2);
// 此时bean2为null,自动注入对于beanFactory来说也是一种扩展功能
}
@Configuration
static class Config{
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean1()");
}
}
}
bean 后处理器
在没有添加bean处理器时候,拿不到Bean1中自动注入的bean2 属性,值为空,当加入bean处理器之后,就能正常拿到自动注入
// Bean 后处理器 针对 bean的生命周期的各个阶段提供扩展,列入@Autowired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class)
.values().stream().forEach(beanFactory::addBeanPostProcessor);
for ( String name: beanFactory.getBeanDefinitionNames()) {
//
System.out.println("name = " + name);
}
System.out.println("下面是拿取bean的操作");
// beanFactory中已经存在Bean1和Bean2,那么考虑拿出来使用
// Bean1自动注入了bean2,需要考虑依赖绑定
//准备好所有的单例
beanFactory.preInstantiateSingletons();
Bean2 bean2 = beanFactory.getBean(Bean1.class).getBean2();
System.out.println("bean2 = " + bean2);
// 此时bean2为null,自动注入对于beanFactory来说也是一种扩展功能
// 加入bean处理器之后,bean2的值bean2 = com.briup.TestBeanFactory$Bean2@4450d156
当第一次创建bean的时候才会创建,但是单例想预先准备好所有单例
否则需要在调用的时候才会创建单例 beanFactory.preInstantiateSingletons();
后处理器排序
Autowired如果没有指定bean的名字,默认按成员变量的名称来注入 Resource也是
但是如果指定了bean的名字,那么就按照指定的名称来注入
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
@Autowired
@Resource(name = "bean4")
private Inter bean3;
public Inter getInter() {
return bean3;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
interface Inter {
}
static class Bean3 implements Inter {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean3() {
log.debug("构造 Bean3()");
}
}
static class Bean4 implements Inter {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean4() {
log.debug("构造 Bean4()");
}
}
day-02 总结:
beanFactory 不会做的事:
1.不会主动调用BeanFactory后处理器
2.不会主动添加Bean 后处理器
3.不会主动初始化单例
4.不会解析beanFactory 还不会解析${} 与 #{}
bean 后处理器会有排序的逻辑
谁的后处理器先被添加,谁的优先级就高
可以通过比较器来控制先后顺序
day-03
ApplicationContext实现
四种实现
- ClassPathXmlApplicationContext
- 基于classpath下xml 格式的配置文件来创建
- FileSystemXmlApplicationContext
- 基于磁盘下xml 格式的配置文件来创建
- AnnotationConfigApplication
- 基于java配置类来创建
- AnnotationConfigServletWebServerApplicationContext
package com.briup.a02;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
/**
* @author 火云勰神
* @date 2022-10-13 22:43
* @description 常见的Application实现类
*/
public class A02Application {
private static final Logger log = LoggerFactory.getLogger(A02Application.class);
public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取前");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
System.out.println("读取后");
// 指定读取的目标要存储到beanFactory当中
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 指定需要读取的xml文件
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
/*
* 读取前
读取后
23:25:50.540 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [b01.xml]
beanDefinitionName = bean1
beanDefinitionName = bean2
* */
System.out.println("anno");
testAnnotationConfigApplication();
}
// 经典容器 基于classpath下xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("b01.xml");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
System.out.println(ctx.getBean(Bean2.class).getBean1());
}
// 运行结果
// beanDefinitionName = bean1
//beanDefinitionName = bean2
//com.briup.a02.A02Application$Bean1@cb51256
// 基于磁盘路径下的xml格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext ctx =
new FileSystemXmlApplicationContext("D:\\ideaproject\\project\\springdp\\spring_depth_03_applicationcontext\\src\\main\\resources\\b01.xml");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
System.out.println(ctx.getBean(Bean2.class).getBean1());
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1){
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
// 较为经典的容器 基于java配置类来创建
private static void testAnnotationConfigApplication() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
for (String name : ctx.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
System.out.println(ctx.getBean(Bean2.class).getBean1());
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1) {
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
// 较为经典的容器,基于java配置类来创建,用于web环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
}
//内部静态配置类
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet () {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatherServlet dispatherServlet) {
return new DispatcherServletRegistrationBean(dispatcherSerrvlet , "/");
}
}
day-04
bean的生命周期
SpringBean 引导类
@SpringBootApplication
public class SpringBean {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringBean.class, args);
ctx.close();
}
}
LifeCycleBean 生命周期的bean
@Component
public class LifeCycleBean {
private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);
public LifeCycleBean() {
log.debug("构造");
}
@Autowired
public void autoWire(@Value("${JAVA_HOME}") String home) {
log.debug("依赖注入:{}",home);
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@PreDestroy
public void destory() {
log.debug("销毁");
}
}
执行结果:
2022-10-17 23:04:35.018 DEBUG 8196 --- [ main] com.briup.LifeCycleBean : 构造
2022-10-17 23:04:35.023 DEBUG 8196 --- [ main] com.briup.LifeCycleBean : 依赖注入:C:\Program Files\Java\jdk1.8.0_301
2022-10-17 23:04:35.024 DEBUG 8196 --- [ main] com.briup.LifeCycleBean : 初始化
2022-10-17 23:04:35.117 INFO 8196 --- [ main] com.briup.SpringBean : Started SpringBean in 0.977 seconds (JVM running for 1.885)
2022-10-17 23:04:35.121 DEBUG 8196 --- [ main] com.briup.LifeCycleBean : 销毁
此时的执行结果为 构造-> 依赖注入 -> 初始化 -> 销毁
当前并没有加入后处理器,加入后处理器后 ,构造过程前后两个增强 ,依赖注入前有一个增强,初始化前后有两个增强,销毁前有一个增强
MyBeanPostProcessor 后处理器的bean
package com.briup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
//bean后处理器
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
return bean;
}
}
执行结果:
2022-10-17 23:11:13.069 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean
2022-10-17 23:11:13.071 DEBUG 10104 --- [ main] com.briup.LifeCycleBean : 构造
2022-10-17 23:11:13.075 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段
2022-10-17 23:11:13.075 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource
2022-10-17 23:11:13.077 DEBUG 10104 --- [ main] com.briup.LifeCycleBean : 依赖注入:C:\Program Files\Java\jdk1.8.0_301
2022-10-17 23:11:13.078 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties
2022-10-17 23:11:13.078 DEBUG 10104 --- [ main] com.briup.LifeCycleBean : 初始化
2022-10-17 23:11:13.078 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强
2022-10-17 23:11:13.178 INFO 10104 --- [ main] com.briup.SpringBean : Started SpringBean in 0.97 seconds (JVM running for 1.799)
2022-10-17 23:11:13.186 DEBUG 10104 --- [ main] com.briup.MyBeanPostProcessor : <<<<<< 销毁之前执行, 如 @PreDestroy
2022-10-17 23:11:13.186 DEBUG 10104 --- [ main] com.briup.LifeCycleBean : 销毁
bean的生命周期: (关于构造的后处理器)->构造->(关于构造的后处理器)->(关于依赖注入的后处理器)->依赖注入->(关于初始化的后处理器)->初始化->(关于初始化的后处理器)->(关于销毁的后处理器)->销毁
模板方法(设计模式)
作用:提高现有代码的扩展能力
步骤: 1.分析哪些代码是不变的(主干),哪些方法需要动态增强的
2.将需要增强的方法抽象为一个接口
3.在主干类中定义一个类型为接口的集合,用于添加方法
4.定义一个动态添加方法的方法
5.在需要增强的代码后遍历集合,判断,取出方法对代码进行增强
第一步
当前代码的可拓展性很差,如果希望在依赖注入后面进行增强,需要改动代码,因此寻找一种方法来动态增强,此处采取模板方法
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.getBean();
}
public Object getBean () {
Object bean = new Object();
System.out.println("构造" + bean);
System.out.println("依赖注入" + bean);
System.out.println("初始化" + bean);
return bean;
}
}
第二步
抽象出一个接口
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.getBean();
}
static class MyBeanFactory {
public Object getBean () {
Object bean = new Object();
System.out.println("构造" + bean);
System.out.println("依赖注入" + bean);
System.out.println("初始化" + bean);
return bean;
}
}
// 变化的,需要增强的 定义成一个方法作为增强
// 类相对于主干
static interface BeanPostProcessor {
public void inject(Object bean); //对依赖注入阶段的扩展
}
}
第三步
编写一个接口类型的list集合,并写一个add方法向集合添加方法
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.getBean();
}
static class MyBeanFactory {
public Object getBean () {
Object bean = new Object();
System.out.println("构造" + bean);
System.out.println("依赖注入" + bean);
System.out.println("初始化" + bean);
return bean;
}
// 写一个接口类型的集合,然后在方法进行遍历
private List<BeanPostProcessor> postProcessors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor postProcessor) {
postProcessors.add(postProcessor);
}
}
static interface BeanPostProcessor {
public void inject(Object bean); //对依赖注入阶段的扩展
}
}
第四步
遍历集合并实现动态增强
package com.briup;
import java.util.ArrayList;
import java.util.List;
/**
* @author 火云勰神
* @date 2022-10-17 23:18
* @description
*/
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
@Override
public void inject(Object bean) {
System.out.println("解析Autowired");
}
}); //可以添加多个
beanFactory.getBean();
}
// 模板方法 Template Method Pattern
static class MyBeanFactory {
// 简化模拟bean工厂
// 当前可拓展性极差
public Object getBean () {
Object bean = new Object();
System.out.println("构造" + bean);
System.out.println("依赖注入" + bean);
for (BeanPostProcessor postProcessor : postProcessors) {
postProcessor.inject(bean);
}
System.out.println("初始化" + bean);
return bean;
}
// 写一个接口类型的集合,然后在方法进行遍历
private List<BeanPostProcessor> postProcessors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor postProcessor) {
postProcessors.add(postProcessor);
}
}
// 变化的,需要增强的 定义成一个方法作为增强
// 类相对于主干
static interface BeanPostProcessor {
public void inject(Object bean); //对依赖注入阶段的扩展
}
}
动态增强前
构造java.lang.Object@4d7e1886
依赖注入java.lang.Object@4d7e1886
初始化java.lang.Object@4d7e1886
动态增强后
构造java.lang.Object@4d7e1886
依赖注入java.lang.Object@4d7e1886
解析Autowired
初始化java.lang.Object@4d7e1886
day-05
常见的bean后处理器
Postprocessor
package com.briup;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.Resource;
@SpringBootApplication
public class Postprocessor {
public static void main(String[] args) {
// GenericApplicationContext是一个干净的容器
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方法注册三个bean
context.registerBean("bean1",Bean1.class);
context.registerBean("bean2",Bean2.class);
context.registerBean("bean3",Bean3.class);
// @Autowired @Value 添加一个后处理器
// 因为默认的解析器无法解析值注入,需要对容器进行添加
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 让@Resource @PostConstruct @PreDestroy注解生效
context.registerBean(CommonAnnotationBeanPostProcessor.class);
// 初始化容器
context.refresh(); //执行内的beanFactory后处理器,添加bean后处理器,初始化所有单例
// 销毁容器
context.close();
}
}
Bean1
package com.briup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* @author 火云勰神
* @date 2022-10-18 22:57
* @description
*/
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
private Bean2 bean2;
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("@Autowired 生效:{}",bean2);
this.bean2 = bean2;
}
private Bean3 bean3;
@Resource
public void setBean3(Bean3 bean3) {
log.debug("@Resource 生效:{}",bean3);
this.bean3 = bean3;
}
private String home;
@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.debug("@Value 生效:{}",home);
this.home = home;
}
@PostConstruct
public void init() {
log.debug("@PostConstruct 生效");
}
@PreDestroy
public void destroy() {
log.debug("@PreDestroy 生效");
}
@Override
public String toString() {
return "Bean1{" +
"bean2=" + bean2 +
", bean3=" + bean3 +
", home='" + home + '\'' +
'}';
}
}
public class Bean2 {
}
public class Bean3 {
}
DigInAutowired(AutowiredAnnotationBeanPostProcessor 运行分析)
//AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// context.registerBean方法注册bean比较麻烦
beanFactory.registerSingleton("bean2",new Bean2()); //认为该bean为依据创建好的,不会再进行生命周期 创建 依赖注入 初始化 销毁
beanFactory.registerSingleton("bean3",new Bean3());
// 能解析Autowired value注解
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// 1. 查找那些属性 方法加了 autowired 称之为InjectionMetadata
// 为了方便观察直接使用AutowiredAnnotationBeanPostProcessor的方法
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
// 后处理器需要解析autowired,需要根据成员变量/方法参数的类型找到需要注入的对象
// 而beanFactory能够提供,所以后处理器需要间接使用beanFactory
processor.setBeanFactory(beanFactory);
// 自己创建的bean1并不会依赖注入之类的操作
Bean1 bean1 = new Bean1();
System.out.println("bean1 = " + bean1);
// 执行依赖注入 解析autowired value
// 三个参数的含义 是否为自动装配(不需要关系) 第二个为目标对象 第三个为名称
processor.postProcessProperties(null,bean1,"bean1");
System.out.println("bean1 = " + bean1);
执行结果为:
bean1 = Bean1{bean2=null, bean3=null, home='null'}
23:48:26.394 [main] DEBUG com.briup.Bean1 - @Value 生效:${JAVA_HOME}
23:48:26.402 [main] DEBUG com.briup.Bean1 - @Autowired 生效:com.briup.Bean2@5442a311
bean1 = Bean1{bean2=com.briup.Bean2@5442a311, bean3=null, home='${JAVA_HOME}'}
在上面代码中有个很重要的方法
processor.postProcessProperties(null,bean1,“bean1”)
由于原码中的findAutowiringMetadata方法是私有方法,不能直接查看,所以采取拆分查看的策略
findAutowiringMetadata方法需要三个对象(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)
即bean的名称 bean的类 还有装配模式
原码
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2",new Bean2());
beanFactory.registerSingleton("bean3",new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// 增加一个解析器,否则拿不到${}里面的值
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
// 自己创建的bean1并不会依赖注入之类的操作
Bean1 bean1 = new Bean1();
// 由于是私有方法,需要反射调用才能够查看到结果
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
// findAutowiringMetadata方法需要三个对象(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)
// 反射指明processor对象来执行
// 获取bean1上加了 value autowired的成员变量 方法参数的信息
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
// 拿到加了autowired注解的信息
System.out.println("metadata = " + metadata);
metadata.inject(bean1,"bean1",null);
}
}
执行结果:
metadata = org.springframework.beans.factory.annotation.InjectionMetadata@29774679
00:07:11.231 [main] DEBUG com.briup.Bean1 - @Autowired 生效:com.briup.Bean2@6500df86
00:07:11.239 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
00:07:11.242 [main] DEBUG com.briup.Bean1 - @Value 生效:C:\Program Files\Java\jdk1.8.0_301
// 先入手成员方法
Field bean3 = Bean1.class.getDeclaredField("bean3");
// 拿到成员变量后,spring内部会封装为DependencyDescriptor对象
// 两个参数,第一个是bean 第二个是方式 如果是false,那依赖注入的时候如果找不到,也不会报错
// 即autowired的注入方式
DependencyDescriptor dd1 = new DependencyDescriptor(bean3,false);
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println("o = " + o);
Method declaredMethod = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
// DependencyDescriptor是以参数为单位进行封装的
// 但是一个方法可能带有多个参数
DependencyDescriptor dd2 = new DependencyDescriptor
// 指定是哪个方法的,并且也指定是第几个参数
(new MethodParameter(declaredMethod,0),false);
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
// 有bean2对象 此时输出o1 = com.briup.Bean2@7085bdee 找到bean2
// 没有bean2对象 找到的是null
// 根据方法参数的类型去容器里面找
System.out.println("o1 = " + o1);
总结:
- 如果是成员变量,根据属性类型进行寻找
- 如果是方法,根据方法参数类型进行寻找
day-06
常见工厂后处理器(BeanFactory后处理器)
ConfigurationClassPostProcessor后处理器:
- 使@ComponentScan @Bean @Import @ImportResource注解能够使用
MapperScannerConfigurer后处理器
- 扫描mybatis的mapper
package com.briup;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
@SpringBootApplication
public class BFactory {
private static final Logger log = LoggerFactory.getLogger(BFactory.class);
public static void main(String[] args) {
// 定义一个干净的容器
GenericApplicationContext context = new GenericApplicationContext();
// 注册一个名为config的bean
context.registerBean("config",Config.class);
// bean工厂后处理器用来开启扫描@ComponentScan @Bean @Import @ImportResource注解
context.registerBean(ConfigurationClassPostProcessor.class);
// 整合mybatis时,扫描mybatis的mapper
context.registerBean(MapperScannerConfigurer.class,bd->{
// 需要指定包名才能使用
bd.getPropertyValues().add("basePackage","com.briup.mapper");
}); //MapperScanner注解
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
// 销毁容器
context.close();
}
}
Config.java
package com.briup;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author 火云勰神
* @date 2022-10-19 23:03
* @description
*/
@Configuration
@ComponentScan("com.briup.component")
public class Config {
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean(initMethod = "init")
public DruidDataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
Bean2.java
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("被spring 管理");
}
}
增加bean后处理器后的执行结果
name = config
name = org.springframework.context.annotation.ConfigurationClassPostProcessor
name = org.mybatis.spring.mapper.MapperScannerConfigurer
name = bean2
name = bean1
name = sqlSessionFactoryBean
name = druidDataSource
name = mapper1
name = mapper2
name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
name = org.springframework.context.annotation.internalCommonAnnotationProcessor
name = org.springframework.context.event.internalEventListenerProcessor
name = org.springframework.context.event.internalEventListenerFactory
工厂后处理器模拟
组件扫描
- 第一步 通过AnnotationUtils工具类来查找某个类上是否加了注解
- 如果加了注解,那么会返回一个对象
- 如果没有加注解,那么会返回一个null值
- 第二步 通过对象拿到文件路径,并且拼接为 classpath:+文件路径+/*** * /***.class的形式
- 当前通过对象.basePackages()方法拿到com.xxx.xxx,但是spring是根据文件路径来寻找的,所以需要替换
- 第三步 根据文件路径,通过getResources方法,拿到对应包下的所有类
- 第四步 创建一个CachingMetadataReaderFactory对象,用于检测是否加了component注解
- 第五步 遍历所有的类,并进行通过getMetadataReader()方法拿到的对象进行判断
- getClassMetadata().getClassName()拿到所有的类名
- getAnnotationMetadata().hasAnnotation(Component.class.getName()) 拿到加了某个注解的类,但是不能检测这个注解的派生注解
- getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) 可以拿到注解的派生类注解
- 第六步 如果直接或者间接加了注解,要把它变成对应的BeanDefinition
- 通过容器的getDefaultListableBeanFactory().registerBeanDefinition(s,bd)方法来完成
- 但是此时需要两个参数,一个是bean的名字,另一个是beanDefinition
- bean的名字通过AnnotationBeanNameGenerator工具类来完成,可以根据bean来生成一个bean的名称
- beanDefinition可以通过BeanDefinitionBuilder来完成
- 通过容器的getDefaultListableBeanFactory().registerBeanDefinition(s,bd)方法来完成
package com.briup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.io.IOException;
/*
* 工厂后处理器模拟-组件扫描
* 模拟componentScan注解的解析
* */
@SpringBootApplication
public class SimulationApplication {
private static final Logger log = LoggerFactory.getLogger(SimulationApplication.class);
public static void main(String[] args) throws Exception {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config",Config.class);
// spring提供的注解,查看某个类上是否加了注解
// 找到会返回注解对象,没有找到返回null
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
// 可以通过对象拿到属性,是个数组类型
// 需要把结果转换为文件路径类型,因为spring是根据文件路径来找的,并不是根据反射来做的
for (String basePackage : componentScan.basePackages()) {
// basePackage = com.briup.component -> classpath:com/briup/component/**/*.class
String path ="classpath*:"+basePackage.replace(".","/")+"/**/*.class";
// path = classpath*:com/briup/component/**/*.class
// 根据spring的一个工具类来判断是否加了注解
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
Resource[] resources = context.getResources(path);
// 此时就一个Bean2,如果包下面存在其他类,都会被扫描出来
// resource = file [D:\ideaproject\project\springdp\spring_depth_07_simulation\target\classes\com\briup\component\Bean2.class]
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:"+reader.getClassMetadata().getClassName());
System.out.println("是否加了注解component:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
// hasAnnotation方法只能判断有没有加component注解,不能判断有没加它的派生注解,如controller
// hasMetaAnnotation才可以
System.out.println("是否加了注解component的派生注解:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
// 如果直接或者加了注解, 变成对应的BeanDefinition
if(reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
|| reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) {
// 需要给一个参数,bean的类名
AbstractBeanDefinition bd = BeanDefinitionBuilder .genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
// 加入到bean工厂
// 因为此时生成bean需要用到bean的名字,所以借助工具类来获取
// AnnotationBeanNameGenerator工具类分析注解,并生成对应的名称
String s = generator.generateBeanName(bd, context.getDefaultListableBeanFactory());
context.getDefaultListableBeanFactory().registerBeanDefinition(s,bd);
}
}
}
}
// 初始化容器
context.refresh();
for (String name :
context.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
// 销毁容器
context.close();
}
}
运行结果:
类名:com.briup.component.Bean2
是否加了注解component:true
是否加了注解component的派生注解:false
类名:com.briup.component.Bean3
是否加了注解component:false
是否加了注解component的派生注解:true
类名:com.briup.component.Bean4
是否加了注解component:false
是否加了注解component的派生注解:false
name = config
name = bean2
name = bean3
封装为一个后处理器类
package com.briup;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @author 火云勰神
* @date 2022-10-21 00:08
* @description 分析component注解并做出bean定义的补充
*/
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override // context.refresh
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
String name = generator.generateBeanName(bd, beanFactory);
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Bean
步骤:
- 先按照路径获取到Config类的metadata
- new CachingMetadataReaderFactory()
- factory.getMetadataReader(new ClassPathResource
- 拿到所有跟注解相关的数据
- 获取被xx注解标注的方法
- 拿到和bean注解相关的方法
- metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
- 拿到bean注解的initMethod属性值
- method.getAllAnnotationAttributes(Bean.class.getName()).get(“initMethod”).toString();
- 但并不是所有的bean都具有初始化属性,所以需要进行判断,如果有,builder进行设置,如果没有就不需要考虑
- 声明builder
- 此时不需要指定类名,之前指定类名是指明最终产品的名称
- 当前是将bean标注的方法变成工厂方法,因此并不需要指定
- BeanDefinitionBuilder.genericBeanDefinition()
- 定义xx类的工厂方法
- 方法需要两个参数,第一个是方法名,第二个是beanFactory
- 先有工厂对象才能调用工厂里的方法,这些方法都是属于Config对象
- 当前代码的含义就是,定义了一些Config类的工厂方法
- builder.setFactoryMethodOnBean(method.getMethodName(),“config”);
- 设置自动装配
- 因为sqlSessionFactoryBean在前,并且调用了dataSource这个bean,所以需要设置自动装配模式才能正常使用
- 构造方法/工厂方法自动装配的模式都是Constructor
- 获取beanDefinition
- builder.getBeanDefinition();
- 添加到容器中
- context.getDefaultListableBeanFactory().registerBeanDefinition
// 把bean变成工厂方法
// 读取类的元数据信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// getMetadataReader不走类加载实现,按照路径来读取
MetadataReader metadataReader = factory.getMetadataReader(new ClassPathResource("com/briup/Config.class"));
// 1.拿到所有跟数据相关的数据
// 2.获取被注解标注的方法
Set<MethodMetadata> methods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
// 拿到bean注解的initMethod属性值
String initMethod = method.getAllAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
// 此时不需要指定类名,之前指定类名是指明最终产品的名称
// 当前是将bean标注的方法变成工厂方法,因此并不需要指定
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
// 并不是所有的bean都会有初始化,如果没有,返回的是一个空字符串
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
// 第一个参数是方法名
// 调用方法是需要有对象,这些方法都是属于Config对象
// 先有工厂对象才能调用工厂里的方法
// 当前代码的含义就是,定义了一些Config类的工厂方法
builder.setFactoryMethodOnBean(method.getMethodName(),"config");
// 因为sqlSessionFactoryBean在前,并且调用了dataSource这个bean
// 所以需要设置自动装配模式才能正常使用
// 构造方法/工厂方法自动装配的模式都是Constructor
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(),beanDefinition);
}
封装为方法
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/briup/Config.class"));
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
System.out.println(method);
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = builder.getBeanDefinition();
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
最终结果:
com.briup.Config.bean1()
com.briup.Config.sqlSessionFactoryBean(javax.sql.DataSource)
com.briup.Config.druidDataSource()
[INFO ] 23:54:07.163 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} inited
name = config
name = com.briup.AtBeanPostProcessor
name = bean1
name = sqlSessionFactoryBean
name = druidDataSource
[INFO ] 23:54:07.280 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closing ...
[INFO ] 23:54:07.282 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closed
Mapper
spring并不能直接管理接口,最终还是管理对象
spring底层对接口bean的添加方式,适用于单个bean的添加,但是不能一次添加多个
// MapperFactoryBean工厂生产对象
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
最终往容器注入的是Mapper工厂,但是由于生成名称是按照类型生成的,所以需要重新生成一个接口类型的beanFactory来生成名字
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
// 实现BeanRegistryPostProcessor的子接口来省去对于强转得到registerBeanDefinition方法
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("com/briup/mapper/**/*.class");
// 保证只拿接口
// 读取类的元数据信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
// 针对每个resource去读取信息
MetadataReader metadataReader = factory.getMetadataReader(resource);
// 调用方法得到类的元数据信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 生成名字
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
if (classMetadata.isInterface()) {
// 拿到接口,生成MapperFactoryBean
// 交给spring管理的是mapper工厂
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
// 给构造方法设置一个参数值
// 也就是把Mapper1作为构造方法的参数加入
.addConstructorArgValue(classMetadata.getClassName())
// 还需要设置sqlSessionFactoryBean,通过设置装配模式按类型装配
// 找到一个符合类型的sqlSessionFactoryBean
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
// 因为名字生成是按照类型来进行生成的,此时的类型mapperFactoryBean,所以做后生成的是mapperFactoryBean
// 后面一次名字相同,所以吧前面的同名bean覆盖
// 当前beanDefinition只是用来根据类型生成名字,并不做添加
AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String name = generator.generateBeanName(bd2, beanFactory);
beanFactory.registerBeanDefinition(name,bd);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
day-07
Aware接口和InitializingBean接口
- Aware 接口用于注入一些相关信息,常见的aware接口:
- (1)BeanNameAware 注入bean的名字
- (2)BeanFactoryAware 注入 BeanFactory容器
- (3)ApplicationContextAware 注入 ApplicationContext 容器
- (4)EmbeddedValueResolverAware 解析${}
- 为什么上面四条功能都能使用autowired实现,还要用aware接口
- @Autowired的解析需要用bean后处理器,属于扩展方法,需要添加后处理器才能进行使用
- aware接口属于内置功能,不加任何拓展,而spring就能识别,某些情况下,扩展功能会失效,而内置功能不会失效
AwareAndInitializing.class
- 使用autowired注解需要加后处理器才能有效果
public static void main(String[] args) {
SpringApplication.run(AwareAndInitializing.class, args);
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBean("myBean",MyBean.class);
ctx.registerBean(AutowiredAnnotationBeanPostProcessor.class);
ctx.registerBean(CommonAnnotationBeanPostProcessor.class);
ctx.refresh();
ctx.close();
/*
* 为什么上面四条功能都能使用autowired实现,还要用aware接口
* 答:
* 1. @Autowired的解析需要用bean后处理器,属于扩展方法
* 2. aware接口属于内置功能,不加任何拓展,而spring就能识别,某些情况下,扩展功能会失效,而内置功能不会失效
* 例如:使用Aware注入ApplicationContext成功,@Autowired注入就会失效
* */
MyBean.class
- 实现BeanNameAware接口实现注入bean的名字
- 实现ApplicationContextAware接口实现注入容器
- 实现InitializingBean接口实现初始化功能
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void setBeanName(String name) {
log.debug("当前bean"+this+"名字叫" + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean"+this+"容器是" + applicationContext);
}
// 只有实现这个bean接口才对应真正的初始化方法,先去回调aware接口再执行初始化方法
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean"+this+"初始化" );
}
@Autowired
public void test(ApplicationContext applicationContext) {
log.debug("当前bean"+this+"使用autowired注入"+applicationContext);
}
}
执行结果
- 当前beancom.briup.MyBean@12bd8a64使用autowired注入org.springframework.context.support.GenericApplicationContext@23fb172e, started on Thu Nov 03 22:58:14 CST 2022
- 当前beancom.briup.MyBean@12bd8a64名字叫myBean
- 当前beancom.briup.MyBean@12bd8a64容器org.springframework.context.support.GenericApplicationContext@23fb172e, started on Thu Nov 03 22:58:14 CST 2022
- 当前beancom.briup.MyBean@12bd8a64初始化
只有实现InitializingBean接口才对应真正的初始化方法,执行顺序是:先去回调aware接口再执行初始化方法
@Autowired失效分析
-
aware 接口提供了内置的注入手段,可以注入BeanFactory、ApplicationContext
-
InitializingBean 接口提供了一种内置的初始化手段
-
内置的注入和初始化不受扩展功能的影响,总会被执行,因此spring框架内部的类常用他们
场景模拟:
autowired注解失效,java配置添加了bean工厂后处理器后,使用传统接口方式的注入和初始化依然成功,而autowired的注入和初始化失败
引导类
GenericApplicationContext ctx = new GenericApplicationContext();
// ctx.registerBean("myConfig1",MyConfig1.class);
ctx.registerBean("myConfig2",MyConfig2.class);
ctx.registerBean(AutowiredAnnotationBeanPostProcessor.class);
ctx.registerBean(CommonAnnotationBeanPostProcessor.class);
ctx.registerBean(ConfigurationClassPostProcessor.class);
ctx.refresh();
ctx.close();
注入失败的配置类1
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext){
log.debug("注入ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@Bean // 添加一个bean工厂的后处理器
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.debug("执行 processor1");
};
}
}
能够完成注入的配置类2
public class MyConfig2 implements InitializingBean,ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("注入applicationContext");
}
@Bean // 添加一个bean工厂的后处理器
public BeanFactoryPostProcessor processor2() {
return beanFactory -> {
log.debug("执行 processor2");
};
}
}
前置知识:
ctx.refresh()方法的主要执行顺序:
-
bean工厂的后处理器
-
添加bean的后处理器
-
执行初始化单例
如下图所示:
- 执行容器内的beanFactory后处理器
- 注册bean后处理器
- 创建单例,初始化
- 执行依赖注入,回调bean后处理器解析 @Value @Autowired
- 初始化 扩展
- 执行Aware 再IntializingBean
@Bean // 添加一个bean工厂的后处理器
public BeanFactoryPostProcessor processor2() {
return beanFactory -> {
log.debug("执行 processor2");
};
}
当前提供的beanFactory后处理器是通过工厂方法的模式配置的,被调用的前提是创建方法所在的对象,配置类对象也是个单例度对象
因此去执行这个方法的时候,执行顺序发生变化,相当于java配置类被提前创建,导致扩展功能失效
day-08
初始化和销毁
初始化/销毁的三种方式
- @PostConstruct/@PreDestroy
- 实现InitializingBean/DisposableBean
- @Bean
初始化
public class Bean1 implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
@PostConstruct
public void init1() {
log.debug("初始化1");
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化2");
}
public void init3() {
log.debug("初始化3");
}
}
销毁
public class Bean2 implements DisposableBean {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
@PreDestroy
public void destroy1(){
log.debug("销毁1");
}
@Override
public void destroy() throws Exception {
log.debug("销毁2");
}
public void destroy3() {
log.debug("销毁3");
}
}
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(InitAndDestroy.class, args);
ctx.close();
}
@Bean(initMethod = "init3")
public Bean1 bean1(){
return new Bean1();
}
@Bean(destroyMethod = "destroy3")
public Bean2 bean2(){
return new Bean2();
}
三种方式的执行顺序: 拓展类接口声明PostConstruct/PreDestroy -> 实现接口的声明InitializingBean/DisposableBean -> @Bean的声明
day-09
scope
spring5版本下有5个scope(范围/作用域):
- singleton
- 每次获取bean,获取的都是同一个对象(单例)
- spring容器创建时创建,关闭时销毁
- prototype
- 每次获取bean,都会产生新的对象(多例)
- 每次使用是会创建,销毁不由容器管理,可以自行调用方法销毁
- request
- 请求域,bean存在于request中,生命周期一致
- 销毁时机:请求来的时候,创建bean存在于request当中,请求结束就会进行销毁
- session
- 会话域,同一个会话内,会话开始,bean创建
- 销毁时机:会话创建时创建bean存在于session域中,销毁时销毁,如 session超时
- application
- 应用程序域,应用程序启动时,bean创建,应用程序销毁,bean销毁,但是spring并没有正确的实现销毁方法,此时的应用程序指的是,web中的servletContext,
新注解 :
- @Scope
- @Lazy
- 因为此时mycontroller是单例,单例使用其他域就需要加上lazy注解,否则会存在失效问题
jdk>=9以后,通过反射调用jdk中的方法,会出现非法访问的错误,这个时候有三种解决手段:
- 降低jdk版本
- 重写方法,就比如此时的例子,加了@Lazy注解,然后并且需要在页面显示,相当于调用了Object中的toString方法,此时可以重写toString方法
- 加入一个参数 --add-opens java.base/java.lang=ALL-UNNAMED
BeanForApplication
@Scope("application")
@Component
public class BeanForApplication {
private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
@PreDestroy
public void destroy(){
log.debug("destroy");
}
}
BeanForRequest
@Scope("request")
@Component
public class BeanForRequest {
private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
@PreDestroy
public void destroy(){
log.debug("destroy");
}
}
BeanForSession
@Scope("session")
@Component
public class BeanForSession {
private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
@PreDestroy
public void destroy(){
log.debug("destroy");
}
}
MyController
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForRequest beanForRequest;
@Lazy
@Autowired
private BeanForSession beanForSession;
@Lazy
@Autowired
private BeanForApplication beanForApplication;
@GetMapping(value = "/test", produces = "text/html")
public String test(HttpServletRequest request, HttpSession session) {
ServletContext sc = request.getServletContext();
String sb = "<ul>" +
"<li>" + "request scope:" + beanForRequest + "</li>" +
"<li>" + "session scope:" + beanForSession + "</li>" +
"<li>" + "application scope:" + beanForApplication + "</li>" +
"</ul>";
return sb;
}
}
此时运行得到的结果:
-
当刷新页面,只有request发生变化,当重新发送请求时,即刷新页面,此时就request发生变化 新的请求,针对request域对象会创建一个新的对象放入到web请求中,请求结束时销毁
-
此时session没变,同一个浏览器不管发送多少次请求,都是属于同一个会话,都是属于session的bean,当重新打开一个浏览器访问,session发生变化
-
此时application没变,同一个web应用程序(ServletContext)不发生变化
scope失效解决
单例对象注入多例时失效
原因
- 单例对象依赖注入只发生一次,e创建好,执行e的依赖注入,创建f1,注入f1,后续没有用到多例的F,因此E用的始终是第一次依赖注入的F
@Component
public class E {
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
@Scope("prototype")
@Component
public class F1 {
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(A08_1.class);
E e = context.getBean(E.class);
System.out.println(e.getF1());
System.out.println(e.getF1());
System.out.println(e.getF1());
context.close();
}
此时的运行结果都是一样的,但是由于F配置多例,希望得到的是不一样的对象
解决
使用@Lazy注解
-
加载被注入的成员变量,但是不是真的注入f1,而是代理,每次调用f1的方法时,由代理创建一个真正的f1对象
-
@Component public class E { @Lazy @Autowired private F1 f1; public F1 getF1() { return f1; } }
在多例对象配置
-
@Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
-
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) @Component public class F2 { }
-
就不需要在注入的时候使用@Lazy注解
使用ObjectFactory解决
不是使用代理的方式来解决,相当于通过一个工厂来识别f3的多例
@Autowired
private ObjectFactory<F3> f3;
public F3 getF3() {
return f3.getObject();
}
注入ApplicationContext
@Autowired
private ApplicationContext context;
public F4 getF4() {
return context.getBean(F4.class);
}
即使是注入其他的scope,也具有类似的四种解决方法,解决方法的共同思想都是推迟scope bean的获取
day-10
aop之ajc增强
@SpringBootApplication
public class AopSpringDepth {
private static final Logger log = LoggerFactory.getLogger(AopSpringDepth.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopSpringDepth.class, args);
MyService myService = context.getBean(MyService.class);
System.out.println(myService.getClass());
myService.foo();
context.close();
}
}
结果:
class com.briup.service.MyService
before()
foo()
目标类
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
public void foo() {
System.out.println("foo()");
}
}
切面类
@Aspect //此切面并不受到spring容器管理
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
@Before("execution(* com.briup.service.MyService.foo())")
public void before() {
System.out.println("before()");
}
}
结论
实例说明,并不是所有的aop增强都是使用,用aspect编译器进行增强,原理是改写原代码,并且因为都改写了原代码,那么说明切面类并不受spring容器管理,甚至启动类可以只写new一个对象来调用方法,即
public class AopSpringDepth {
public static void main(String[] args) {
new MyService.foo();
}
}
对比
改写前
public void foo() {
System.out.println("foo()");
}
改写后(通过反编译观察)
public void foo() {
MyAspect.aspectOf().before();
System.out.println("foo()");
}
使用
既然都不受到spring容器管理,那么它的使用肯定比较特殊,需要使用到一个编译插件aspectj,代理不能增强静态方法,但是aspectj可以做到增强
注意:如果idea调用自带编译器javac,达不到增强效果,需要走aspectj编译环境增强
<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>
aop之agent增强
不是在编译阶段来增强修改,而是在类加载的阶段来进行增强
切面类
@Aspect
public class MyAspect {
@Before("execution(* com.briup.service.MyService.*())")
public void before() {
System.out.println("before()");
}
}
目标类
调用本类的方法,并不会通过代理,而是通过this
@Service
public class MyService {
final public void foo() {
System.out.println("foo()");
bar();
}
public void bar() {
System.out.println("bar()");
}
}
此时没用通过编译器插件,不能做到编译时重写目标类进行增强,需要写一个jvm参数
-javaagent:D:/maven/maven_repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
其中D:/maven/maven_repository 改为 maven 仓库起始地址
aop之proxy(基于jdk)增强
利用proxy.newProxyInstance完成需要增强逻辑的编写:
- Proxy.newProxyInstance()
- 方法三个参数的含义,类加载器 实现的接口 封装行为的对象
- 类加载器:代理没有原码,运行期间直接生成字节码,需要加载后运行
- 接口:可以使用数组的形式,可能存在多个接口
- handler中的invoke中的三个方法参数分别为
- 代理对象本身 正在执行的方法对象 以及方法传递过来的参数
- 通过反射调用实现功能增强
- method.invoke(目标类,方法参数)
- 方法三个参数的含义,类加载器 实现的接口 封装行为的对象
- 注意:
- 基于jdk实现的动态代理,代理类和目标类属于平级的兄弟关系,都是实现了接口,他俩直接不能进行相互转换
- 目标是可以为final类型的
public class JdkProxy {
interface Foo {
void foo();
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}
}
// jdk代理
public static void main(String[] args) {
// 目标对象
Target target = new Target();
// 代理没有原码,运行期间直接生成字节码,需要加载后运行
// 用来加载运行期间动态生成的字节码
ClassLoader loader = JdkProxy.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
@Override
// 代理对象本身 正在执行的方法对象 以及方法传递过来的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
// 当前完成增强的逻辑,那么也需要调用原有的逻辑
// 方法.invoke(目标,参数)
Object result = method.invoke(target, args);
System.out.println("after");
//因为不一定每个需要增强的方法都是void类型,所以需要返回一个参数
return result;
}
});
proxy.foo();
}
}
aop之proxy(基于cglib)增强
cglib是子父关系型的
使用Enhancer.create来实现功能增强
- Enhancer.create()两个参数,第一个是父亲,即需要继承的目标类,第二个参数为Callback,即封装的行为,但是一般使用子接口MethodInterceptor来实现
- intercept方法四个参数分别为 代理对象本身 正在执行的方法对象 方法传递过来的参数 方法对象
- cglib调用方法实现增强的三种方式
- 反射调用 method.invoke(target, args); 需要目标对象和方法参数
- intercept方法的第四个参数来调用
- methodProxy.invoke(target, args); 避免使用反射 需要目标类和方法参数 spring中cglib使用
- methodProxy.invokeSuper(o, args); 避免使用反射 不需要目标类,需要代理对象和方法参数
- 注意:
- 目标类不能使用final,否则cglib无法生成代理对象
- 方法不能使用final,否则增强失败
public class CglibProxy {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
// 代理是子类型,目标类是父类型
public static void main(String[] args) {
// 目标对象
Target target = new Target();
// 两个参数,第一个为父亲,即需要继承的目标类
// 第二个参数为Callback,即封装的行为,但是一般使用子接口来实现
Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
@Override
// 代理对象本身 正在执行的方法对象 方法传递过来的参数 方法对象
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
// 方法反射调用目标
// Object result = method.invoke(target, args);
// 用第四个参数来进行调用,避免反射调用
// Object result = methodProxy.invoke(target, args); //内部没有用反射
// 只需要传递代理类自身,不需要目标
Object result = methodProxy.invokeSuper(o, args);
System.out.println("after");
return result;
}
});
proxy.foo();
}
}
day-11
jdk代理原理
- 首先需要和目标类实现同一个接口(jdk动态代理)
- 抽象出方法,动态调用,不把增强的逻辑写死
- 即通过调用接口的invoke方法来实现功能的增强
- 考虑有多个方法的情况,需要知道是哪个方法进行调用,参数传递
- 考虑存在返回值的情况,需要提供返回值的处理手段
- 把接口方法类型改为Object,并且提供一个代理类的对象方便以后使用
- 进行异常处理,两种类型的异常
- 编译时异常,需要转换为运行时异常抛出,不能够直接抛出
- 运行时异常,直接进行抛出
- 代理类改进
- 由于每个方法每次调用都会进行一次方法获取,改为静态类型,一次性加载调用
public class A13 {
interface Foo {
void foo();
int bar();
}
static class Target implements Foo {
@Override
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[] args) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws InvocationTargetException, IllegalAccessException {
// 1.功能增强
System.out.println("before");
// 2.调用目标
// new Target().foo();
return method.invoke(new Target(),args);
}
});
proxy.foo();
proxy.bar();
}
}
$Proxy0 代理类 模仿底层实现 底层命名也是像这样
public class $Proxy0 implements Foo {
private InvocationHandler h;
public $Proxy0(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 = Foo.class.getMethod("foo");
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
jdk代理字节码生成
jdk生成代理类没有经历源码、编译阶段,直接到了字节码阶段,即直接生成字节码,而之所以能看到java源码,是因为对字节码做反编译
直接生成字节码的底层技术叫asm,作用:动态生成字节码
public interface Foo {
public void foo();
}
$Proxy0 可以通过asm技术生成,所以暂时忽略当前手写类
public class $Proxy0 extends Proxy implements Foo {
public $Proxy0(InvocationHandler h) {
super(h);
}
@Override
public void foo() {
try {
this.h.invoke(this,foo, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static Method foo;
static {
try {
foo = Foo.class.getMethod("foo");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
通过idea插件ASM Bytecode Outline来进行查看,编译的快捷方式为ctrl+shift+F9,编译完成以后,可以使用插件进行观看,并且复制ASMified中的代码,复制为一个新的类需要进行导包,jdk和spring提供的asm效果一样,当前使用spring提供的asm
通过插件能够看到的类对象,该对象主要是通过ClassWriter生成字节码,最后返回一个byte数组
package com.briup.asm;
import org.springframework.asm.*;
public class $Proxy0Dump implements Opcodes {
public static byte[] dump() throws Exception {
// 通过ClassWriter生成字节码
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
// 生成一个类,visit方法有版本,修饰符,类名,父类,接口
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "com/briup/asm/$Proxy0", null, "java/lang/reflect/Proxy", new String[]{"com/briup/asm/Foo"});
cw.visitSource("$Proxy0.java", null);
{
fv = cw.visitField(ACC_STATIC, "foo", "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
mv.visitParameter("h", 0);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(15, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", false);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(16, l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLocalVariable("this", "Lcom/briup/asm/$Proxy0;", null, l0, l2, 0);
mv.visitLocalVariable("h", "Ljava/lang/reflect/InvocationHandler;", null, l0, l2, 1);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "foo", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Throwable");
mv.visitLabel(l0);
mv.visitLineNumber(20, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "com/briup/asm/$Proxy0", "h", "Ljava/lang/reflect/InvocationHandler;");
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETSTATIC, "com/briup/asm/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitInsn(POP);
mv.visitLabel(l1);
mv.visitLineNumber(23, l1);
Label l3 = new Label();
mv.visitJumpInsn(GOTO, l3);
mv.visitLabel(l2);
mv.visitLineNumber(21, l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"});
mv.visitVarInsn(ASTORE, 1);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitLineNumber(22, l4);
mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
mv.visitLabel(l3);
mv.visitLineNumber(24, l3);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitLocalVariable("throwable", "Ljava/lang/Throwable;", null, l4, l3, 1);
mv.visitLocalVariable("this", "Lcom/briup/asm/$Proxy0;", null, l0, l5, 0);
mv.visitMaxs(4, 2);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NoSuchMethodException");
mv.visitLabel(l0);
mv.visitLineNumber(29, l0);
mv.visitLdcInsn(Type.getType("Lcom/briup/asm/Foo;"));
mv.visitLdcInsn("foo");
mv.visitInsn(ICONST_0);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
mv.visitFieldInsn(PUTSTATIC, "com/briup/asm/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
mv.visitLabel(l1);
mv.visitLineNumber(32, l1);
Label l3 = new Label();
mv.visitJumpInsn(GOTO, l3);
mv.visitLabel(l2);
mv.visitLineNumber(30, l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/NoSuchMethodException"});
mv.visitVarInsn(ASTORE, 0);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitLineNumber(31, l4);
mv.visitTypeInsn(NEW, "java/lang/NoSuchMethodError");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "getMessage", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(ATHROW);
mv.visitLabel(l3);
mv.visitLineNumber(33, l3);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
mv.visitLocalVariable("e", "Ljava/lang/NoSuchMethodException;", null, l4, l3, 0);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
编写测试类
第一次测试生成$Proxy0.class文件,反编译后观察到的结果和之前手写的一致,但是不采用这种形式
通过类加载,根据字节数组直接生成类对象,然后拿到构造,并以此来进行反射创建
- 第一步 使用ClassLoader,重写findClass方法,提供参数:名称 字节码数组 开始位置 长度
- 第二步 通过类加载生成一个类对象
- 第三步 拿到这个类对象的构造
- 第四步 通过反射生成代理
public class TestProxy {
public static void main(String[] args) throws Exception {
byte[] dump = $Proxy0Dump.dump();
// FileOutputStream os = new FileOutputStream("$Proxy0.class");
// os.write(dump,0,dump.length);
// os.close();
// 在内存中把二进制字节码加载并使用
ClassLoader loader = new ClassLoader() {
// 重写查找类方法,根据字节数组生成类对象
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.defineClass(name,dump,0,dump.length);
}
};
// 通过类加载拿到一个类对象,并且得到他的构造
Class<?> proxyClass = loader.loadClass("com.briup.asm.$Proxy0");
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
Foo proxy = (Foo) constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
System.out.println("调用目标");
return null;
}
});
proxy.foo();
}
}
cglib代理原理
Target
package com.briup;
/**
* @author 火云勰神
* @date 2022-12-01 16:12
* @description
*/
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.briup;
import org.springframework.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
/**
* @author 火云勰神
* @date 2022-12-01 16:26
* @description
*/
public class Proxy extends Target {
// cglib的动态代理是基于MethodInterceptor来实现的
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],null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this,save1,new Object[]{i},null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this,save2,new Object[]{j},null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
package com.briup;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author 火云勰神
* @date 2022-12-01 16:50
* @description
*/
public class TestCglib {
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[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
// return methodProxy.invoke(target,objects); //内部无反射,结合目标使用
return methodProxy.invokeSuper(p,objects); //内部无反射,结合代理
}
});
proxy.save();
proxy.save(1);
proxy.save(1L);
}
}
- cglib和jdk动态代理的区别,之前对jdk动态代理进行17次调用,而16次是反射调用,第17次才进行优化直接调用,导致效率很低,而cglibb的动态代理是直接调用的,主要是通过MethodProxy参数
MethodProxy参数
需要提供一个带原始功能的方法,需要进行创建,根据MethodProxy的create方法进行创建
- 第一个参数是目标类对象
- 第二个参数是代理类对象
- 第三个参数是表明参数类型和返回值
- ()V 代表 无参无返回值
- (I)V 代表 int类型的参数无返回值
- (J)V 代表 long类型的参数无返回值
- 第四个参数是需要增强的方法
- 第五个参数是原始方法
static Method save0;
static Method save1;
static Method save2;
static MethodProxy saveSuper0;
static MethodProxy saveSuper1;
static MethodProxy saveSuper2;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
saveSuper0 = MethodProxy.create(Target.class,Proxy.class,"()V","save","saveSuper");
saveSuper1 = MethodProxy.create(Target.class,Proxy.class,"(I)V","save","saveSuper");
saveSuper2 = 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],saveSuper0);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this,save1,new Object[]{i},saveSuper1);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this,save2,new Object[]{j},saveSuper2);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
day-12
cglib避免反射调用
methodProxy如何避免反射调用
// return methodProxy.invoke(target,objects); //内部无反射,结合目标使用
return methodProxy.invokeSuper(p,objects); //内部无反射,结合代理
- 不会走反射,正常调用,关键在于内部会产生两个代理类
- 一个配合invoke和目标类使用
- 一个配合invokeSuper和代理对象使用
- 此处所说的代理和之前的代理并不一样
- 之前的代理是为了增强,而这里是为了避免反射调用,继承与fastClass
TargetFastClass 配合invoke和目标类使用的FastClass类
- 第一 定义三个方法的方法签名
- 方法名 返回类型 方法参数等
- 第二 根据签名获取方法编号
- 提前就定义好每个方法对应的编号
- 0 代表 save()方法
- 1 代表 save(int)方法
- 2 代表 save(long)方法
- 提前就定义好每个方法对应的编号
- 第三 根据方法编号调用方法
- 根据对应的方法编号直接调用对应的方法
public class TargetFastClass {
// 定义三个方法的方法签名
static Signature s0 = new Signature("save","()v");
static Signature s1 = new Signature("save","(I)v");
static Signature s2 = new Signature("save","(J)v");
// 获取目标方法的编号
/*
* Target
* save() 0
* save(int) 1
* save(long) 2
* 通过Signature(签名信息)获取编号,包括方法名,方法参数,返回值等
* */
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
}else if (s1.equals(signature)) {
return 1;
}else if (s2.equals(signature)){
return 2;
}
return -1;
}
// 根据返回的方法编号正常调用目标对象的方法
// 方法编号 目标对象 参数数组
public Object invoke (int index, Object target,Object[] args) {
// 调用无参无返回值的save()方法
if (index == 0) {
((Target)target).save();
return null;
// 调用int参数 无返回值的save(int)方法
}else if (index == 1) {
((Target)target).save((int)args[0]);
return null;
// 调用long参数 无返回值的save(long)方法
}else if(index == 2) {
((Target)target).save((long)args[0]);
return null;
}else {
throw new RuntimeException("无次方法");
}
}
}
模拟测试
public static void main(String[] args) {
TargetFastClass fastClass = new TargetFastClass();
int index = fastClass.getIndex(new Signature("save", "(I)V"));
System.out.println("index = " + index);
fastClass.invoke(index,new Target(),new Object[]{100});
}
测试结果
ProxyFastClass配合invokeSuper和代理类使用的FastClass类
- 在对方法进行调用的时候,只能调用原始功能的方法
- 如果调用增强功能的save,间接调用intercept,陷入死循环,只能调原始功能
public class ProxyFastClass {
// 定义三个方法的方法签名
static Signature s0 = new Signature("saveSuper","()V");
static Signature s1 = new Signature("saveSuper","(I)V");
static Signature s2 = new Signature("saveSuper","(J)V");
// 获取代理对象方法的编号 调用原始功能
// 如果调带增强功能的save,间接调用intercept,陷入死循环,只能调原始功能
/*
* Target
* saveSuper() 3
* saveSuper(int) 4
* saveSuper(long) 5
* 通过Signature(签名信息)获取编号,包括方法名,方法参数,返回值等
* */
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 3;
}else if (s1.equals(signature)) {
return 4;
}else if (s2.equals(signature)){
return 5;
}
return -1;
}
// 根据返回的方法编号正常调用目标对象的方法
// 方法编号 目标对象 参数数组
public Object invoke (int index, Object proxy,Object[] args) {
// 调用无参无返回值的save()方法
if (index == 3) {
((Proxy)proxy).saveSuper();
return null;
// 调用int参数 无返回值的save(int)方法
}else if (index == 4) {
((Proxy)proxy).saveSuper((int)args[0]);
return null;
// 调用long参数 无返回值的save(long)方法
}else if(index == 5) {
((Proxy)proxy).saveSuper((long)args[0]);
return null;
}else {
throw new RuntimeException("无此方法");
}
}
public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println("index = " + index);
fastClass.invoke(index,new Proxy(),new Object[0]);
}
}
与jdk相比
- jdk先调用16次,第17次针对一个方法产生一个代理类,让反射变成无需反射直接调用,cglib一调用就会产生代理,避免反射调用
- jdk优化是针对一个方法,而cglib,一个代理类会生成两个代理类对象(FastClass),可能匹配多个方法
day-13
spring选择代理
区分spring中的两种切面
在日常学习中,能接触到的切面就是 aspect切面,但是spring还存在一种切面为advisor切面
-
aspect切面,就是我们日常能够接触到的切面,由很多的通知+切点组成
-
aspect = 通知1(advice) + 切点1(pointcut) 通知2(advice) + 切点2(pointcut) 通知3(advice) + 切点3(pointcut)
-
//aspect切面的使用 @Aspect static class MyAspect { @Before("execution(* foo())") public void before() { System.out.println("前置通知"); } }
-
-
advisor切面是细粒度的切面,包含一个通知和切点,最终在生效执行之前,aspect会被拆解为多个advisor,advisor是更底层,更基本的切面
具体实现:
- 第一步 准备切点
- 底层的切点是通过接口来体现的,即springframework.aop下的PointCut接口
- 第二步 准备通知
- 最基础的通知MethodInterceptor,当前是aop下的,之前在cglib底层原理也使用过同名的包,但只是同名,这是一个最基础最重要的通知,可以说其他的通知最后都会被转变为MethodInterceptor
- 第三步 准备切面
- 第四步 创建代理
- 并不需要通过jdk或者cglib的形式来进行创建,spring提供了一个工厂叫ProxyFactory, 内部会根据不同的情况来选择是jdk的实现还是cglib的实现
- 需要设置的属性
- 目标
- 切面
- 是否实现接口
public class A15_1 {
interface I1 {
void foo();
void bar();
}
static class Target1 implements I1 {
@Override
public void foo() {
System.out.println("Target1.foo");
}
@Override
public void bar() {
System.out.println("Target1.bar");
}
}
static class Target2 {
public void foo() {
System.out.println("Target2.foo");
}
public void bar() {
System.out.println("Target2.bar");
}
}
public static void main(String[] args) {
/*
* 两个切面的概念
* aspect =
* 通知1(advice) + 切点1(pointcut)
* 通知2(advice) + 切点2(pointcut)
* 通知3(advice) + 切点3(pointcut)
* ...
* advisor = 更细粒度的切面,包含一个通知和切点
* */
// 1.准备切点
// 切点是根据springframework.aop下的PointCut接口来体现的
// 当前选择最为常见的->根据表达式来匹配
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2.准备通知
// 当前介绍一个最基本最重要的通知,可以说其他类型的通知最终都会被转化为这个通知
// MethodInterceptor,当前是aop下的,之前在cglib底层原理也使用过同名的包,但是只是同名
MethodInterceptor advice = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before");
// 表示调用目标
Object result = invocation.proceed();
System.out.println("after");
return result;
}
};
// 3.准备切面
// 因为只需要准备一个切点一个通知,所以选择一个较为简洁的切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
// 4.创建代理
// 并不需要使用jdk或者cglib的形式来创建,提供了一个工厂
// 内部会根据不同的情况来选择是jdk的实现还是cglib的实现
ProxyFactory factory = new ProxyFactory();
// 需要设置的属性 目标 切面
// 调用getProxy方法来创建
factory.setTarget( new Target1());
factory.addAdvisor(advisor);
// Target1实现了I1接口,使用接口类型
I1 proxy = (I1) factory.getProxy();
System.out.println("proxy = " + proxy.getClass());
proxy.bar();
proxy.foo();
}
}
运行结果:
结果显示,当前只有foo方法被增强,并且当前的代理是cglib类型的代理
spring选择代理
ProxyFactory会去读取父类proxyConfig的成员变量proxyTargetClass ,默认值是false,根据这个属性,spring创建代理会存在三种情况:
- proxyTargetClass = false,目标实现了接口 ,用jdk实现
- proxyTargetClass = false,目标没有实现接口,用cglib实现
- proxyTargetClass = true,无论有没有实现接口,总是使用cglib
上图的运行结果,虽然实现了接口,但是还是cglib类型的原因是,spring需要手动去设置属性,否则认为没有接口,即通过factory.setInterfaces(new Target1().getClass().getInterfaces()); 来实现
如果设置了属性 factory.setProxyTargetClass(true); 那么无论有没有实现接口,最后的结果都会采用cglib实现
## day-14
切点匹配
常见切点匹配实现
利用aspectJ来创建切点,用setExpression方法来进行表明规则
常见的两种切点匹配:
- 根据表达式进行切点匹配
- 根据注解进行切点匹配
match方法可以对结果进行判断,如果符合匹配规则,那么就会返回true,如果不满足匹配规则,那么就会返回false
public class PoMa {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// 根据表达式进行判断
pointcut.setExpression("execution(* bar())");
// 判断是否匹配,如果匹配,返回true,如果不匹配,返回false
System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));
AspectJExpressionPointcut pointcut1 = new AspectJExpressionPointcut();
// 根据注解来进行判断
pointcut1.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pointcut1.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut1.matches(T1.class.getMethod("bar"), T1.class));
}
static class T1{
@Transactional
public void foo() {
}
public void bar() {
}
}
}
但是像@Transactional还有很多的用法,可以加在方法上、类上、接口上,所以spring底层对于注解类型的编写并不简简单单是上面的实现方式,因此也就不能使用AspectJExpressionPointcut来构造切点
通过StaticMethodMatcherPointcut来构建,由于类是够抽象类,需要对里面的方法进行实现, MergedAnnotations.from方法来进行指定查找,是需要匹配类上的注解还是需要匹配方法上的注解
默认策略,只会查找本类上有没有查找相关注解
mergedAnnotations = MergedAnnotations.from(targetClass);
增加一个属性表明可以从相关类上进行查找
mergedAnnotations = MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
public class PoMa {
public static void main(String[] args) throws NoSuchMethodException {
StaticMethodMatcherPointcut pointcut2 = new StaticMethodMatcherPointcut() {
@Override
// 第一个参数 方法对象 第二个参数 目标类型(方法所在的类,比如T1)
public boolean matches(Method method, Class<?> targetClass) {
// 可以利用反射的方式来寻找方法,以及目标类,当前使用spring封装的方法来进行判断
// 检查方法上是否加了Transactional注解
MergedAnnotations mergedAnnotations = MergedAnnotations.from(method);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
// 查看类上是否加了Transactional注解
mergedAnnotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pointcut2.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pointcut2.matches(T3.class.getMethod("foo"), T3.class));
}
static class T1{
@Transactional
public void foo() {
}
public void bar() {
}
}
@Transactional
// 表明当前类的所有方法都需要事物增强
static class T2 {
public void foo() {
}
}
@Transactional
// 接口中定义的方法,实现后也会具有事物增强的效果
interface I3 {
public void foo();
}
static class T3 implements I3 {
@Override
public void foo() {
}
}
}
day-14
切点匹配
常见切点匹配实现
利用aspectJ来创建切点,用setExpression方法来进行表明规则
常见的两种切点匹配:
- 根据表达式进行切点匹配
- 根据注解进行切点匹配
match方法可以对结果进行判断,如果符合匹配规则,那么就会返回true,如果不满足匹配规则,那么就会返回false
public class PoMa {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// 根据表达式进行判断
pointcut.setExpression("execution(* bar())");
// 判断是否匹配,如果匹配,返回true,如果不匹配,返回false
System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));
AspectJExpressionPointcut pointcut1 = new AspectJExpressionPointcut();
// 根据注解来进行判断
pointcut1.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pointcut1.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut1.matches(T1.class.getMethod("bar"), T1.class));
}
static class T1{
@Transactional
public void foo() {
}
public void bar() {
}
}
}
但是像@Transactional还有很多的用法,可以加在方法上、类上、接口上,所以spring底层对于注解类型的编写并不简简单单是上面的实现方式,因此也就不能使用AspectJExpressionPointcut来构造切点
通过StaticMethodMatcherPointcut来构建,由于类是够抽象类,需要对里面的方法进行实现, MergedAnnotations.from方法来进行指定查找,是需要匹配类上的注解还是需要匹配方法上的注解
默认策略,只会查找本类上有没有查找相关注解
mergedAnnotations = MergedAnnotations.from(targetClass);
增加一个属性表明可以从相关类上进行查找
mergedAnnotations = MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
public class PoMa {
public static void main(String[] args) throws NoSuchMethodException {
StaticMethodMatcherPointcut pointcut2 = new StaticMethodMatcherPointcut() {
@Override
// 第一个参数 方法对象 第二个参数 目标类型(方法所在的类,比如T1)
public boolean matches(Method method, Class<?> targetClass) {
// 可以利用反射的方式来寻找方法,以及目标类,当前使用spring封装的方法来进行判断
// 检查方法上是否加了Transactional注解
MergedAnnotations mergedAnnotations = MergedAnnotations.from(method);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
// 查看类上是否加了Transactional注解
mergedAnnotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pointcut2.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pointcut2.matches(T3.class.getMethod("foo"), T3.class));
}
static class T1{
@Transactional
public void foo() {
}
public void bar() {
}
}
@Transactional
// 表明当前类的所有方法都需要事物增强
static class T2 {
public void foo() {
}
}
@Transactional
// 接口中定义的方法,实现后也会具有事物增强的效果
interface I3 {
public void foo();
}
static class T3 implements I3 {
@Override
public void foo() {
}
}
}
day-15
从@Aspect 到 Advisor
准备环境
- 高级切面
- 高级切面准备有多组通知和切点
- 前置通知 @before
- 后置通知 @After
- 高级切面准备有多组通知和切点
- 低级切面
- 低级切面只有一组通知和切点,并且低级通知以配置的形式出现(当前环境是以配置形式出现),具有两个bean
- 第一个bean是准备切面,传入两个参数,切点和通知
- 第二个bean是准备通知,因为稍复杂,所以用bean的形式将通知作为参数传递进切面bean
- 低级切面只有一组通知和切点,并且低级通知以配置的形式出现(当前环境是以配置形式出现),具有两个bean
public class T {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1",Aspect1.class);
context.registerBean("config",Config.class);
//添加后处理器处理@bean
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name :
context.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
}
static class Target1 {
public void foo() {
System.out.println("Target1.foo");
}
}
static class Target2 {
public void bar() {
System.out.println("Target2.bar");
}
}
// 高级切面
@Aspect
static class Aspect1 {
// 多组通知和切点
@Before("execution(* foo())")
public void before() {
System.out.println("Aspect1.before");
}
@After("execution(* foo())")
public void after() {
System.out.println("Aspect1.after");
}
}
// 低等级切面,以配置类的形式出现
// 右一个切点和一个通知组成
// 通知采用的是环绕通知,以bean的形式作为参数传递
@Configuration
static class Config {
@Bean
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 切面,传入切点还有通知
return new DefaultPointcutAdvisor(pointcut,advice3);
}
@Bean
public MethodInterceptor advice3() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("advice3.before");
Object result = invocation.proceed();
System.out.println("advice3.after");
return result;
}
};
}
}
}
后处理器AnnotationAwareAspectJAutoProxyCreator
由于两个重要方法是protected类型的,所以把方法放在同包下,能够直接调用,当然也可以用反射等方法调用
重要方法一:findEligibleAdvisors
重要作用:
- 作用1:寻找容器中所有切面,如果是高级切面,转换为低级切面
- 作用2:根据切面创建代理对象,调用ProxyFactory来创建代理对象
寻找对象:
- 对象1:有【资格】的Advisor 一部分是低级的,可以由自己编写,如advisor3
- 对象2:有【资格】的Advisor 另一部分是高级的,解析@Aspect后获得
// BeanPostProcessor
// 创建 -> 依赖注入 ->初始化
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
context.refresh();
for (String name :
context.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
生成四个切面
第一个切面是由spring提供的切面,第二个切面是低级切面,第三个和第四个是由高级切面转换后得到的低级切面
重要方法二:wrapIfNecessary
重要作用:
- 判断是否有必要为目标创建代理
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());
o1生成代理对象,o2不生成代理对象
代理创建时机
public class T_1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
context.close();
// 创建 ->(*)依赖注入 -> 初始化(*)
}
@Configuration
static class Config {
@Bean // 解析 @Aspect、产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean // 解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}
@Bean // 解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
public void foo() {
}
public Bean1() {
System.out.println("Bean1()");
}
@Autowired public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean1 init()");
}
}
static class Bean2 {
public Bean2() {
System.out.println("Bean2()");
}
@Autowired public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean2 init()");
}
}
}
总结:
- 代理创建时机
- 初始化之后(没有循环依赖)
- 实例创建后,依赖注入前(有循环依赖时),并暂存于二级缓存
- 依赖注入与初始化不应该被增强,仍应被施加于原始对象
@Order注解
-
@Order注解可以指定先后顺序,但是如果加在@Bean上没有任何效果,以之前的例子来指定先后顺序,如
-
@Bean @Order(1) //这样使用,order并不会有任何效果
-
高级切面上使用 @Order(1)来进行指定
-
低级切面上,DefaultPointcutAdvisor实现了oreder接口,可以调用set方法来进行指定
-
-
对于高级切面来说,其中的方法是同一个优先级的,如果想给每个方法进行单独设置@Order,那么也不会生效
-
当前情况,两个方法优先级一致
-
// 高级切面 @Aspect @Order(1) static class Aspect1 { // 多组通知和切点 @Before("execution(* foo())") public void before1() { System.out.println("Aspect1.before1"); } @Before("execution(* foo())") public void before2() { System.out.println("Aspect1.before2"); } }
-
-
对每个方法单独进行添加,不会生效,即
-
// 高级切面 @Aspect static class Aspect1 { // 多组通知和切点 @Before("execution(* foo())") @Order(2) public void before1() { System.out.println("Aspect1.before1"); } @Before("execution(* foo())") @Order(1) public void before2() { System.out.println("Aspect1.before2"); } }
-
-
-
加载类上,可以和其他的切面方法作比较,提高优先级,加载方法上不会产生任何的作用效果
高级切面转低级切面
- 找到切面类以后,拿到对应的方法
- 以@Befor为例,如果方法有注解修饰,拿到切点表达式
- 创建一个切点,并传递切点表达式
- 创建一个通知,AspectJMethodBeforeAdvice为前置通知类,传递三个参数,方法、切点、工厂
- 创建一个切面,并加入到集合当中收集起来
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
public void afterReturning() {
System.out.println("afterReturning");
}
public void afterThrowing() {
System.out.println("afterThrowing");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
- @Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
- a. 通知代码从哪儿来
- b. 切点是什么(这里为啥要切点, 后面解释)
- c. 通知对象如何创建,本例共用同一个 Aspect 对象
- 类似的通知还有
- AspectJAroundAdvice (环绕通知)
- AspectJAfterReturningAdvice
- AspectJAfterThrowingAdvice
- AspectJAfterAdvice (环绕通知)
day-16
统一转换为环绕通知
不管是前置通知,还是后置通知,最后都会统一转换为MethodInterceptor
其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象,如图,在执行的时候,会先去执行advice1的前置通知before1,然后去执行advice2的before2,然后逐层执行,最后才到after1,因此当前最适合的,一层一层包裹起来的,其实是环绕通知
- a. 因为 advice有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
- b. MethodInvocation 要知道 advice 有哪些, 还要知道目标
- c. 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知
- d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式
- 对外是为了方便使用要区分 before、afterReturning
- 对内统一都是环绕通知, 统一用 MethodInterceptor 表示
将 MethodInvocation 放入当前线程
|-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
| |
| |-> before2 -------------------- | 从当前线程获取 MethodInvocation
| | | |
| | |-> target ------ 目标 advice2 advice1
| | | |
| |-> after2 --------------------- |
| |
|-> after1 ------------------------------------
package org.springframework.aop.framework;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class A18 {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("afterReturning");
}
@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing " + e.getMessage());
}
@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 1. 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(Around.class)) {
// 解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 创建并执行调用链 (环绕通知s + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
methodInvocation.proceed();
}
}
总结:
学到了什么
a. 无参数绑定的通知如何被调用
b. MethodInvocation 编程技巧: 拦截器、过滤器等等实现都与此类似
c. 适配器模式在 Spring 中的体现