13.javase-基础加强-手写spring框架

一.设计流程
第一步:User书写数据类型
这里是书写数据库的基本数据结构

第二步:
1.@Controller
这里是对操作类的注解,用于区分类文件,便于反射的时候创建对象
2.@Service
这里是对业务类的注解,用于区分类文件,便于反射的时候创建对象
3.@Repository
这里是多Dao层的注解,用于区分类文件,便于反射的时候穿件对象
4.@Autowired
这里是多私有属性的注解,便于依赖注入,实现各对象之间的关联

第三步:UserDao层
这里是书写数据库操作层

第四步:UserService这是业务层
这里是业务层

第五步:UserController这是表现层
这里是表现层,具体操作的功能实现的类,调用业务层执行业务逻辑(查询)

第六步:container
这里是容器,主要是用于具体的操作的对象做相关操作时,不需要创建对象,所有的对象都装在该容器里.
这里容器层有很多操作.
第一个方法:给容器Map赋值.
1.获取当前类下的所有的class文件
2.将class文件转成类的全局限定名字,也就是类在包下的路径名
3.创建一个成员变量Map作为容器
4.通过反射,和注解筛选,创建map中的value值.
5.把类的包下路径名转为首字母小写的类名作为key值.
6.将key值和value值放入容器Map中.
第二个方法:给容器依赖注入
8.遍历容器,获得所有的value值
9.通过反射,注解筛选出带@Autowired的字段.
10.通过方法三给该字段赋值.
第三个方法:获取容器Map中的value值,也就是对象
11.public T getValue(String beanName) {
return (T)BeansFactory.beans.get(beanName);
}

第七步:TestUser
这是具体的操作的每个对象,也就是每个客户.
1.获取当前类的路径.
2.创建BeansFactory对象.
3.给对象中Map字段赋值
4.依赖注入
5.通过key获取容器Map中的对象.
6.调用对象的方法

第一步,写pojo对象

public class User {
	private Integer id;
	private String name;
	private String company;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCompany() {
		return company;
	}
	public void setCompany(String company) {
		this.company = company;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", company=" + company + "]";
	}
	
	
}

第二步
书写注解@controller,@Service,@Repostory,@Autowired.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {

}

第三-第五步
书写controller层/service层/dao层

@Controller
public class UserController {
	//这是依赖属性,稍后建立容器需要依赖注入的字段,所以需要加上注解
		@Autowired
		private UserService userService;
		
		//输入参数id,获取一个数据User
		public User seletOne(Integer id) {
			return userService.seletOne(id);
		}
		
		//获取全部的数据User
		public List<User> seletList(){
			return userService.seletList();
		}
}
@Service
public class UserService {
	//这是依赖属性,稍后建立容器需要依赖注入的字段,所以需要加上注解
	@Autowired
	private UserDao userDao;
	
	//输入参数id,获取一个数据User
	public User seletOne(Integer id) {
		return userDao.seletOne(id);
	}
	
	//获取全部的数据User
	public List<User> seletList(){
		return userDao.seletList();
	}
}
@Repository
public class UserDao {
	//输入参数id,返回一条数据
	public User seletOne(Integer id) {
		//这下面的代码,后面是直接调用数据库的数据
		//这里是模拟返回的数据
		User u = new User();
		u.setId(id);
		u.setName("马云");
		u.setCompany("阿里巴巴");

		return u;
	}

	//返回全部的数据
	public List<User> seletList(){
		List<User> userList = new ArrayList<User>();
		User u1 = new User();
		u1.setId(0002);
		u1.setName("马化腾");
		u1.setCompany("腾讯");
		userList.add(u1);

		User u2 = new User();
		u2.setId(0002);
		u2.setName("李彦宏");
		u2.setCompany("百度");
		userList.add(u2);

		User u3 = new User();
		u3.setId(0002);
		u3.setName("王兴");
		u3.setCompany("美团");
		userList.add(u3);

		//返回集合
		return userList;
	}
}

第六步
书写框架,生成容器container
这里是容器,主要是用于具体的操作的对象做相关操作时,不需要创建对象,所有的对象都装在该容器里.

这里容器层有很多操作. 第一个方法:给容器Map赋值.
1.获取当前类下的所有的class文件
2.将class文件转成类的全局限定名字,也就是类在包下的路径名
3.创建一个成员变量Map作为容器
4.通过反射,和注解筛选,创建map中的value值.
5.把类的包下路径名转为首字母小写的类名作为key值.
6.将key值和value值放入容器Map中. 第二个方法:给容器依赖注入
8.遍历容器,获得所有的value值
9.通过反射,注解筛选出带@Autowired的字段.
10.通过方法三给该字段赋值. 第三个方法:获取容器Map中的value值,也就是对象
11.public T getValue(String beanName) { return (T)BeansFactory.beans.get(beanName); }

容器初始化类

public class ComponentScan {
	//初始化一个容器
	private static final Map<String,Object> beans = new HashMap<String,Object>();
	
