Spring框架学习笔记(四):手动实现 Spring 底层机制(初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP)

Spring 整体架构示意图

阶段 -- 编写自己的 Spring 容器,实现多层扫描包

编写自己的 Spring 容器,实现多层扫描包,排除包下不是bean的, 得到 bean 对象,放入到临时ioc容器中

代码实现:

(1)准备代码 创建两个注解ComponentScan.java、Component.java,ComponentScan.java注解用于指定需要扫描的包,Component.java用于标识bean,即加了该注解的类就是bean,没加就不是

package com.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {//表示ComponentScan注解可以传入一个value属性,指定要扫描的包String value() default "";
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {//表示Component注解可以传入一个value属性,指定id值String value() default "";
}

(2)创建配置类 SpringConfigXml 用来替代xml文件,创建 MonsterService.java、MonsterDao.java用来作为需要放入容器中的bean

import com.spring.annotation.ComponentScan;/*** 这是一个配置类,用来替代xml文件*/
@ComponentScan(value = "com.spring")
public class SpringConfigXml {
}
import com.spring.annotation.Component;@Component(value = "monsterDao")
public class MonsterDao {
}
import com.spring.annotation.Component;/*** MonsterService 是一个 Service* 1.如果指定value,注入容器时,以指定的value值为准* 2.如果没用指定value,则默认为类名首字母小写*/
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService {
}

(3)创建 SpringApplicationContext.java 充当容器类

import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** 该类作用类似于Spring原生ioc容器*/
@SuppressWarnings("all")
public class SpringApplicationContext {//拿到配置类.class文件private Class configClass;//ioc存放的就是通过反射创建的对象private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();//构造器public SpringApplicationContext(Class configClass) {this.configClass = configClass;
//        System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先获取到ComponentScan注解ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.获取到注解的value值String path = componentScan.value();
//        System.out.println("要扫描的包= " + path);//得到要扫描的包下的所有class文件(在target目录下)//1.得到类的加载器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描的包的资源路径path = path.replace(".", "/");//一定要把.替换成/URL resource = classLoader.getResource(path);
//        System.out.println(resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());readAllFiles(file, path, classLoader);}//递归遍历所有包,实现扫描多层包结构public void readAllFiles(File file, String path, ClassLoader classLoader) {if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {if (f.isFile()) {dealFile(f,path,classLoader);} else if (f.isDirectory()) {readAllFiles(f, path, classLoader);}}} else if (file.isFile()) {dealFile(file,path,classLoader);}}public void dealFile(File f, String path, ClassLoader classLoader){//获取到.class文件的绝对路径String fileAbsolutePath = f.getAbsolutePath();//这里只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取到类全类名//1.获取到类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//                System.out.println(className);//2.拼接成全类名String temp = fileAbsolutePath.replace("\\","/");String classFullName = temp.substring(temp.indexOf(path),temp.indexOf(".class"));classFullName = classFullName.replace("/",".");
//                    System.out.println(classFullName);//3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controllertry {//这时,就得到了该类的Class对象//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 也可以反射类的Class//3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);//判断该类是否有 @Component @Service @Repository @Controllerif (aClass.isAnnotationPresent(Component.class)) {//org.springframework.util.StringUtils包中的静态方法uncapitalize可以将字符串首字母小写
//                            String keyVal = StringUtils.uncapitalize(className);String keyVal = className.substring(0, 1).toLowerCase() + className.substring(1);//获取到value值,替换指定idif (aClass.isAnnotationPresent(Component.class)) {Component annotation = aClass.getDeclaredAnnotation(Component.class);String value = annotation.value();if (!value.equals("")) {keyVal = value;}}//这时候就可以创建该类的对象,并放入到ioc容器中Class<?> clazz = Class.forName(classFullName);Object o = clazz.newInstance();ioc.put(keyVal, o);}} catch (Exception e) {e.printStackTrace();}}}//通过id获取到bean对象public Object getBean(String id){return ioc.get(id);}
}

阶段 2 -- 将bean信息封装到BeanDefinition对象中,并放入到 Map,初始化单例池

将bean信息封装到BeanDefinition对象中,并放入到 Map,初始化单例池,修改getbean方法,如果是单例返回单例池中的对象,如果是多例则临时创建一个对象

 代码实现:

(1)创建注解 Scope.java 用来标识bean为单例还是多例 ,prototype为多例,singleton为单例

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value(); //不需要给默认值
}

 (2)修改 MonsterService.java ,增加Scope注解,用于测试

