1 Spring 整体架构示意图
2 阶段 1 -- 编写自己的 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);}
}
3 阶段 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)运行结果