spring高级篇

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来完成
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的后处理器

  • 执行初始化单例

如下图所示:

  1. 执行容器内的beanFactory后处理器
  2. 注册bean后处理器
  3. 创建单例,初始化
    1. 执行依赖注入,回调bean后处理器解析 @Value @Autowired
    2. 初始化 扩展
    3. 执行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});
    }

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7aOkZCRQ-1670660760848)(spring%E9%AB%98%E7%BA%A7%E7%AF%87%20day%2010-20.assets/1670659231007.png)]



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
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 中的体现

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值