@Scope(value = "prototype")
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService {
}

(3)BeanDefinition.java,封装bean对象

/*** 1. 在扫描时,将 bean 信息,封装到 BeanDefinition 对象中* 2. 然后将 BeanDefinition 对象 , 放入到 spring 容器的集合中
*/
public class BeanDefinition {private Class clazz;//bean对应的class对象private String scope;//bean的作用域,singleton/prototype
}

(4)SpringApplicationContext.java, 将扫描到的 Bean 信息封装到 BeanDefinition 对象中,并保存到 BeanDefinitionMap,实现getBean方法

import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** 该类作用类似于Spring原生ioc容器*/
@SuppressWarnings("all")
public class SpringApplicationContext {//拿到配置类.class文件private Class configClass;//如果对象是单例的,就直接放在这个 单例 bean 对象池private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//将 bean 的定义,放在这个 beanDefinitionMap 集合private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//构造器public SpringApplicationContext(Class configClass) {//通过扫描,得到 beanDefinition 的 mapbeanDefinitionsByscan(configClass);}private void beanDefinitionsByscan(Class configClass) {this.configClass = configClass;
//        System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先获取到ComponentScan注解ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.获取到注解的value值String path = componentScan.value();
//        System.out.println("要扫描的包= " + path);//得到要扫描的包下的所有class文件(在target目录下)//1.得到类的加载器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描的包的资源路径path = path.replace(".", "/");//一定要把.替换成/URL resource = classLoader.getResource(path);
//        System.out.println(resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());readAllFiles(file, path, classLoader);//通过beanDefinitionMap , 初始化singletonObjects 单例池//遍历所有的beanDefinition对象Enumeration<String> keys = beanDefinitionMap.keys();while (keys.hasMoreElements()){//得到beanNameString beanName = keys.nextElement();//通过beanName 得到对应的beanDefinition对象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判断该bean是singleton还是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//将该bean实例放入到singletonObjects 集合Object bean = createBean(beanDefinition);singletonObjects.put(beanName, bean);}}}//递归遍历所有包private void readAllFiles(File file, String path, ClassLoader classLoader) {if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {if (f.isFile()) {dealFile(f,path,classLoader);} else if (f.isDirectory()) {readAllFiles(f, path, classLoader);}}} else if (file.isFile()) {dealFile(file,path,classLoader);}}private void dealFile(File f, String path, ClassLoader classLoader){//获取到.class文件的绝对路径String fileAbsolutePath = f.getAbsolutePath();//这里只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取到类全类名//1.获取到类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//                System.out.println(className);//2.拼接成全类名String temp = fileAbsolutePath.replace("\\","/");String classFullName = temp.substring(temp.indexOf(path),temp.indexOf(".class"));classFullName = classFullName.replace("/",".");
//                    System.out.println(classFullName);//3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controllertry {//这时,就得到了该类的Class对象//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 也可以反射类的Class//3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);//判断该类是否有 @Componentif (aClass.isAnnotationPresent(Component.class)) {//org.springframework.util.StringUtils包中的静态方法uncapitalize可以将字符串首字母小写
//                            String keyVal = StringUtils.uncapitalize(className);String keyVal = className.substring(0, 1).toLowerCase() + className.substring(1);//获取到value值,替换指定idComponent annotation = aClass.getDeclaredAnnotation(Component.class);String value = annotation.value();if (!value.equals("")) {keyVal = value;}//这时候就可以创建该bean的定义对象Class<?> clazz = Class.forName(classFullName);BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//如果存在Scope注解,就设置该注解的值,如果不存在,则设为单例 singletonif (clazz.isAnnotationPresent(Scope.class)){Scope s = clazz.getDeclaredAnnotation(Scope.class);String val = s.value();beanDefinition.setScope(val);}else {beanDefinition.setScope("singleton");}//将 BeanDefinition 对象放入 map 集合中beanDefinitionMap.put(keyVal, beanDefinition);}} catch (Exception e) {e.printStackTrace();}}}//通过id获取到bean对象public Object getBean(String id){if (beanDefinitionMap.containsKey(id)) {BeanDefinition bean = beanDefinitionMap.get(id);String scope = bean.getScope();if (scope.equals("singleton")) {//如果是单例的,就直接从单例池中获取return singletonObjects.get(id);} else {//如果bean是多实例的,就临时创建一个对象Class clazz = bean.getClazz();return createBean(bean);}}else {throw new NullPointerException("没有该bean");}}private Object createBean(BeanDefinition bean){Class clazz = bean.getClazz();try {return clazz.newInstance();} catch (Exception e) {e.printStackTrace();}return null;}
}

