写点什么

java 培训如何用反射做简易 Spring IOC 容器

作者:@零度
  • 2022 年 3 月 25 日
  • 本文字数:4666 字

    阅读完需:约 15 分钟

前言

在学习 Spring 之后,对其中的工作原理产生了浓厚的兴趣,近些日子看视频了解到了一些 SpringIOC 容器的工作原理,并边打边磨做出了一个非常简易的 SpringIOC 容器,实现了他的 XML 文件装配,java 代码装配以及自动装配的功能,下面思路以及代码

一、模仿 XML 形式的装配思路

1.思路

采用 XML 装配 bean 时 Spring 会解析 applicationContext.xml 文件,并将各种类型的 bean 注入 IOC 容器,容器中的 bean 可以被无数次重复调用,极大地提高了系统效率而不用多次重复的 new 对象。

为了模仿 IOC,我们利用 Map 集合存放需要的 bean,并新建一个 conf.properties 文件存放信息,存放 com.wql.dao.userDao=com.wql.daoImpl.userDaoImpl 等数据信息_java培训

在获取 bean 之前解析 conf.properties 文件中的信息,利用反射技术将 conf.properties 文件中的内容通过 Class.forName 的形式解析为 Class 对象放入集合 map 中,这样每次获取对象都会从 map 中进行获取,不必再 new

2.具体代码实现

conf.properties:

com.wql.dao.userDao=com.wql.daoImpl.userDaoImpl

com.wql.service.userService=com.wql.serviceImpl.userServiceImpl

MyApplicationContext 类代码实现:

package com.wql.application;

import java.io.InputStream;

import java.util.HashMap;

import java.util.Map;

import java.util.Properties;

import java.util.Set;

public class MyApplicationContext<T> {

//模拟 IOC 容器

private Map<Class,Object> map = new HashMap<>();

private String ResourcePath;

private String filepath;

public MyApplicationContext() {

}

public MyApplicationContext(String resourcePath) {

ResourcePath = resourcePath;

}

//获得一个类型不知的对象(通过 map 集合)

public T getBean(Class clazz){

return (T)map.get(clazz);

}

//通过 properties 中存储的键值对获取 Class 对象,并注入进 map 集合

public void initXMLSpringIOC(){

try{

InputStream stream = MyApplicationContext.class.getClassLoader().getResourceAsStream(ResourcePath);

Properties properties = new Properties();

properties.load(stream);

//获取内容

Set<Object> keys = properties.keySet();

for(Object key:keys){

//Class:实例

map.put(Class.forName(key.toString()),Class.forName(properties.getProperty(key.toString())).newInstance() );

}

}catch (Exception e){

e.printStackTrace();

}

}

}

我们对代码进行测试:



可见成功获得对象

但是通过此种方式进行装配的方式似乎已经过时了,下面我们通过 @Bean 实现注入

二、通过注解方式进行注入

1.思路



先获取项目路径,然后通过字符串截取的方式获取接口实现类的全路径,通过反射技术检查该类是否含有 @Bean 注解,有则加入 map

2.代码实现

@Bean:

package com.wql.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 Bean {

}

MyApplicationContext 类代码实现:

//通过注解进行 bean 的装配

public void initAnnotationSpringIOC(){

filepath = MyApplicationContext.class.getClassLoader().getResource("").getFile();

//获取项目路径后

System.out.println(filepath);

loadOne(new File(filepath));

}

private void loadOne(File fileparent) {

if(fileparent.isDirectory()){

//获取子文件

File[] files = fileparent.listFiles();

if(files.length==0||files==null){

return;

}else{

//其下文件夹不为空

for(File file:files){

if(file.isDirectory()){

loadOne(file);

}else{

try{

String oldpath = file.getAbsolutePath().substring(filepath.length()-1,file.getAbsolutePath().length());

if(oldpath.contains(".class")){

String newpath = oldpath.replaceAll("\\\\",".").replace(".class","");

Class<?> aClass = Class.forName(newpath);

//模拟装配 bean

if(!aClass.isInterface()){

if(aClass.getAnnotation(Bean.class)!=null){

//证明此类包含 Bean 注解,将其加入 map

map.put(aClass.getInterfaces()[0],aClass.newInstance());

}

}

}

}catch (Exception e){

e.printStackTrace();

}

}

}

}

}

}

在代码中我们步步逼近,通过 MyApplicationContext.class.getClassLoader().getResource("").getFile();获取项目路径,这样我们就有了获取.class 文件的途径_java培训机构

项目路径:

/D:/IdeaJava/untitled1/out/production/MySpringIOCProject/

利用 file.getAbsolutePath().substring(filepath.length()-1,file.getAbsolutePath().length()); 我们成功截取到.class 文件和其他文件的路径

com\wql\Annotation\Bean.class

com\wql\Annotation\MyAutowired.class

com\wql\application\ApplicationContext.class

com\wql\application\MyApplicationContext.class

com\wql\dao\userDao.class

com\wql\daoImpl\userDaoImpl.class

com\wql\entity\User.class

com\wql\service\userService.class

com\wql\serviceImpl\userServiceImpl.class

com\wql\test\test.class

conf.properties

META-INF\MySpringIOCProject.kotlin_module

然后利用 contains 方法过滤出后缀名为.class 的文件,对其路径进行更改将/用.进行替换,将.class 字符删除

我们得到:

com.wql.Annotation.Bean

com.wql.Annotation.MyAutowired

com.wql.application.ApplicationContext

com.wql.application.MyApplicationContext

com.wql.dao.userDao

com.wql.daoImpl.userDaoImpl

com.wql.entity.User

com.wql.service.userService

com.wql.serviceImpl.userServiceImpl

com.wql.test.test