	/**
	 * 这里是给容器赋值.
	 * @param pathName 通过路径,给容器赋值
	 * @throws Exception
	 */
	public void setBeans(String pathName) throws Exception {
		//获取该路劲下的class文件
		List<String> pathNameList = BeansUtil.scanFile(pathName);
		//截取字符串成类的包名路径
		for(String s : pathNameList) {
			
			//调用工具类方法,获取类的包名路劲
			String className = BeansUtil.parse(s);
			
			//调用工具类,筛选出具有相关注解的类
			Class<?> clazz = Class.forName(className);
			Repository re = clazz.getDeclaredAnnotation(Repository.class);
			Service se = clazz.getDeclaredAnnotation(Service.class);
			Controller co = clazz.getDeclaredAnnotation(Controller.class);
			if(re != null || se != null || co != null) {
				Object obj = clazz.newInstance();
			
				//这里调用方法,重新设置key的值,因为key的值简单的话,后面调用会更简易.
				String beanName = BeansUtil.parseBeanName(className);
				beans.put(beanName, obj);
			}
			
		}
	}
	
	/**
	 * 这是依赖注入,必须的重新写一个方法,因为如果卸载容器初始化里
	 * 则,因为容器赋值的先后顺序不同,会导致有些依赖注入的对象不能注入
	 * @throws Exception
	 */
	public void inject() throws Exception {
		//遍历容器
		for(String s : beans.keySet()) {
			//获取容器中的对象
			Object obj = beans.get(s);
			
			//利用反射的原理,获取字段上的注解
			Class<?> clazz = obj.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for(Field f : fields) {
				//设置字段的可见性
				f.setAccessible(true);
				//获取字段上的@Autowired注解
				Autowired aut = f.getDeclaredAnnotation(Autowired.class);
				//如果该注解不为空,则调用下面的方法,给成员变量也就是属性,注入对象.
				if(aut != null) {
					//这里就和前面的为什么key值得首字母要小写对应起来了
					//因为这里获得的字段的名字是类名首字母小写的,所以可以直接传入到
					//getValue方法内,直接获取对象值
					f.set(obj, getValue(f.getName()));
					
				}
			}	
		}
	}
	
	
	/**
	 * 通过beans的key值来调用具体的对象,也就是Ioc核心的控制反转
	 * 具体的操作只能通过容器的key值来获取容器中的对象
	 * 这里注意要用泛型,因为将来的从容器中获取的对象则不需要再强转一次了
	 */
	public <T> T getValue(String beanName) {
		return (T)ComponentScan.beans.get(beanName);
	}

}

工具类

public class BeansUtil {
	//设置一个集合用于接收返回的class文件集合
		private static final List<String> list = new ArrayList<String>();
		/**
		 * @param pathName 这是你想查找的路径参数
		 * @return scanFile 返回List<string>,这个集合是该路径下的所有的class文件
		 */	
		public static List<String> scanFile(String pathName){
			File file = new File(pathName);
			File[] fileArray = file.listFiles();
			for(File f : fileArray) {
				String className = f.getAbsolutePath();
				if(f.isDirectory()) {
					pathName = className;
					scanFile(pathName);
				}else {
					if(className.endsWith(".class")){						
						list.add(f.getAbsolutePath());
					}
				}
			}
			return list;
		}
		
		/**
		 * 这是将一个绝对路径类名转换成包路径类名
		 * @param pathName 绝对路径路径
		 * @return 包名路径
		 */
		public static String parse(String pathName){
			String newName = pathName.substring(pathName.lastIndexOf("bin") + 4,pathName.lastIndexOf("."));
			newName = newName.replaceAll("\\\\", "\\.");
			newName = newName.replaceAll("/", "\\.");
			return newName;
		}
		
		/**
		 * 
		 * @param className 这是包名路径
		 * @return 返回值是首字母小写的类名例如:userService
		 */
		public static String parseBeanName(String className) {
			int index = className.lastIndexOf('.') + 1;
			className = className.substring(index);
			String beanName = className.toLowerCase();
			beanName = beanName.substring(0,1) + className.substring(1);
			return beanName;
		} 
		
}

第七步:TestUser

这是具体的操作的每个对象,也就是每个客户.
1.获取当前类的路径.
2.创建BeansFactory对象.
3.给对象中Map字段赋值
4.依赖注入
5.通过key获取容器Map中的对象.
6.调用对象的方法

public class RunApp {
	public static void main(String[] args) throws Exception {
		//创建一个容器对象
		ComponentScan comp = new ComponentScan();
		
		//获取当前类的路径
		String path = RunApp.class.getResource("/").getPath();
		String pathName = path.substring(1);
		
		//给容器初始化赋值
		comp.setBeans(pathName);
		//这是给容器初始化依赖注入,必须放在容器初始化之后执行.
		comp.inject();
		
		UserDao ud = comp.getValue("userDao");
		UserService us = comp.getValue("userService");
		UserController uc = comp.getValue("userController");
		
		
		System.out.println(uc.seletList());
	}
}
发布了42 篇原创文章 · 获赞 0 · 访问量 675

猜你喜欢

转载自blog.csdn.net/weixin_45449911/article/details/104435913