(5)创建主程序,测试

public class AppMain {public static void main(String[] args) {SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");MonsterService monsterService2 = (MonsterService)ioc.getBean("monsterService");MonsterDao monsterDao = (MonsterDao)ioc.getBean("monsterDao");MonsterDao monsterDao1 = (MonsterDao)ioc.getBean("monsterDao");System.out.println(monsterService);System.out.println(monsterService2);System.out.println(monsterDao);System.out.println(monsterDao1);System.out.println("ok~");}
}

(6)运行效果

分析:可以看到,多例 bean 每次 getbean 返回的是不同的对象,而单例 bean 每次 getbean 返回的是同一个对象

4 阶段 3 -- 完成依赖注入

完成依赖注入

(1)Autowired.java注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Autowired 自动依赖注入*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Autowired {
}

(2)MonsterDao.java,MonsterService.java,加入 Autowired 注解

import com.spring.annotation.Component;@Component(value = "monsterDao")
public class MonsterDao {public void hi() {System.out.println("hi 我是 monster Dao");}
}
import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.Scope;/*** MonsterService 是一个 Service* 1.如果指定value,注入容器时,以指定的value值为准* 2.如果没用指定value,则默认为类名首字母小写*/
@Scope(value = "prototype")
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService {//表示该属性,是通过容器完成依赖注入//说明:这里只实现了按照属性名来进行组装@Autowiredprivate MonsterDao monsterDao;public void m1() {//调用 monsterDao 的 hi()monsterDao.hi();}
}

(3)SpringApplicationContext.java 类中的 createBean 方法,实现依赖注入

private Object createBean(BeanDefinition bean){Class clazz = bean.getClazz();try {Object instance = clazz.newInstance();//完成依赖注入Field[] fields = clazz.getDeclaredFields();//遍历当前要创建的对象的所有字段for (Field field : fields) {//判断这个字段是否有@Autowired注解if (field.isAnnotationPresent(Autowired.class)){//如果该属性有@Autowired, 就进行组装//得到这个字段名字String fieldName = field.getName();//因为属性是private,需要爆破field.setAccessible(true);//进行组装,通过getBean方法获取到要组装的对象field.set(instance, getBean(fieldName));}}return instance;} catch (Exception e) {e.printStackTrace();}return null;
}

(4)编写主程序,查看依赖是否注入成功

public class AppMain {public static void main(String[] args) {SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");monsterService.m1();System.out.println("ok~");}
}

(5)运行效果

依赖注入成功!

5 阶段 4 -- bean后置处理器实现

实现自己的 bean 后置处理器

(1)InitializingBean.java 接口, 实现该接口的 Bean , 需要实现 Bean 初始化方法

/*** 1. 根据原生Spring 定义了一个InitializingBean* 2. 该InitializingBean接口有一个方法void afterPropertiesSet() throws Exception;* 3. afterPropertiesSet() 在Bean的 setter后执行,即就是初始化方法* 4. 当一个Bean实现这个接口后,就实现afterPropertiesSet() , 这个方法就是初始化方法*/
public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

(2)修改MonsterService.java、MonsterDao.java, 实现 InitializingBean 接口

import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.Scope;
import com.spring.processor.InitializingBean;/*** MonsterService 是一个 Service* 1.如果指定value,注入容器时,以指定的value值为准* 2.如果没用指定value,则默认为类名首字母小写*/
@Scope(value = "prototype")
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService implements InitializingBean {//表示该属性,是通过容器完成依赖注入//说明:这里只实现了按照属性名来进行组装@Autowiredprivate MonsterDao monsterDao;public void m1() {//调用 monsterDao 的 hi()monsterDao.hi();}/*** afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用* 即就是初始化方法*/@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");}
}
import com.spring.annotation.Component;
import com.spring.processor.InitializingBean;@Component(value = "monsterDao")
public class MonsterDao implements InitializingBean {public void hi() {System.out.println("hi 我是 monster Dao");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("MonsterDao 初始化方法被调用...");}
}

(3)SpringApplicationContext.java 类中的createBean方法 , 在创建好 Bean 实例后,判断是否需要进行初始化

private Object createBean(BeanDefinition bean){Class clazz = bean.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//完成依赖注入Field[] fields = clazz.getDeclaredFields();//遍历当前要创建的对象的所有字段for (Field field : fields) {//判断这个字段是否有@Autowired注解if (field.isAnnotationPresent(Autowired.class)){//如果该属性有@Autowired, 就进行组装//得到这个字段名字String fieldName = field.getName();//因为属性是private,需要爆破field.setAccessible(true);//进行组装,通过getBean方法获取到要组装的对象field.set(instance, getBean(fieldName));}}System.out.println("====创建好实例=====" + instance);//这里判断是否要执行Bean初始化方法//1. 判断当前创建的Bean对象是否实现了InitializingBean//2. instanceof 可以用来判断某个对象的运行类型是不是某个类型或者//  某个类型的子类型if (instance instanceof InitializingBean){// 转型成接口类型调用方法((InitializingBean) instance).afterPropertiesSet();// 也可以使用反射调用
//                Method initMethod = clazz.getDeclaredMethod("afterPropertiesSet");
//                initMethod.invoke(instance);}return instance;} catch (Exception e) {e.printStackTrace();}return null;
}

(4)编写主程序,测试

public class AppMain {public static void main(String[] args) {SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");System.out.println("ok~");}
}

(5)运行效果

成功实现初始化方法!

(6)现在开始实现后置处理器,BeanPostProcessor.java 接口,创建MyBeanPostProcessor.java 类实现该接口,增加一个Car类,用于测试

/*** 1. 参考原生Spring容器定义一个接口BeanPostProcessor* 2. 该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization* 3. 这两个方法,会对Spring容器的所有Bean生效*/
public interface BeanPostProcessor {/*** postProcessBeforeInitialization在Bean的初始化方法前调用*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** postProcessAfterInitialization在Bean的初始化方法后调用*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}
package com.spring.component;import com.spring.annotation.Component;
import com.spring.processor.InitializingBean;@Component
public class Car implements InitializingBean {String name = "小黄";@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Car的初始化方法..");}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
import com.spring.annotation.Component;
import com.spring.processor.BeanPostProcessor;/*** 1. 这是我们自己的一个后置处理器* 2. 实现了BeanPostProcessor* 3. 我们可以重写before和after方法* 4. 在Spring容器中,仍然把HspBeanPostProcessor当做一个Bean对象, 要注入到容器* 5. 需要加上@Component 标识* 6. 我们要让HspBeanPostProcessor成为真正的后置处理器, 需要在容器中加入业务代码* 7. 还要考虑多个后置处理器对象注入到容器问题*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {//后置处理器会对所有的bean生效,这里就可以对Car对象进行单独的处理if (bean instanceof Car){System.out.println("这是一个Car对象,我可以处理");}System.out.println("后置处理器HspBeanPostProcessor Before调用 bean类型="+ bean.getClass() + " bean的名字=" + beanName);return null;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置处理器HspBeanPostProcessor After调用 bean类型="+ bean.getClass() + " bean的名字=" + beanName);return bean;}
}

(7)修改 SpringApplicationContext.java 容器类

添加属性,存放后置处理器对象

//定义一个属性beanPostProcessorList, => 存放后置处理器
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

修改dealFile方法,在以下位置加入以下代码,将后置处理器对象放入集合中

//判断该类是否有 @Component
if (aClass.isAnnotationPresent(Component.class)) {//1. 为了方便,这里将后置处理器放入到一个ArrayList//2. 如果发现是一个后置处理器, 放入到 beanPostProcessorList//3. 在原生的Spring容器中, 对后置处理器还是走的getBean, createBean//   , 但是需要我们在singletonObjects 加入相应的业务逻辑,这里进行了简化//判断当前的这个class对象有没有实现BeanPostProcessor//说明, 这里我们不能使用 instanceof 来判断clazz是否实现了BeanPostProcessor//原因: clazz不是一个实例对象,而是一个类对象/clazz, 可以使用isAssignableFromif (BeanPostProcessor.class.isAssignableFrom(aClass)) {BeanPostProcessor beanPostProcessor =(BeanPostProcessor) aClass.newInstance();//放入到beanPostProcessorListbeanPostProcessorList.add(beanPostProcessor);return;}。。。。。
}

修改create方法,增加一个参数beanName,并在初始化方法前后调用后置处理器方法

private Object createBean(BeanDefinition bean, String beanName)

create方法修改部分代码如下

System.out.println("====创建好实例=====" + instance);//在Bean的初始化方法前,调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置处理器的before方法中,可以对容器的bean实例进行处理//然后返回处理后的实例Object current = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);//这样处理,如果返回null,对bean对象不会造成影响,后置处理器中对bean做出的修改依然会生效if (current != null){instance = current;}
}//这里判断是否要执行Bean初始化方法
//1. 判断当前创建的Bean对象是否实现了InitializingBean
//2. instanceof 可以用来判断某个对象的运行类型是不是某个类型或者
//  某个类型的子类型
if (instance instanceof InitializingBean){// 转型成接口类型调用方法((InitializingBean) instance).afterPropertiesSet();// 也可以使用反射调用// Method initMethod = clazz.getDeclaredMethod("afterPropertiesSet");//  initMethod.invoke(instance);
}//在Bean的初始化方法后,调用后置处理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置处理器的after方法中,可以对容器的bean实例进行处理//然后返回处理后的实例Object current = beanPostProcessor.postProcessAfterInitialization(instance,beanName);//这样处理,如果返回null,对bean对象不会造成影响if (current != null){instance = current;}
}

(8)编写主类测试

package com.spring;import com.spring.component.MonsterService;
import com.spring.ioc.SpringApplicationContext;
import com.spring.ioc.SpringConfigXml;public class AppMain {public static void main(String[] args) {SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");System.out.println("ok~");}
}

(9)运行结果

后置处理器成功作用在所有bean上

6 阶段 5 -- AOP机制实现

实现一个简单的AOP,这里做了简化,使用的硬编码,写死了

(1)创建注解Aspect.java,Before.java,AfterReturning.java

package com.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {String value() default "";
}
package com.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {String value();String argNames() default "";
}
package com.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {String value() default "";String pointcut() default "";String returning() default "";String argNames() default "";
}

(2)创建接口 SmartAnimalable.java,实现类 SmartDog.java,切面类 SmartAnimalAspect.java

package com.spring.component;@SuppressWarnings("all")
public interface SmartAnimalable {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}
package com.spring.component;import com.spring.annotation.Component;@Component
public class SmartDog implements SmartAnimalable {@Overridepublic float getSum(float i, float j) {float result = i + j;System.out.println("SmartDog-getSum方法内部打印result = " + result);return result;}@Overridepublic float getSub(float i, float j) {float result = i - j;System.out.println("SmartDog-getSub方法内部打印result = " + result);return result;}
}
package com.spring.component;import com.spring.annotation.AfterReturning;
import com.spring.annotation.Aspect;
import com.spring.annotation.Before;
import com.spring.annotation.Component;/*** SmartAnimalAspect当做一个切面类来使用*/
@Aspect
@Component
public class SmartAnimalAspect {@Before(value = "execution com.spring.aop.aspectj.SmartDog getSum")public static void showBeginLog() {System.out.println("前置通知..");}@AfterReturning(value = "execution com.spring.aop.aspectj.SmartDog getSum")public static void showSuccessLog() {System.out.println("返回通知..");}
}

(3)修改 MyBeanPostProcessor.java 类中的 postProcessAfterInitialization方法,实现AOP,返回代理对象

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置处理器HspBeanPostProcessor After调用 bean类型="+ bean.getClass() + " bean的名字=" + beanName);//实现AOP,返回代理对象,即对Bean进行包装if ("smartDog".equals(beanName)){//使用jdk的动态代理,返回bean的代理对象Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SmartAnimalAspect.showBeginLog();Object res = method.invoke(bean,args);SmartAnimalAspect.showSuccessLog();return res;}});return proxyInstance;}return bean;
}

(4)创建 AOPTest.java

package com.spring;import com.spring.annotation.AfterReturning;
import com.spring.annotation.Before;
import com.spring.component.SmartAnimalAspect;import java.lang.reflect.Method;public class AOPTest {public static void main(String[] args) throws Exception {//1. 获取SmartAnimalAspect的class对象Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;//2. 遍历该类的所有方法for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {//如果切面类的方法有Before注解if (declaredMethod.isAnnotationPresent(Before.class)) {//得到切面类的切入方法名System.out.println("m:= " + declaredMethod.getName());//得到Before(value="xxxx")//得到Before注解Before annotation = declaredMethod.getAnnotation(Before.class);//得到Before注解的valueSystem.out.println("value:= " + annotation.value());//调用切入方法[通过反射调用]declaredMethod.invoke(smartAnimalAspectClass.newInstance(), null);} else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {//如果发现切面类有AfterReturning注解,同样可以进行处理..System.out.println("m:= " + declaredMethod.getName());AfterReturning annotation = declaredMethod.getAnnotation(AfterReturning.class);System.out.println("value:= " + annotation.value());//调用切入方法[反射调用]declaredMethod.invoke(smartAnimalAspectClass.newInstance(), null);}}}
}

(5)运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/15279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OSM最新台湾电力数据(2024年5月23日数据)转换总结及与(2024年2月16日数据)转换结果对比

osm包含种类繁多&#xff0c;我们这里是只以刚转换的台湾电力设备为例抛砖引玉的进行说明。本次仅仅转换了&#xff0c;变电站、电厂和线路杆塔数据&#xff01; 这种带增强的文件&#xff0c;是我在基础规则之外增加的提取规则。是为了更多的将OSM相关类型数据过滤提取出来&am…

干货 | 2024 EISS 企业信息安全高峰论坛(脱敏)PPT(7份可下载)

2024 EISS 企业信息安全高峰论坛&#xff08;脱敏&#xff09;PPT&#xff0c;共7份。 AI在出海业务的安全实践.pdf Palo Alto Networks为中国企业全球化布局保驾护航.pdf 安全建设与治理思路.pdf 车路云一体化安全体系建设实践.pdf 企业研发安全DevSecOps流程落地实践.pdf 浅谈…

c++入门的基础知识

c入门 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff1a; 补充C语言语法的不足&#xff0c;以及C是如何对C语言设计…

2024年收集搜索引擎蜘蛛大全以及浏览器模拟蜘蛛方法

对于做SEOer来说经常和搜索引擎蜘蛛打交道&#xff0c;下面整理收集了最全的搜索引擎蜘蛛大全。供有需要的朋友使用&#xff0c;建议收藏。 搜索引擎蜘蛛大全 "TencentTraveler", "Baiduspider", "BaiduGame", "bingbot",//必应蜘蛛…

uni-app App端实现文字语音播报(Ba-TTS)

前言 最近在遇到消息提示语音播放出来&#xff0c;查了一圈文档发现并没有自带api 后面想起支付宝收钱播报&#xff0c;不受限与系统环境和版本环境&#xff08;后面查阅他是音频实现的&#xff09; 如果是由安卓端需要语音播放功能-直接使用Ba-TTs救急&#xff08;需要付费2…

详解CSS(一)

目录 1.CSS是什么 2.基本语法规范 3.引入方式 3.1内部样式表 3.2行内样式表 3.3外部样式表 4.选择器 4.1基础选择器 4.1.1标签选择器 4.1.2类选择器 4.1.3id选择器 4.1.4通配符选择器 4.2复合选择器 4.2.1后代选择器 4.2.2子选择器 4.2.3并集选择器 4.2.4伪类选择…

【通义千问—Qwen-Agent系列3】案例分析(五子棋游戏多Agent冒险游戏多智能体群组交流)

目录 前言一、快速开始1-1、介绍1-2、安装1-3、开发你自己的Agent 二、基于Qwen-Agent的案例分析2-0、环境安装2-1、五子棋游戏实现2-2、多Agent冒险游戏2-N、 多智能体群组交流&#xff08;太酷啦&#xff09; 附录1、agent源码2、router源码 总结 前言 Qwen-Agent是一个开发…

DataGear 制作服务端分页的数据可视化看板

DataGear 2.3.0 版本新增了附件图表数据集特性&#xff08;在新建图表时将关联的数据集设置为 附件 &#xff0c;具体参考官网文档定义图表章节&#xff09;&#xff0c;在制作看板时&#xff0c;可以基于此特性&#xff0c;结合dg-chart-listener&#xff0c;利用服务端数据扩…

基于高通公司AI Hub Models的On-Device AI学习:Introduction to On-Device AI

Introduction to On-Device AI 本文是学习 https://www.deeplearning.ai/short-courses/introduction-to-on-device-ai/这门课的学习笔记。 What you’ll learn in this course As AI moves beyond the cloud, on-device inference is rapidly expanding to smartphones, IoT…

MySQL 带游标的存储过程(实验报告)

一、实验名称&#xff1a; 带游标的存储过程 二、实验日期&#xff1a; 2024 年 5月 25 日 三、实验目的&#xff1a; 掌握MySQL带游标的存储过程的创建及调用&#xff1b; 四、实验用的仪器和材料&#xff1a; 硬件&#xff1a;PC电脑一台&#xff1b; 配置&#xff1…

[OpenGL] opengl切线空间

目录 一 引入 二 TBN矩阵 三 代码实现 3.1手工计算切线和副切线 3.2 像素着色器 3.3 切线空间的两种使用方法 3.4 渲染效果 四 复杂的物体 本章节源码点击此处 继上篇法线贴图 来熟悉切线空间是再好不过的。对于法线贴图来说,我们知道它就是一个2D的颜色纹理,根据rgb…

使用DataGrip连接Elasticsearch

使用DataGrip连接Elasticsearch 前言&#xff0c;公司需要使用ES来做数据的查询&#xff0c;我安装完ES&#xff0c;安装完Kibana的时候&#xff0c;想先开始尝试一下&#xff0c;插入查询数据能否可用&#xff0c;但是上次使用ES是好久前了&#xff0c;增删改查的请求根本记不…

利用sql注入对某非法网站的渗透

本文仅用于技术讨论&#xff0c;切勿用于违法途径&#xff0c;且行且珍惜&#xff0c; 所有非经授权的渗透&#xff0c;都是违法行为 前言 这段时间一直在捣鼓sql注入&#xff0c;最近又通过一个sql注入点&#xff0c;成功进入某个非法网站的后台&#xff0c;拿到整个网站的…

Liunx基本指令以及权限(个人笔记)

Linux指令和权限 1.指令1.1ls指令1.2pwd命令1.3cd指令1.4touch指令1.5mkdir指令1.6rm指令1.7man指令1.8cp指令1.9mv指令1.10cat指令1.11less指令1.12head指令1.13tail指令1.14date显示1.15Cal指令1.16find指令1.17grep指令1.18zip/unzip指令1.19tar指令1.20bc指令1.21uname -r指…

【Tools】微服务工程中的通用功能模块抽取

Catalog 通用功能模块抽取一、需求二、步骤三、细节 通用功能模块抽取 一、需求 在微服务工程中&#xff0c;可能有一些工具类、实体类是多个微服务通用的&#xff0c;如果在每个微服务中都复制粘贴这些工具类&#xff0c;会产生很多重复性的代码&#xff0c;对开发来说也很繁…

uniapp+php服务端实现苹果iap内购的消耗性项目和非续期订阅项目,前后端代码加逻辑分析

前言&#xff1a;公司的项目app在上架苹果商店时发现人家要求里面的部分购买项目必须使用iap购买的方式&#xff0c;使用原本的微信支付方式审核不给通过&#xff0c;无奈只能重新研究这个东西。做起来还是有点麻烦&#xff0c;主要是网上的文章很少&#xff0c;不能直接硬抄。…

C语言笔记20 •整数和浮点数在内存中存储•

整数和浮点数在内存中存储 1.整数在内存中存储 整数在内存中存储比较简单&#xff0c;整数存储分为正整数存储和负整数存储。 对于有符号整数 符号位中0表示正整数&#xff0c;1表示负整数。 正整数在内存中存储&#xff1a; 正整数原码&#xff0c;反码 &#xff0c;补码…

合约demo——hello contract

520的日子&#xff0c;没出现在各大水群&#xff0c;假装忙着约会&#xff0c;实则在这偷偷躲起来写博客&#xff0c;不能让人发现我今天很有空都没人约๑乛◡乛๑ 智能合约开发 性质 根本性质&#xff1a;表达商业、“法律”关系的契约 机制 运行机制 Transation驱动的E…

LangChain - 为何我们选择 ClickHouse 作为 LangSmith 的动力

本文字数&#xff1a;4742&#xff1b;估计阅读时间&#xff1a;12 分钟 作者&#xff1a;Ankush Gola 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 “我们在使用 ClickHouse 方面有着良好的经历。它使我们能够将 LangSmith 扩展到…