此时我们离成功已经很近了,我们利用上一步获取的全类名,使用 Class.forName 获取 Class 对象,利用 aClass.isInterface()与 aClass.getAnnotation(Bean.class)我么你判断其实否为接口与是否含有 @Bean 注解,不为接口且含有 @Bean 注解,我们获得它的实例并将其加入 map 中

com.wql.daoImpl.userDaoImpl

com.wql.serviceImpl.userServiceImpl

测试:

userDaoImpl:

package com.wql.daoImpl;

import com.wql.Annotation.Bean;

import com.wql.dao.userDao;

@Bean

public class userDaoImpl implements userDao {

@Override

public void test() {

System.out.println("成功自动装配");

}

}


成功通过 @Bean 进行注入

三、@Autowired 自动装配

自动装配要先有东西才能装呀,所以我们只能先遍历 map 集合,获取已经存在 map 中的对象的 Class,再获取他们的字段,判断字段上是否含有 @Autowired 注解,有的话进行自动装配即可

代码:

MyAutowired:

package com.wql.Annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAutowired {

}

userDaoImpl 类代码实现:

package com.wql.daoImpl;

import com.wql.Annotation.Bean;

import com.wql.dao.userDao;

@Bean

public class userDaoImpl implements userDao {

@Override

public void test() {

System.out.println("成功自动装配");

}

}

serviceImpl:

package com.wql.serviceImpl;

import com.wql.Annotation.MyAutowired;

import com.wql.Annotation.Bean;

import com.wql.dao.userDao;

import com.wql.service.userService;

@Bean

public class userServiceImpl implements userService {

@MyAutowired

public userDao userDao;

@Override

public void test() {

userDao.test();

}

}

MyApplicationContext 类代码实现:

//通过注解进行 bean 的装配

public void initAnnotationSpringIOC(){

filepath = MyApplicationContext.class.getClassLoader().getResource("").getFile();

//获取项目路径后

System.out.println(filepath);

loadOne(new File(filepath));

AnnotationAutowired();

}

//自动装配

private void AnnotationAutowired() {

for(Map.Entry<Class,Object> entry:map.entrySet()){

Object obj = entry.getValue();

Class<?> aClass = obj.getClass();

Field[] fields = aClass.getDeclaredFields();

for(Field field: fields){

field.setAccessible(true);

if(field.getAnnotation(MyAutowired.class)!=null){

try{

field.set(obj,map.get(field.getType()));

}catch (Exception e){

e.printStackTrace();

}

}

}

}

}

测试


自动装配测试成功

总结

最后附上 MyApplicationContext 的全部代码:

package com.wql.application;

import com.wql.Annotation.Bean;

import com.wql.Annotation.MyAutowired;

import java.io.File;

import java.io.InputStream;

import java.lang.reflect.Field;

import java.util.HashMap;

import java.util.Map;

import java.util.Properties;

import java.util.Set;

public class MyApplicationContext<T> {

//模拟 IOC 容器

private Map<Class,Object> map = new HashMap<>();

private String ResourcePath;

private String filepath;

public MyApplicationContext() {

}

public MyApplicationContext(String resourcePath) {

ResourcePath = resourcePath;

}

//获得一个类型不知的对象(通过 map 集合)

public T getBean(Class clazz){

return (T)map.get(clazz);

}

//通过 properties 中存储的键值对获取 Class 对象,并注入进 map 集合

public void initXMLSpringIOC(){

try{

InputStream stream = MyApplicationContext.class.getClassLoader().getResourceAsStream(ResourcePath);

Properties properties = new Properties();

properties.load(stream);

//获取内容

Set<Object> keys = properties.keySet();

for(Object key:keys){

//Class:实例

map.put(Class.forName(key.toString()),Class.forName(properties.getProperty(key.toString())).newInstance() );

}

}catch (Exception e){

e.printStackTrace();

}

}

//通过注解进行 bean 的装配

public void initAnnotationSpringIOC(){

filepath = MyApplicationContext.class.getClassLoader().getResource("").getFile();

//获取项目路径后

System.out.println(filepath);

loadOne(new File(filepath));

AnnotationAutowired();

}

private void loadOne(File fileparent) {

if(fileparent.isDirectory()){

//获取子文件

File[] files = fileparent.listFiles();

if(files.length==0||files==null){

return;

}else{

//其下文件夹不为空

for(File file:files){

if(file.isDirectory()){

loadOne(file);

}else{

try{

String oldpath = file.getAbsolutePath().substring(filepath.length()-1,file.getAbsolutePath().length());

if(oldpath.contains(".class")){

String newpath = oldpath.replaceAll("\\\\",".").replace(".class","");

Class<?> aClass = Class.forName(newpath);

//模拟装配 bean

if(!aClass.isInterface()){

if(aClass.getAnnotation(Bean.class)!=null){

//证明此类包含 Bean 注解,将其加入 map

map.put(aClass.getInterfaces()[0],aClass.newInstance());

}

}

}

}catch (Exception e){

e.printStackTrace();

}

}

}

}

}

}

//自动装配

private void AnnotationAutowired() {

for(Map.Entry<Class,Object> entry:map.entrySet()){

Object obj = entry.getValue();

Class<?> aClass = obj.getClass();

Field[] fields = aClass.getDeclaredFields();

for(Field field: fields){

field.setAccessible(true);

if(field.getAnnotation(MyAutowired.class)!=null){

try{

field.set(obj,map.get(field.getType()));

}catch (Exception e){

e.printStackTrace();

}

}

}

}

}

}

文章来源于 Java 知音

用户头像

@零度

关注

关注尚硅谷,轻松学IT 2021.11.23 加入

还未添加个人简介

评论

发布
暂无评论
java培训如何用反射做简易 Spring IOC 容器_Java_@零度_InfoQ写作平台